Skip to content

Commit 69e534e

Browse files
committed
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.
1 parent 2949343 commit 69e534e

File tree

2 files changed

+59
-3
lines changed

2 files changed

+59
-3
lines changed

get_git.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type GitGetter struct {
2727
}
2828

2929
var defaultBranchRegexp = regexp.MustCompile(`\s->\sorigin/(.*)`)
30+
var lsRemoteSymRefRegexp = regexp.MustCompile(`ref: refs/heads/([^\s]+).*`)
3031

3132
func (g *GitGetter) ClientMode(_ *url.URL) (ClientMode, error) {
3233
return ClientModeDir, nil
@@ -114,7 +115,7 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
114115
if err == nil {
115116
err = g.update(ctx, dst, sshKeyFile, ref, depth)
116117
} else {
117-
err = g.clone(ctx, dst, sshKeyFile, u, depth)
118+
err = g.clone(ctx, dst, sshKeyFile, u, ref, depth)
118119
}
119120
if err != nil {
120121
return err
@@ -166,14 +167,17 @@ func (g *GitGetter) checkout(dst string, ref string) error {
166167
return getRunCommand(cmd)
167168
}
168169

169-
func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.URL, depth int) error {
170+
func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.URL, ref string, depth int) error {
170171
args := []string{"clone"}
171172

173+
if ref == "" {
174+
ref = findRemoteDefaultBranch(u)
175+
}
172176
if depth > 0 {
173177
args = append(args, "--depth", strconv.Itoa(depth))
174178
}
175179

176-
args = append(args, u.String(), dst)
180+
args = append(args, "--branch", ref, u.String(), dst)
177181
cmd := exec.CommandContext(ctx, "git", args...)
178182
setupGitEnv(cmd, sshKeyFile)
179183
return getRunCommand(cmd)
@@ -236,6 +240,20 @@ func findDefaultBranch(dst string) string {
236240
return matches[len(matches)-1]
237241
}
238242

243+
// findRemoteDefaultBranch checks the remote repo's HEAD symref to return the remote repo's
244+
// default branch. "master" is returned if no HEAD symref exists.
245+
func findRemoteDefaultBranch(u *url.URL) string {
246+
var stdoutbuf bytes.Buffer
247+
cmd := exec.Command("git", "ls-remote", "--symref", u.String(), "HEAD")
248+
cmd.Stdout = &stdoutbuf
249+
err := cmd.Run()
250+
matches := lsRemoteSymRefRegexp.FindStringSubmatch(stdoutbuf.String())
251+
if err != nil || matches == nil {
252+
return "master"
253+
}
254+
return matches[len(matches)-1]
255+
}
256+
239257
// setupGitEnv sets up the environment for the given command. This is used to
240258
// pass configuration data to git and ssh and enables advanced cloning methods.
241259
func setupGitEnv(cmd *exec.Cmd, sshKeyFile string) {

get_git_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,44 @@ func TestGitGetter_shallowClone(t *testing.T) {
162162
}
163163
}
164164

165+
func TestGitGetter_shallowCloneWithTag(t *testing.T) {
166+
if !testHasGit {
167+
t.Log("git not found, skipping")
168+
t.Skip()
169+
}
170+
171+
g := new(GitGetter)
172+
dst := tempDir(t)
173+
174+
repo := testGitRepo(t, "upstream")
175+
repo.commitFile("upstream.txt", "0")
176+
repo.commitFile("upstream.txt", "1")
177+
repo.git("tag", "v1.0")
178+
179+
// Specifiy a clone depth of 1 with a tag
180+
q := repo.url.Query()
181+
q.Add("ref", "v1.0")
182+
q.Add("depth", "1")
183+
repo.url.RawQuery = q.Encode()
184+
185+
if err := g.Get(dst, repo.url); err != nil {
186+
t.Fatalf("err: %s", err)
187+
}
188+
189+
// Assert rev-list count is '1'
190+
cmd := exec.Command("git", "rev-list", "HEAD", "--count")
191+
cmd.Dir = dst
192+
b, err := cmd.Output()
193+
if err != nil {
194+
t.Fatalf("err: %s", err)
195+
}
196+
197+
out := strings.TrimSpace(string(b))
198+
if out != "1" {
199+
t.Fatalf("expected rev-list count to be '1' but got %v", out)
200+
}
201+
}
202+
165203
func TestGitGetter_branchUpdate(t *testing.T) {
166204
if !testHasGit {
167205
t.Skip("git not found, skipping")

0 commit comments

Comments
 (0)