Skip to content
Open
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
9 changes: 8 additions & 1 deletion internal/git/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,5 +600,12 @@ func (r *Repo) readOriginHead() string {
return ""
}

return match[reHeadBranch.SubexpIndex("name")]
// Return the remote-tracking ref (e.g. "origin/main"), not the bare
// branch name: the bare name may not exist as a local branch (a clone
// that never checked out the default branch has no local "main"), which
// makes the diff in PushFiles fail and the whole hook error out. The
// remote-tracking ref always exists once origin/HEAD does, and reflects
// what was actually fetched rather than a possibly stale/absent local
// branch of the same name.
return "origin/" + match[reOriginHeadBranch.SubexpIndex("name")]
}
64 changes: 61 additions & 3 deletions internal/git/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,11 @@ func TestPushFiles(t *testing.T) {
switch command {
case "git diff --name-only HEAD @{push}":
return errors.New("no upstream configured")
case "git diff --name-only HEAD dev --":
case "git diff --name-only HEAD origin/dev --":
_, err := out.Write([]byte("other.txt\n"))
return err
case "git diff --name-only HEAD dev":
return errors.New("fatal: ambiguous argument 'dev': both revision and filename")
case "git diff --name-only HEAD origin/dev":
return errors.New("fatal: ambiguous argument 'origin/dev': both revision and filename")
default:
t.Fatalf("unexpected command: %s", command)
return nil
Expand All @@ -315,4 +315,62 @@ func TestPushFiles(t *testing.T) {
t.Fatalf("expected %v, got %v", want, files)
}
})

t.Run("falls back to the remote-tracking ref when no local branch of that name exists", func(t *testing.T) {
fs := afero.NewMemMapFs()
root := "/repo"
gitPath := filepath.Join(root, ".git")
originHead := filepath.Join(gitPath, "refs", "remotes", "origin", "HEAD")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Standalone regression test structure

The new regression case uses a standalone t.Run block and direct t.Fatalf assertions instead of the repository-required table-driven map[string]struct structure and testify/assert, making related cases harder to extend consistently.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


if err := fs.MkdirAll(root, 0o755); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if err := fs.MkdirAll(filepath.Dir(originHead), 0o755); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if err := afero.WriteFile(fs, filepath.Join(root, "file.txt"), []byte("changed"), 0o644); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if err := afero.WriteFile(fs, originHead, []byte("ref: refs/remotes/origin/main\n"), 0o644); err != nil {
t.Fatalf("unexpected error: %s", err)
}

cmd := cmdtest.NewTracking(func(command string, _ string, out io.Writer) error {
switch command {
case "git diff --name-only HEAD @{push}":
// A branch pushed for the first time has no upstream yet.
return errors.New("fatal: no upstream configured for branch 'feature'")
case "git diff --name-only HEAD main --":
// There is no local branch named "main" in this clone (e.g.
// someone who always branches straight off origin/main), so
// this must never be run.
return errors.New("fatal: bad revision 'main'")
case "git diff --name-only HEAD origin/main --":
_, err := out.Write([]byte("file.txt\n"))
return err
default:
t.Fatalf("unexpected command: %s", command)
return nil
}
})

logger := loggertest.New()
repository := &Repo{
Fs: fs,
RootPath: root,
GitPath: gitPath,
Git: NewCommander(cmd, logger),
logger: logger,
}
repository.ResetCache()

files, err := repository.PushFiles()
if err != nil {
t.Fatalf("unexpected error: %s", err)
}

if want := []string{"file.txt"}; len(files) != len(want) || files[0] != want[0] {
t.Fatalf("expected %v, got %v", want, files)
}
})
}