feat: detect uncommitted JSONL changes before sync (GH#885, bd-vd8e)
Add pre-flight safety check to detect when a previous sync exported but failed before commit, leaving JSONL in an inconsistent state. - Add gitHasUncommittedBeadsChanges() helper in sync_git.go - Call in sync pre-flight checks after merge/rebase check - If uncommitted changes detected, force re-export to reconcile state This catches the failure mode early before it compounds across worktrees. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
7b90678afe
commit
44a5c3a0ec
@@ -447,6 +447,45 @@ func restoreBeadsDirFromBranch(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// gitHasUncommittedBeadsChanges checks if .beads/issues.jsonl has uncommitted changes.
|
||||
// This detects the failure mode where a previous sync exported but failed before commit.
|
||||
// Returns true if the JSONL file has staged or unstaged changes (M or A status).
|
||||
// GH#885: Pre-flight safety check to detect incomplete sync operations.
|
||||
func gitHasUncommittedBeadsChanges(ctx context.Context) (bool, error) {
|
||||
beadsDir := beads.FindBeadsDir()
|
||||
if beadsDir == "" {
|
||||
return false, nil // No beads dir, nothing to check
|
||||
}
|
||||
|
||||
jsonlPath := filepath.Join(beadsDir, "issues.jsonl")
|
||||
|
||||
// Check git status for the JSONL file specifically
|
||||
cmd := exec.CommandContext(ctx, "git", "status", "--porcelain", jsonlPath) //nolint:gosec // G204: jsonlPath from internal beads.FindBeadsDir()
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("git status failed: %w", err)
|
||||
}
|
||||
|
||||
// Parse status output - look for modified/added files
|
||||
// Format: XY filename where X=staged, Y=unstaged
|
||||
// M = modified, A = added, ? = untracked
|
||||
statusLine := strings.TrimSpace(string(output))
|
||||
if statusLine == "" {
|
||||
return false, nil // No changes
|
||||
}
|
||||
|
||||
// Any status (M, A, MM, AM, etc.) indicates uncommitted changes
|
||||
if len(statusLine) >= 2 {
|
||||
x, y := statusLine[0], statusLine[1]
|
||||
// Check for modifications (staged or unstaged)
|
||||
if x == 'M' || x == 'A' || y == 'M' || y == 'A' {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// getDefaultBranch returns the default branch name (main or master) for origin remote
|
||||
// Checks remote HEAD first, then falls back to checking if main/master exist
|
||||
func getDefaultBranch(ctx context.Context) string {
|
||||
|
||||
Reference in New Issue
Block a user