1package git
2
3import (
4 "errors"
5 "fmt"
6 "os/exec"
7
8 "github.com/go-git/go-git/v5"
9 "github.com/go-git/go-git/v5/config"
10)
11
12func Fork(repoPath, source string) error {
13 cloneCmd := exec.Command("git", "clone", "--bare", source, repoPath)
14 if err := cloneCmd.Run(); err != nil {
15 return fmt.Errorf("failed to bare clone repository: %w", err)
16 }
17
18 configureCmd := exec.Command("git", "-C", repoPath, "config", "receive.hideRefs", "refs/hidden")
19 if err := configureCmd.Run(); err != nil {
20 return fmt.Errorf("failed to configure hidden refs: %w", err)
21 }
22
23 return nil
24}
25
26func (g *GitRepo) Sync() error {
27 branch := g.h.String()
28
29 fetchOpts := &git.FetchOptions{
30 RefSpecs: []config.RefSpec{
31 config.RefSpec("+" + branch + ":" + branch), // +refs/heads/master:refs/heads/master
32 },
33 }
34
35 err := g.r.Fetch(fetchOpts)
36 if errors.Is(git.NoErrAlreadyUpToDate, err) {
37 return nil
38 } else if err != nil {
39 return fmt.Errorf("failed to fetch origin branch: %s: %w", branch, err)
40 }
41 return nil
42}
43
44// TrackHiddenRemoteRef tracks a hidden remote in the repository. For example,
45// if the feature branch on the fork (forkRef) is feature-1, and the remoteRef,
46// i.e. the branch we want to merge into, is main, this will result in a refspec:
47//
48// +refs/heads/main:refs/hidden/feature-1/main
49func (g *GitRepo) TrackHiddenRemoteRef(forkRef, remoteRef string) error {
50 fetchOpts := &git.FetchOptions{
51 RefSpecs: []config.RefSpec{
52 config.RefSpec(fmt.Sprintf("+refs/heads/%s:refs/hidden/%s/%s", remoteRef, forkRef, remoteRef)),
53 },
54 RemoteName: "origin",
55 }
56
57 err := g.r.Fetch(fetchOpts)
58 if errors.Is(git.NoErrAlreadyUpToDate, err) {
59 return nil
60 } else if err != nil {
61 return fmt.Errorf("failed to fetch hidden remote: %s: %w", forkRef, err)
62 }
63 return nil
64}