Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions get_git.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down
50 changes: 50 additions & 0 deletions get_git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,56 @@ 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("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()
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)
}

// 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) {
if !testHasGit {
t.Skip("git not found, skipping")
Expand Down