diff --git a/pkg/git/repo.go b/pkg/git/repo.go index 0df8cc5..bcb5608 100644 --- a/pkg/git/repo.go +++ b/pkg/git/repo.go @@ -108,9 +108,20 @@ func (r *Repo) Untracked() (int, error) { // CurrentBranch returns the short name currently checked-out branch for the Repository. // If Repo is in a detached head state, it will return "HEAD". +// If the repository has no commits yet, it returns "main" (for new repositories). func (r *Repo) CurrentBranch() (string, error) { out, err := run.Git("rev-parse", "--symbolic-full-name", "--abbrev-ref", "HEAD").OnRepo(r.path).AndCaptureLine() if err != nil { + // Check if this is a new repository without commits + if strings.Contains(err.Error(), "ambiguous argument 'HEAD'") { + // Try to get the default branch name from git config + defaultBranch, branchErr := run.Git("config", "--get", "init.defaultBranch").OnRepo(r.path).AndCaptureLine() + if branchErr == nil && defaultBranch != "" { + return defaultBranch, nil + } + // Fall back to "main" as the modern default + return "main", nil + } return "", err } @@ -175,6 +186,10 @@ func (r *Repo) Remote() (string, error) { // https://stackoverflow.com/a/16880000/1085632 out, err := run.Git("ls-remote", "--get-url").OnRepo(r.path).AndCaptureLine() if err != nil { + // Check if this is a repository without any remotes configured + if strings.Contains(err.Error(), "No remote configured to list refs from") { + return "", nil // Return empty string instead of error for missing remote + } return "", err } diff --git a/pkg/git/repo_test.go b/pkg/git/repo_test.go index 1461f1c..212350a 100644 --- a/pkg/git/repo_test.go +++ b/pkg/git/repo_test.go @@ -114,12 +114,11 @@ func TestCurrentBranch(t *testing.T) { repoMaker func(*testing.T) *test.Repo want string }{ - // TODO: maybe add wantErr to check if error is returned correctly? - // { - // name: "empty", - // repoMaker: newTestRepo, - // want: "", - // }, + { + name: "empty repo without commits", + repoMaker: test.RepoEmpty, + want: "main", + }, { name: "only master branch", repoMaker: test.RepoWithCommit, @@ -359,6 +358,57 @@ func TestCleanupFailedClone(t *testing.T) { } } +func TestRemote(t *testing.T) { + tests := []struct { + name string + repoMaker func(*testing.T) *test.Repo + want string + wantErr bool + }{ + { + name: "empty repo without remote", + repoMaker: test.RepoEmpty, + want: "", + wantErr: false, + }, + { + name: "repo with commit but no remote", + repoMaker: test.RepoWithCommit, + want: "", + wantErr: false, + }, + { + name: "repo with upstream", + repoMaker: test.RepoWithBranchWithUpstream, + want: "", // This will contain the actual remote URL but we just test it doesn't error + wantErr: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + r, _ := Open(test.repoMaker(t).Path()) + got, err := r.Remote() + + if test.wantErr && err == nil { + t.Errorf("expected error but got none") + } + if !test.wantErr && err != nil { + t.Errorf("unexpected error: %q", err) + } + + // For repos with remote, just check no error occurred + if test.name == "repo with upstream" { + if err != nil { + t.Errorf("unexpected error for repo with remote: %q", err) + } + } else if got != test.want { + t.Errorf("expected %q; got %q", test.want, got) + } + }) + } +} + func createTestDirTree(t *testing.T) string { root := test.TempDir(t, "") err := os.MkdirAll(filepath.Join(root, "a", "b", "c"), os.ModePerm)