Skip to content

Commit 29b8534

Browse files
authored
Fix render failing on fresh bare clones (#19)
* fix render failing on fresh bare clones by fetching remote tracking refs * allow same branch in multiple worktrees with --force
1 parent 44326a3 commit 29b8534

3 files changed

Lines changed: 20 additions & 1 deletion

File tree

internal/git/git.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,11 @@ func (r *RealRunner) Fetch(ctx context.Context, repoPath string) error {
109109
}
110110

111111
// AddWorktree creates a worktree from a bare repo at the given path and branch.
112+
// Uses --force to allow the same branch to be checked out in multiple worktrees
113+
// across different workspaces.
112114
func (r *RealRunner) AddWorktree(ctx context.Context, bareRepo, worktreePath, branch string) error {
113115
r.log().Debug("adding worktree", "bare_repo", bareRepo, "worktree", worktreePath, "branch", branch)
114-
return r.run(ctx, "-C", bareRepo, "worktree", "add", worktreePath, branch)
116+
return r.run(ctx, "-C", bareRepo, "worktree", "add", "--force", worktreePath, branch)
115117
}
116118

117119
// AddWorktreeNewBranch creates a worktree with a new branch starting from startPoint.

internal/workspace/workspace.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ func (s *Service) Render(ctx context.Context, id string, progress func(msg strin
209209
if err := s.Git.BareClone(ctx, repo.URL, barePath); err != nil {
210210
return fmt.Errorf("cloning %s: %w", repo.URL, err)
211211
}
212+
// Fetch after bare clone to create remote tracking refs
213+
// (bare clones don't create refs/remotes/origin/* by default).
214+
if err := s.Git.Fetch(ctx, barePath); err != nil {
215+
return fmt.Errorf("fetching %s: %w", repo.URL, err)
216+
}
212217
} else {
213218
s.log().Debug("bare clone exists, fetching", "url", repo.URL, "path", barePath)
214219
if err := s.Git.Fetch(ctx, barePath); err != nil {
@@ -239,6 +244,12 @@ func (s *Service) Render(ctx context.Context, id string, progress func(msg strin
239244
return fmt.Errorf("getting default branch for %s: %w", repo.URL, err)
240245
}
241246
}
247+
// Ensure the remote tracking ref exists for the base branch so
248+
// origin/{baseBranch} resolves (especially when baseBranch differs
249+
// from the default branch, which Fetch only creates refs for).
250+
if err := s.Git.EnsureRemoteRef(ctx, barePath, baseBranch); err != nil {
251+
return fmt.Errorf("ensuring remote ref for %s: %w", repo.URL, err)
252+
}
242253
// Use the remote ref to ensure we branch from the latest fetched state,
243254
// not a potentially stale local branch ref in the bare repo.
244255
startPoint := "origin/" + baseBranch

internal/workspace/workspace_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,10 @@ func TestRenderNewBranchUsesRemoteRef(t *testing.T) {
465465
if mock.startPoints[0] != "origin/main" {
466466
t.Errorf("startPoint = %q, want origin/main", mock.startPoints[0])
467467
}
468+
// EnsureRemoteRef should be called for the base branch during render
469+
if len(mock.remoteRefs) != 1 || mock.remoteRefs[0] != "main" {
470+
t.Errorf("remoteRefs = %v, want [main]", mock.remoteRefs)
471+
}
468472
}
469473

470474
func TestRenderNewBranchUsesBaseField(t *testing.T) {
@@ -836,6 +840,7 @@ func TestSyncCleanRebase(t *testing.T) {
836840
}
837841

838842
mock.fetches = nil
843+
mock.remoteRefs = nil
839844
mock.isClean = true
840845
if err := svc.Sync(ctx, "sync-clean", noop); err != nil {
841846
t.Fatalf("Sync: %v", err)
@@ -892,6 +897,7 @@ func TestSyncUsesBaseField(t *testing.T) {
892897
}
893898

894899
mock.fetches = nil
900+
mock.remoteRefs = nil
895901
mock.isClean = true
896902
if err := svc.Sync(ctx, "sync-base", noop); err != nil {
897903
t.Fatalf("Sync: %v", err)

0 commit comments

Comments
 (0)