fix(git): fetch origin after configuring refspec for bare clones (#384)

Bare clones don't have refs/remotes/origin/* populated by default.
The configureRefspec fix (a91e6cd6) set up the fetch config but didn't
actually run a fetch, leaving origin/main unavailable.

This caused polecat worktree creation to fail with:
  fatal: invalid reference: origin/main

Fixes:
1. Add git fetch after configureRefspec in bare clone setup
2. Add fetch before polecat worktree creation (ensures latest code)

The second fix matches RepairWorktreeWithOptions which already had a fetch.

Related: #286

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Julian Knutsen
2026-01-12 09:45:09 +00:00
committed by GitHub
parent 0d0d2763a8
commit 7b35398ebc
3 changed files with 105 additions and 0 deletions

View File

@@ -194,6 +194,12 @@ func configureRefspec(repoPath string) error {
if err := cmd.Run(); err != nil {
return fmt.Errorf("configuring refspec: %s", strings.TrimSpace(stderr.String()))
}
// Fetch to populate refs/remotes/origin/* so worktrees can use origin/main
fetchCmd := exec.Command("git", "-C", repoPath, "fetch", "origin")
fetchCmd.Stderr = &stderr
if err := fetchCmd.Run(); err != nil {
return fmt.Errorf("fetching origin: %s", strings.TrimSpace(stderr.String()))
}
return nil
}

View File

@@ -395,3 +395,96 @@ func TestCheckConflicts_WithConflict(t *testing.T) {
t.Error("expected clean working directory after CheckConflicts")
}
}
// TestCloneBareHasOriginRefs verifies that after CloneBare, origin/* refs
// are available for worktree creation. This was broken before the fix:
// bare clones had refspec configured but no fetch was run, so origin/main
// didn't exist and WorktreeAddFromRef("origin/main") failed.
//
// Related: GitHub issue #286
func TestCloneBareHasOriginRefs(t *testing.T) {
tmp := t.TempDir()
// Create a "remote" repo with a commit on main
remoteDir := filepath.Join(tmp, "remote")
if err := os.MkdirAll(remoteDir, 0755); err != nil {
t.Fatalf("mkdir remote: %v", err)
}
cmd := exec.Command("git", "init")
cmd.Dir = remoteDir
if err := cmd.Run(); err != nil {
t.Fatalf("git init: %v", err)
}
cmd = exec.Command("git", "config", "user.email", "test@test.com")
cmd.Dir = remoteDir
_ = cmd.Run()
cmd = exec.Command("git", "config", "user.name", "Test User")
cmd.Dir = remoteDir
_ = cmd.Run()
// Create initial commit
readmeFile := filepath.Join(remoteDir, "README.md")
if err := os.WriteFile(readmeFile, []byte("# Test\n"), 0644); err != nil {
t.Fatalf("write file: %v", err)
}
cmd = exec.Command("git", "add", ".")
cmd.Dir = remoteDir
_ = cmd.Run()
cmd = exec.Command("git", "commit", "-m", "initial")
cmd.Dir = remoteDir
if err := cmd.Run(); err != nil {
t.Fatalf("git commit: %v", err)
}
// Get the main branch name (main or master depending on git version)
cmd = exec.Command("git", "branch", "--show-current")
cmd.Dir = remoteDir
out, err := cmd.Output()
if err != nil {
t.Fatalf("git branch --show-current: %v", err)
}
mainBranch := string(out[:len(out)-1]) // trim newline
// Clone as bare repo using our CloneBare function
bareDir := filepath.Join(tmp, "bare.git")
g := NewGit(tmp)
if err := g.CloneBare(remoteDir, bareDir); err != nil {
t.Fatalf("CloneBare: %v", err)
}
// Verify origin/main exists (this was the bug - it didn't exist before the fix)
bareGit := NewGitWithDir(bareDir, "")
cmd = exec.Command("git", "branch", "-r")
cmd.Dir = bareDir
out, err = cmd.Output()
if err != nil {
t.Fatalf("git branch -r: %v", err)
}
originMain := "origin/" + mainBranch
if !stringContains(string(out), originMain) {
t.Errorf("expected %q in remote branches, got: %s", originMain, out)
}
// Verify WorktreeAddFromRef succeeds with origin/main
// This is what polecat creation does
worktreePath := filepath.Join(tmp, "worktree")
if err := bareGit.WorktreeAddFromRef(worktreePath, "test-branch", originMain); err != nil {
t.Errorf("WorktreeAddFromRef(%q) failed: %v", originMain, err)
}
// Verify the worktree was created and has the expected file
worktreeReadme := filepath.Join(worktreePath, "README.md")
if _, err := os.Stat(worktreeReadme); err != nil {
t.Errorf("expected README.md in worktree: %v", err)
}
}
func stringContains(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}

View File

@@ -261,6 +261,12 @@ func (m *Manager) AddWithOptions(name string, opts AddOptions) (*Polecat, error)
return nil, fmt.Errorf("finding repo base: %w", err)
}
// Fetch latest from origin to ensure worktree starts from up-to-date code
if err := repoGit.Fetch("origin"); err != nil {
// Non-fatal - proceed with potentially stale code
fmt.Printf("Warning: could not fetch origin: %v\n", err)
}
// Determine the start point for the new worktree
// Use origin/<default-branch> to ensure we start from the rig's configured branch
defaultBranch := "main"