From 69e534ea7707fe5a21ad7be208fd01e24d3a21c4 Mon Sep 17 00:00:00 2001 From: Zachary Whaley Date: Fri, 28 Aug 2020 09:27:30 -0500 Subject: [PATCH 1/3] Let shallow clone work with any ref Currently the shallow clone only works if ref is either not set, or set to the default branch. This is becuase go-getter does not set the ref to checkout during a shallow clone so Git assumes the default branch, and then when go-getter tries to checkout a ref other than the default branch it fails because the shallow clone did not fetch that ref. To fix this, I have used `--branch` to explicitly set the ref to fetch during a clone so that when a shallow clone is done go-getter will clone the ref given and not the default branch. This will also make clones of non-default branches just a tad bit more optimized since the clone will not waste time checking out a branch it ultimately will not use. --- get_git.go | 24 +++++++++++++++++++++--- get_git_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/get_git.go b/get_git.go index 1b9f4be81..2e924ffca 100644 --- a/get_git.go +++ b/get_git.go @@ -27,6 +27,7 @@ type GitGetter struct { } var defaultBranchRegexp = regexp.MustCompile(`\s->\sorigin/(.*)`) +var lsRemoteSymRefRegexp = regexp.MustCompile(`ref: refs/heads/([^\s]+).*`) func (g *GitGetter) ClientMode(_ *url.URL) (ClientMode, error) { return ClientModeDir, nil @@ -114,7 +115,7 @@ func (g *GitGetter) Get(dst string, u *url.URL) error { if err == nil { err = g.update(ctx, dst, sshKeyFile, ref, depth) } else { - err = g.clone(ctx, dst, sshKeyFile, u, depth) + err = g.clone(ctx, dst, sshKeyFile, u, ref, depth) } if err != nil { return err @@ -166,14 +167,17 @@ func (g *GitGetter) checkout(dst string, ref string) error { return getRunCommand(cmd) } -func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.URL, depth int) error { +func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.URL, ref string, depth int) error { args := []string{"clone"} + if ref == "" { + ref = findRemoteDefaultBranch(u) + } if depth > 0 { args = append(args, "--depth", strconv.Itoa(depth)) } - args = append(args, u.String(), dst) + args = append(args, "--branch", ref, u.String(), dst) cmd := exec.CommandContext(ctx, "git", args...) setupGitEnv(cmd, sshKeyFile) return getRunCommand(cmd) @@ -236,6 +240,20 @@ func findDefaultBranch(dst string) string { return matches[len(matches)-1] } +// findRemoteDefaultBranch checks the remote repo's HEAD symref to return the remote repo's +// default branch. "master" is returned if no HEAD symref exists. +func findRemoteDefaultBranch(u *url.URL) string { + var stdoutbuf bytes.Buffer + cmd := exec.Command("git", "ls-remote", "--symref", u.String(), "HEAD") + cmd.Stdout = &stdoutbuf + err := cmd.Run() + matches := lsRemoteSymRefRegexp.FindStringSubmatch(stdoutbuf.String()) + if err != nil || matches == nil { + return "master" + } + return matches[len(matches)-1] +} + // setupGitEnv sets up the environment for the given command. This is used to // pass configuration data to git and ssh and enables advanced cloning methods. func setupGitEnv(cmd *exec.Cmd, sshKeyFile string) { diff --git a/get_git_test.go b/get_git_test.go index b1a7058de..119ece898 100644 --- a/get_git_test.go +++ b/get_git_test.go @@ -162,6 +162,44 @@ func TestGitGetter_shallowClone(t *testing.T) { } } +func TestGitGetter_shallowCloneWithTag(t *testing.T) { + if !testHasGit { + t.Log("git not found, skipping") + t.Skip() + } + + g := new(GitGetter) + dst := tempDir(t) + + repo := testGitRepo(t, "upstream") + repo.commitFile("upstream.txt", "0") + repo.commitFile("upstream.txt", "1") + repo.git("tag", "v1.0") + + // Specifiy a clone depth of 1 with a tag + q := repo.url.Query() + q.Add("ref", "v1.0") + q.Add("depth", "1") + repo.url.RawQuery = q.Encode() + + if err := g.Get(dst, repo.url); err != nil { + t.Fatalf("err: %s", err) + } + + // Assert rev-list count is '1' + cmd := exec.Command("git", "rev-list", "HEAD", "--count") + cmd.Dir = dst + b, err := cmd.Output() + if err != nil { + t.Fatalf("err: %s", err) + } + + out := strings.TrimSpace(string(b)) + if out != "1" { + t.Fatalf("expected rev-list count to be '1' but got %v", out) + } +} + func TestGitGetter_branchUpdate(t *testing.T) { if !testHasGit { t.Skip("git not found, skipping") From 7dda4ee3bf91006f756b34bd2202c3eb45d2d2ba Mon Sep 17 00:00:00 2001 From: Zachary Whaley Date: Fri, 28 Aug 2020 09:45:42 -0500 Subject: [PATCH 2/3] Improve test --- get_git_test.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/get_git_test.go b/get_git_test.go index 119ece898..d175df0a3 100644 --- a/get_git_test.go +++ b/get_git_test.go @@ -172,9 +172,9 @@ func TestGitGetter_shallowCloneWithTag(t *testing.T) { dst := tempDir(t) repo := testGitRepo(t, "upstream") - repo.commitFile("upstream.txt", "0") - repo.commitFile("upstream.txt", "1") + repo.commitFile("v1.0.txt", "0") repo.git("tag", "v1.0") + repo.commitFile("v1.1.txt", "1") // Specifiy a clone depth of 1 with a tag q := repo.url.Query() @@ -198,6 +198,18 @@ func TestGitGetter_shallowCloneWithTag(t *testing.T) { if out != "1" { t.Fatalf("expected rev-list count to be '1' but got %v", out) } + + // Verify the v1.0 file exists + mainPath := filepath.Join(dst, "v1.0.txt") + if _, err := os.Stat(mainPath); err != nil { + t.Fatalf("err: %s", err) + } + + // Verify the v1.1 file does not exists + mainPath := filepath.Join(dst, "v1.1.txt") + if _, err := os.Stat(mainPath); err == nil { + t.Fatalf("expected v1.1 file to not exist") + } } func TestGitGetter_branchUpdate(t *testing.T) { From d39dd15ce537d40ff016dd8a3fe6aa79b73479fb Mon Sep 17 00:00:00 2001 From: Michael Schurter Date: Mon, 4 Oct 2021 16:01:56 -0700 Subject: [PATCH 3/3] fix error in test --- get_git_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/get_git_test.go b/get_git_test.go index d175df0a3..db96b09ac 100644 --- a/get_git_test.go +++ b/get_git_test.go @@ -206,7 +206,7 @@ func TestGitGetter_shallowCloneWithTag(t *testing.T) { } // Verify the v1.1 file does not exists - mainPath := filepath.Join(dst, "v1.1.txt") + mainPath = filepath.Join(dst, "v1.1.txt") if _, err := os.Stat(mainPath); err == nil { t.Fatalf("expected v1.1 file to not exist") }