fix(sync): atomic export and force-push detection (bd-3bhl, bd-4hh5)
bd-3bhl: Add sync rollback on git commit failure - Use exportToJSONLDeferred() instead of exportToJSONL() for atomic sync - Call finalizeExport() only after git commit succeeds - Rollback JSONL from git HEAD on commit failure - Add rollbackJSONLFromGit() helper function - Coverage: regular commit, sync branch, external beads repo paths bd-4hh5: Fix false-positive force-push detection - Use explicit refspec in CheckForcePush fetch - +refs/heads/beads-sync:refs/remotes/origin/beads-sync - Ensures tracking ref is always created/updated - Fixes stale ref comparison causing false positives 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -492,6 +492,27 @@ func parseGitStatusForBeadsChanges(statusOutput string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// rollbackJSONLFromGit restores the JSONL file from git HEAD after a failed commit.
|
||||
// This is part of the sync atomicity fix (GH#885/bd-3bhl): when git commit fails
|
||||
// after export, we restore the JSONL to its previous state so the working
|
||||
// directory stays consistent with the last successful sync.
|
||||
func rollbackJSONLFromGit(ctx context.Context, jsonlPath string) error {
|
||||
// Check if the file is tracked by git
|
||||
cmd := exec.CommandContext(ctx, "git", "ls-files", "--error-unmatch", jsonlPath)
|
||||
if err := cmd.Run(); err != nil {
|
||||
// File not tracked - nothing to restore
|
||||
return nil
|
||||
}
|
||||
|
||||
// Restore from HEAD
|
||||
restoreCmd := exec.CommandContext(ctx, "git", "checkout", "HEAD", "--", jsonlPath) //nolint:gosec // G204: jsonlPath from internal beads.FindBeadsDir()
|
||||
output, err := restoreCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("git checkout failed: %w\n%s", err, output)
|
||||
}
|
||||
return 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