From 6c7b5b90bd2069d63ac59b2bd01aaa2fa3d3a3de Mon Sep 17 00:00:00 2001 From: Tom van der Woerdt Date: Wed, 28 Aug 2024 15:49:12 +0200 Subject: [PATCH] git: implement git on top of go-git Code is simpler, faster, and we can get rid of ref detection (could previously already have done that, just need to pull HEAD). --- vcs/git.go | 157 +++++++++++++----------------------------------- vcs/git_test.go | 85 -------------------------- 2 files changed, 43 insertions(+), 199 deletions(-) delete mode 100644 vcs/git_test.go diff --git a/vcs/git.go b/vcs/git.go index 7049b0f7..7043034a 100644 --- a/vcs/git.go +++ b/vcs/git.go @@ -4,35 +4,25 @@ import ( "bytes" "encoding/json" "fmt" - "io" "log" "os/exec" "path/filepath" - "regexp" - "strings" + + gogit "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" ) -const defaultRef = "master" +const indexRef = "localindex" const autoGeneratedAttribute = "linguist-generated" -var headBranchRegexp = regexp.MustCompile(`HEAD branch: (?P.+)`) - func init() { Register(newGit, "git") } type GitDriver struct { - DetectRef bool `json:"detect-ref"` - Ref string `json:"ref"` - Bare bool `json:"bare"` - refDetetector refDetetector -} - -type refDetetector interface { - detectRef(dir string) string -} - -type headBranchDetector struct { + Ref string `json:"ref"` + Bare bool `json:"bare"` } func newGit(b []byte) (Driver, error) { @@ -44,97 +34,62 @@ func newGit(b []byte) (Driver, error) { } } - d.refDetetector = &headBranchDetector{} - return &d, nil } func (g *GitDriver) HeadRev(dir string) (string, error) { - targetRef := "HEAD" - if g.Bare { - targetRef = fmt.Sprintf("origin/%s", g.targetRef(dir)) + repo, err := gogit.PlainOpen(dir) + if err != nil { + return "", nil } - cmd := exec.Command( - "git", - "rev-parse", - targetRef) - cmd.Dir = dir - r, err := cmd.StdoutPipe() + h, err := repo.ResolveRevision(plumbing.Revision(indexRef)) if err != nil { return "", err } - defer r.Close() - if err := cmd.Start(); err != nil { + return h.String(), nil +} + +func (g *GitDriver) Pull(dir string) (string, error) { + repo, err := gogit.PlainOpen(dir) + if err != nil { return "", err } - var buf bytes.Buffer + fetchTarget := "HEAD" + if g.Ref != "" { + fetchTarget = g.Ref + } - if _, err := io.Copy(&buf, r); err != nil { + if err = repo.Fetch(&gogit.FetchOptions{ + Depth: 1, + Prune: true, + Tags: gogit.NoTags, + RefSpecs: []config.RefSpec{ + config.RefSpec(fmt.Sprintf("+%s:refs/heads/%s", fetchTarget, indexRef)), + }, + }); err != nil { return "", err } - return strings.TrimSpace(buf.String()), cmd.Wait() -} - -func run(desc, dir, cmd string, args ...string) (string, error) { - c := exec.Command(cmd, args...) - c.Dir = dir - out, err := c.CombinedOutput() + newHead, err := repo.ResolveRevision(indexRef) if err != nil { - log.Printf( - "Failed to %s %v at %q, see output below\n%s: %+v\nContinuing...", - desc, - c.Args, c.Dir, - out, err) - } - - return string(out), nil -} - -func (g *GitDriver) Pull(dir string) (string, error) { - targetRef := g.targetRef(dir) - - if _, err := run("git fetch", dir, - "git", - "fetch", - "--prune", - "--no-tags", - "--depth", "1", - "origin", - fmt.Sprintf("+%s:remotes/origin/%s", targetRef, targetRef)); err != nil { return "", err } if !g.Bare { - // XXX(tvdw) Check if this works without fetch? - if _, err := run("git reset", dir, - "git", - "reset", - "--hard", - fmt.Sprintf("origin/%s", targetRef)); err != nil { + worktree, err := repo.Worktree() + if err != nil { return "", err } + worktree.Reset(&gogit.ResetOptions{ + Mode: gogit.HardReset, + Commit: *newHead, + }) } - return g.HeadRev(dir) -} - -func (g *GitDriver) targetRef(dir string) string { - var targetRef string - if g.Ref != "" { - targetRef = g.Ref - } else if g.DetectRef { - targetRef = g.refDetetector.detectRef(dir) - } - - if targetRef == "" { - targetRef = defaultRef - } - - return targetRef + return newHead.String(), nil } func (g *GitDriver) Clone(dir, url string) (string, error) { @@ -168,6 +123,11 @@ func (g *GitDriver) SpecialFiles() []string { func (g *GitDriver) AutoGeneratedFiles(dir string) []string { var files []string + // XXX(tvdw): broken under Bare + if g.Bare { + return nil + } + filesCmd := exec.Command("git", "ls-files", "-z") filesCmd.Dir = dir pipe, err := filesCmd.StdoutPipe() @@ -210,40 +170,9 @@ func (g *GitDriver) AutoGeneratedFiles(dir string) []string { return files } -func (d *headBranchDetector) detectRef(dir string) string { - output, err := run("git show remote info", dir, - "git", - "remote", - "show", - "origin", - ) - - if err != nil { - log.Printf( - "error occured when fetching info to determine target ref in %s: %s. Will fall back to default ref %s", - dir, - err, - defaultRef, - ) - return "" - } - - matches := headBranchRegexp.FindStringSubmatch(output) - if len(matches) != 2 { - log.Printf( - "could not determine target ref in %s. Will fall back to default ref %s", - dir, - defaultRef, - ) - return "" - } - - return matches[1] -} - func (g *GitDriver) FileSystem(dir string) (FileSystem, error) { if g.Bare { - return NewGitFilesystem(dir, fmt.Sprintf("origin/%s", g.targetRef(dir))) + return NewGitFilesystem(dir, indexRef) } else { return NewDirFilesystem(dir) } diff --git a/vcs/git_test.go b/vcs/git_test.go deleted file mode 100644 index 737b324b..00000000 --- a/vcs/git_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package vcs - -import ( - "fmt" - "testing" -) - -type testRefDetector struct { - result string -} - -func (d *testRefDetector) detectRef(dir string) string { - return d.result -} - -func TestTargetRef(t *testing.T) { - testCases := []struct { - explicitRef string - detectRefEnabled bool - detectRefResult string - expectedResult string - }{ - { - explicitRef: "", - detectRefEnabled: true, - detectRefResult: "detected-ref", - expectedResult: "detected-ref", - }, - { - explicitRef: "", - detectRefEnabled: true, - detectRefResult: "", - expectedResult: defaultRef, - }, - { - explicitRef: "explicit-ref", - detectRefEnabled: true, - detectRefResult: "detected-ref", - expectedResult: "explicit-ref", - }, - { - explicitRef: "explicit-ref", - detectRefEnabled: true, - detectRefResult: "", - expectedResult: "explicit-ref", - }, - { - explicitRef: "explicit-ref", - detectRefEnabled: false, - detectRefResult: "foo", - expectedResult: "explicit-ref", - }, - { - explicitRef: "", - detectRefEnabled: false, - detectRefResult: "", - expectedResult: defaultRef, - }, - { - explicitRef: "explicit-ref", - detectRefEnabled: false, - detectRefResult: "", - expectedResult: "explicit-ref", - }, - { - explicitRef: "explicit-ref", - detectRefEnabled: false, - detectRefResult: "detected-ref", - expectedResult: "explicit-ref", - }, - } - for idx, testCase := range testCases { - t.Run(fmt.Sprintf("test case %d", idx), func(t *testing.T) { - driver := &GitDriver{ - Ref: testCase.explicitRef, - DetectRef: testCase.detectRefEnabled, - refDetetector: &testRefDetector{result: testCase.detectRefResult}, - } - actualResult := driver.targetRef("dir") - if actualResult != testCase.expectedResult { - t.Errorf("expected target ref: %q, got: %q", testCase.expectedResult, actualResult) - } - }) - } -}