Skip to content

Commit ade8a4d

Browse files
fix(cli): checkout working tree after gt clone
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
1 parent 94e072b commit ade8a4d

2 files changed

Lines changed: 97 additions & 10 deletions

File tree

internal/cli/clone.go

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,9 @@ func Clone(daemonURL, repoID, localPath string) error {
1616

1717
// Get repo info with refs
1818
var repoInfo struct {
19-
ID string `json:"id"`
20-
Name string `json:"name"`
21-
Refs []struct {
22-
Name string `json:"name"`
23-
Hash string `json:"hash"`
24-
} `json:"refs"`
19+
ID string `json:"id"`
20+
Name string `json:"name"`
21+
Refs []cloneRef `json:"refs"`
2522
}
2623
if err := client.Get(fmt.Sprintf("/api/v1/repos/%s/clone", url.PathEscape(repoID)), &repoInfo); err != nil {
2724
return fmt.Errorf("fetching repo info: %w", err)
@@ -50,10 +47,49 @@ func Clone(daemonURL, repoID, localPath string) error {
5047
}
5148
}
5249

50+
checkoutRef := defaultCheckoutRef(repoInfo.Refs)
51+
if checkoutRef != "" {
52+
if err := repo.Storer.SetReference(plumbing.NewSymbolicReference(plumbing.HEAD, checkoutRef)); err != nil {
53+
return fmt.Errorf("setting HEAD to %s: %w", checkoutRef, err)
54+
}
55+
worktree, err := repo.Worktree()
56+
if err != nil {
57+
return fmt.Errorf("opening worktree: %w", err)
58+
}
59+
if err := worktree.Checkout(&git.CheckoutOptions{Branch: checkoutRef, Force: true}); err != nil {
60+
return fmt.Errorf("checking out %s: %w", checkoutRef, err)
61+
}
62+
}
63+
5364
_, _ = fmt.Fprintf(Stderr(), "Cloned %s to %s (%d refs, %d objects)\n", repoID, localPath, len(repoInfo.Refs), len(seen))
5465
return nil
5566
}
5667

68+
type cloneRef struct {
69+
Name string `json:"name"`
70+
Hash string `json:"hash"`
71+
}
72+
73+
func defaultCheckoutRef(refs []cloneRef) plumbing.ReferenceName {
74+
for _, ref := range refs {
75+
if ref.Name == "refs/heads/main" {
76+
return plumbing.ReferenceName(ref.Name)
77+
}
78+
}
79+
for _, ref := range refs {
80+
if ref.Name == "refs/heads/master" {
81+
return plumbing.ReferenceName(ref.Name)
82+
}
83+
}
84+
for _, ref := range refs {
85+
refName := plumbing.ReferenceName(ref.Name)
86+
if refName.IsBranch() {
87+
return refName
88+
}
89+
}
90+
return ""
91+
}
92+
5793
// fetchObjectRecursive fetches an object and all its children
5894
func fetchObjectRecursive(client *Client, repoID string, repo *git.Repository, hashStr string, seen map[string]bool, depth int) error {
5995
if depth > 1000 {
@@ -154,10 +190,7 @@ func Pull(repoPath, daemonURL, repoID string) error {
154190
client := NewClient(daemonURL)
155191

156192
var repoInfo struct {
157-
Refs []struct {
158-
Name string `json:"name"`
159-
Hash string `json:"hash"`
160-
} `json:"refs"`
193+
Refs []cloneRef `json:"refs"`
161194
}
162195
if err := client.Get(fmt.Sprintf("/api/v1/repos/%s/clone", url.PathEscape(repoID)), &repoInfo); err != nil {
163196
return fmt.Errorf("fetching remote refs: %w", err)

internal/cli/clone_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package cli
2+
3+
import (
4+
"testing"
5+
6+
"github.com/go-git/go-git/v6/plumbing"
7+
)
8+
9+
func TestDefaultCheckoutRef(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
refs []cloneRef
13+
want plumbing.ReferenceName
14+
}{
15+
{
16+
name: "prefers main",
17+
refs: []cloneRef{
18+
{Name: "refs/heads/feature", Hash: "1111111111111111111111111111111111111111"},
19+
{Name: "refs/heads/main", Hash: "2222222222222222222222222222222222222222"},
20+
{Name: "refs/heads/master", Hash: "3333333333333333333333333333333333333333"},
21+
},
22+
want: "refs/heads/main",
23+
},
24+
{
25+
name: "falls back to master",
26+
refs: []cloneRef{
27+
{Name: "refs/tags/v1", Hash: "1111111111111111111111111111111111111111"},
28+
{Name: "refs/heads/master", Hash: "2222222222222222222222222222222222222222"},
29+
},
30+
want: "refs/heads/master",
31+
},
32+
{
33+
name: "falls back to first branch",
34+
refs: []cloneRef{
35+
{Name: "refs/tags/v1", Hash: "1111111111111111111111111111111111111111"},
36+
{Name: "refs/heads/feature", Hash: "2222222222222222222222222222222222222222"},
37+
},
38+
want: "refs/heads/feature",
39+
},
40+
{
41+
name: "no branches",
42+
refs: []cloneRef{{Name: "refs/tags/v1", Hash: "1111111111111111111111111111111111111111"}},
43+
want: "",
44+
},
45+
}
46+
47+
for _, tt := range tests {
48+
t.Run(tt.name, func(t *testing.T) {
49+
if got := defaultCheckoutRef(tt.refs); got != tt.want {
50+
t.Fatalf("defaultCheckoutRef() = %q, want %q", got, tt.want)
51+
}
52+
})
53+
}
54+
}

0 commit comments

Comments
 (0)