Skip to content

Commit 463bcf3

Browse files
committed
fix: handle uninitialized repos without commits gracefully
1 parent a7f5f77 commit 463bcf3

5 files changed

Lines changed: 103 additions & 12 deletions

File tree

cmd/git.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,21 @@ func convertToWebURL(url string) string {
8080
// getBranchNameFunc is a variable that can be replaced for testing
8181
var getBranchNameFunc = func(repo *git.Repository) (string, error) {
8282
head, err := repo.Head()
83-
if err != nil {
83+
if err == nil {
84+
return head.Name().Short(), nil
85+
}
86+
87+
ref, refErr := repo.Reference("HEAD", true)
88+
if refErr != nil {
8489
return "", fmt.Errorf("error getting HEAD: %w", err)
8590
}
86-
return head.Name().Short(), nil
91+
92+
target := ref.Target()
93+
if target.IsBranch() {
94+
return target.Short(), nil
95+
}
96+
97+
return "", fmt.Errorf("error getting HEAD: %w", err)
8798
}
8899

89100
func getBranchName(repo *git.Repository) (string, error) {

cmd/git_test.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,22 @@ func Test_getBranchName(t *testing.T) {
249249
branchName: "feature-branch",
250250
wantErr: false,
251251
},
252+
{
253+
name: "uninitialized repo with HEAD reference",
254+
remoteURL: "https://github.com/zhaochunqi/git-open.git",
255+
branchName: "main",
256+
wantErr: true, // Expect error when there's no commit
257+
},
252258
}
253259

254260
for _, tt := range tests {
255261
t.Run(tt.name, func(t *testing.T) {
256-
_, cleanup := testhelper.SetupTestRepo(t, tt.remoteURL, tt.branchName)
262+
var cleanup func()
263+
if tt.name == "uninitialized repo with HEAD reference" {
264+
_, cleanup = testhelper.SetupTestRepoWithoutCommit(t, tt.remoteURL, tt.branchName)
265+
} else {
266+
_, cleanup = testhelper.SetupTestRepo(t, tt.remoteURL, tt.branchName)
267+
}
257268
defer cleanup()
258269

259270
repo, err := getCurrentGitDirectory()

cmd/root.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,12 @@ and converts it to a web URL. The web URL is then printed to the console.`,
4040
}
4141

4242
branchName, err := getBranchName(repo)
43-
if err != nil {
44-
return fmt.Errorf("error getting branch name: %w", err)
45-
}
46-
47-
// For now, we only append branch name if it's not 'main' or 'master'.
48-
// This can be improved later to fetch default branch from remote or allow configuration.
49-
if branchName != "main" && branchName != "master" {
50-
webURL = buildBranchURL(webURL, branchName, remoteURL)
43+
if err == nil {
44+
// For now, we only append branch name if it's not 'main' or 'master'.
45+
// This can be improved later to fetch default branch from remote or allow configuration.
46+
if branchName != "main" && branchName != "master" {
47+
webURL = buildBranchURL(webURL, branchName, remoteURL)
48+
}
5149
}
5250

5351
err = openURLInBrowserFunc(webURL)

cmd/root_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ func Test_rootCmd_ErrorHandling(t *testing.T) {
289289
return "", errors.New("branch name error")
290290
}
291291
},
292-
wantErr: true,
292+
wantErr: false, // Branch name error should not cause command to fail, opens main page
293293
},
294294
{
295295
name: "browser error",

internal/testhelper/git.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,74 @@ func SetupTestRepo(t *testing.T, remoteURL string, branchName string) (string, f
104104

105105
return tmpDir, cleanup
106106
}
107+
108+
// SetupTestRepoWithoutCommit creates a temporary git repository without any commits.
109+
// It returns the temporary directory path and a cleanup function.
110+
func SetupTestRepoWithoutCommit(t *testing.T, remoteURL string, branchName string) (string, func()) {
111+
t.Helper()
112+
113+
// Create temporary directory
114+
tmpDir, err := os.MkdirTemp("", "git-test-no-commit")
115+
if err != nil {
116+
t.Fatal(err)
117+
}
118+
119+
// Initialize git repository
120+
repo, err := git.PlainInit(tmpDir, false)
121+
if err != nil {
122+
t.Fatal(err)
123+
}
124+
125+
// Add remote if URL is provided
126+
if remoteURL != "" {
127+
_, err = repo.CreateRemote(&config.RemoteConfig{
128+
Name: "origin",
129+
URLs: []string{remoteURL},
130+
})
131+
if err != nil {
132+
t.Fatal(err)
133+
}
134+
}
135+
136+
// Create a worktree and add a file (but don't commit)
137+
w, err := repo.Worktree()
138+
if err != nil {
139+
t.Fatal(err)
140+
}
141+
err = os.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("hello"), 0644)
142+
if err != nil {
143+
t.Fatal(err)
144+
}
145+
_, err = w.Add("test.txt")
146+
if err != nil {
147+
t.Fatal(err)
148+
}
149+
150+
// Create a symbolic reference for HEAD to point to the specified branch
151+
if branchName != "" {
152+
headRef := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.ReferenceName("refs/heads/"+branchName))
153+
err = repo.Storer.SetReference(headRef)
154+
if err != nil {
155+
t.Fatal(err)
156+
}
157+
}
158+
159+
// Save current directory
160+
currentDir, err := os.Getwd()
161+
if err != nil {
162+
t.Fatal(err)
163+
}
164+
165+
// Change to test directory
166+
if err := os.Chdir(tmpDir); err != nil {
167+
t.Fatal(err)
168+
}
169+
170+
// Return cleanup function
171+
cleanup := func() {
172+
os.Chdir(currentDir)
173+
os.RemoveAll(tmpDir)
174+
}
175+
176+
return tmpDir, cleanup
177+
}

0 commit comments

Comments
 (0)