fix(context): complete RepoContext migration for remaining sync files (#1114)

Follow-up to #1102 - migrates remaining git command locations to use
RepoContext API for correct repo resolution when BEADS_DIR is set.

Files migrated:
- sync_branch.go: getCurrentBranch, showSyncStatus, mergeSyncBranch
- sync_check.go: checkForcedPush
- sync_import.go: doSyncFromMain
- autoimport.go: readFromGitRef, checkGitForIssues
- status.go: getGitActivity
- import.go: attemptAutoMerge (gitRoot lookup)
- reinit_test.go: add ResetCaches for test isolation

Pattern used throughout:
- Try RepoContext first: rc.GitCmd() runs in beads repo
- Fallback to CWD for tests or repos without beads
- Graceful degradation maintains backwards compatibility
This commit is contained in:
Peter Chanthamynavong
2026-01-15 19:23:07 -08:00
committed by GitHub
parent fabf857b2a
commit e110632afc
7 changed files with 110 additions and 33 deletions

View File

@@ -8,13 +8,22 @@ import (
"strings"
"time"
"github.com/steveyegge/beads/internal/beads"
"github.com/steveyegge/beads/internal/syncbranch"
)
// getCurrentBranch returns the name of the current git branch
// getCurrentBranch returns the name of the current git branch in the beads repo.
// Uses symbolic-ref instead of rev-parse to work in fresh repos without commits (bd-flil)
// GH#1110: Now uses RepoContext to ensure we query the beads repo, not CWD.
// Falls back to CWD-based query if no beads context found (for tests/standalone git).
func getCurrentBranch(ctx context.Context) (string, error) {
cmd := exec.CommandContext(ctx, "git", "symbolic-ref", "--short", "HEAD")
var cmd *exec.Cmd
if rc, err := beads.GetRepoContext(); err == nil {
cmd = rc.GitCmd(ctx, "symbolic-ref", "--short", "HEAD")
} else {
// Fallback to CWD for tests or repos without beads
cmd = exec.CommandContext(ctx, "git", "symbolic-ref", "--short", "HEAD")
}
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed to get current branch: %w", err)
@@ -53,12 +62,18 @@ func getSyncBranch(ctx context.Context) (string, error) {
return syncBranch, nil
}
// showSyncStatus shows the diff between sync branch and main branch
// showSyncStatus shows the diff between sync branch and main branch.
// GH#1110: Now uses RepoContext to ensure git commands run in beads repo.
func showSyncStatus(ctx context.Context) error {
if !isGitRepo() {
return fmt.Errorf("not in a git repository")
}
rc, err := beads.GetRepoContext()
if err != nil {
return fmt.Errorf("failed to get repo context: %w", err)
}
currentBranch := getCurrentBranchOrHEAD(ctx)
syncBranch, err := getSyncBranch(ctx)
@@ -67,7 +82,7 @@ func showSyncStatus(ctx context.Context) error {
}
// Check if sync branch exists
checkCmd := exec.CommandContext(ctx, "git", "show-ref", "--verify", "--quiet", "refs/heads/"+syncBranch) //nolint:gosec // syncBranch from config
checkCmd := rc.GitCmd(ctx, "show-ref", "--verify", "--quiet", "refs/heads/"+syncBranch)
if err := checkCmd.Run(); err != nil {
return fmt.Errorf("sync branch '%s' does not exist", syncBranch)
}
@@ -77,7 +92,7 @@ func showSyncStatus(ctx context.Context) error {
// Show commit diff
fmt.Println("Commits in sync branch not in main:")
logCmd := exec.CommandContext(ctx, "git", "log", "--oneline", currentBranch+".."+syncBranch) //nolint:gosec // branch names from git
logCmd := rc.GitCmd(ctx, "log", "--oneline", currentBranch+".."+syncBranch)
logOutput, err := logCmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to get commit log: %w\n%s", err, logOutput)
@@ -90,7 +105,7 @@ func showSyncStatus(ctx context.Context) error {
}
fmt.Println("\nCommits in main not in sync branch:")
logCmd = exec.CommandContext(ctx, "git", "log", "--oneline", syncBranch+".."+currentBranch) //nolint:gosec // branch names from git
logCmd = rc.GitCmd(ctx, "log", "--oneline", syncBranch+".."+currentBranch)
logOutput, err = logCmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to get commit log: %w\n%s", err, logOutput)
@@ -104,7 +119,7 @@ func showSyncStatus(ctx context.Context) error {
// Show file diff for .beads/issues.jsonl
fmt.Println("\nFile differences in .beads/issues.jsonl:")
diffCmd := exec.CommandContext(ctx, "git", "diff", currentBranch+"..."+syncBranch, "--", ".beads/issues.jsonl") //nolint:gosec // branch names from git
diffCmd := rc.GitCmd(ctx, "diff", currentBranch+"..."+syncBranch, "--", ".beads/issues.jsonl")
diffOutput, err := diffCmd.CombinedOutput()
if err != nil {
// diff returns non-zero when there are differences, which is fine
@@ -122,12 +137,18 @@ func showSyncStatus(ctx context.Context) error {
return nil
}
// mergeSyncBranch merges the sync branch back to the main branch
// mergeSyncBranch merges the sync branch back to the main branch.
// GH#1110: Now uses RepoContext to ensure git commands run in beads repo.
func mergeSyncBranch(ctx context.Context, dryRun bool) error {
if !isGitRepo() {
return fmt.Errorf("not in a git repository")
}
rc, err := beads.GetRepoContext()
if err != nil {
return fmt.Errorf("failed to get repo context: %w", err)
}
currentBranch, err := getCurrentBranch(ctx)
if err != nil {
return err
@@ -139,13 +160,13 @@ func mergeSyncBranch(ctx context.Context, dryRun bool) error {
}
// Check if sync branch exists
checkCmd := exec.CommandContext(ctx, "git", "show-ref", "--verify", "--quiet", "refs/heads/"+syncBranch) //nolint:gosec // syncBranch from config
checkCmd := rc.GitCmd(ctx, "show-ref", "--verify", "--quiet", "refs/heads/"+syncBranch)
if err := checkCmd.Run(); err != nil {
return fmt.Errorf("sync branch '%s' does not exist", syncBranch)
}
// Check if there are uncommitted changes
statusCmd := exec.CommandContext(ctx, "git", "status", "--porcelain")
statusCmd := rc.GitCmd(ctx, "status", "--porcelain")
statusOutput, err := statusCmd.Output()
if err != nil {
return fmt.Errorf("failed to check git status: %w", err)
@@ -159,7 +180,7 @@ func mergeSyncBranch(ctx context.Context, dryRun bool) error {
if dryRun {
fmt.Println("→ [DRY RUN] Would merge sync branch")
// Show what would be merged
logCmd := exec.CommandContext(ctx, "git", "log", "--oneline", currentBranch+".."+syncBranch) //nolint:gosec // branch names from git
logCmd := rc.GitCmd(ctx, "log", "--oneline", currentBranch+".."+syncBranch)
logOutput, _ := logCmd.CombinedOutput()
if len(strings.TrimSpace(string(logOutput))) > 0 {
fmt.Println("\nCommits that would be merged:")
@@ -171,7 +192,7 @@ func mergeSyncBranch(ctx context.Context, dryRun bool) error {
}
// Perform the merge
mergeCmd := exec.CommandContext(ctx, "git", "merge", syncBranch, "-m", fmt.Sprintf("Merge sync branch '%s'", syncBranch)) //nolint:gosec // syncBranch from config
mergeCmd := rc.GitCmd(ctx, "merge", syncBranch, "-m", fmt.Sprintf("Merge sync branch '%s'", syncBranch))
mergeOutput, err := mergeCmd.CombinedOutput()
if err != nil {
return fmt.Errorf("merge failed: %w\n%s", err, mergeOutput)