From 3ffd1c1030d0e185b57f70b1e8761c030c2bb9b8 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Sat, 29 Nov 2025 22:29:10 -0800 Subject: [PATCH] fix(sync): default noGitHistory=true for --from-main mode (#418) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #417: When using --from-main mode (either explicitly or auto-detected), git history backfill now defaults to disabled. This prevents creating incorrect deletion records for locally-created beads that don't exist in main's git history. Changes: - Add resolveNoGitHistoryForFromMain() helper function - Apply noGitHistory=true for both explicit and auto-detected from-main mode - Add comprehensive unit tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: GraemeF Co-Authored-By: Claude --- cmd/bd/sync.go | 17 +++++++++++++++- cmd/bd/sync_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/cmd/bd/sync.go b/cmd/bd/sync.go index 19aa9841..4350fbef 100644 --- a/cmd/bd/sync.go +++ b/cmd/bd/sync.go @@ -68,6 +68,9 @@ Use --merge to merge the sync branch back to main branch.`, daemonClient = nil } + // Resolve noGitHistory based on fromMain (fixes #417) + noGitHistory = resolveNoGitHistoryForFromMain(fromMain, noGitHistory) + // Find JSONL path jsonlPath := findJSONLPath() if jsonlPath == "" { @@ -170,7 +173,8 @@ Use --merge to merge the sync branch back to main branch.`, if hasGitRemote(ctx) { // Remote exists but no upstream - use from-main mode fmt.Println("→ No upstream configured, using --from-main mode") - if err := doSyncFromMain(ctx, jsonlPath, renameOnImport, dryRun, noGitHistory); err != nil { + // Force noGitHistory=true for auto-detected from-main mode (fixes #417) + if err := doSyncFromMain(ctx, jsonlPath, renameOnImport, dryRun, true); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } @@ -1495,3 +1499,14 @@ func sanitizeJSONLWithDeletions(jsonlPath string) (*SanitizeResult, error) { return result, nil } + +// resolveNoGitHistoryForFromMain returns the resolved noGitHistory value for sync operations. +// When syncing from main (--from-main), noGitHistory is forced to true to prevent creating +// incorrect deletion records for locally-created beads that don't exist on main. +// See: https://github.com/steveyegge/beads/issues/417 +func resolveNoGitHistoryForFromMain(fromMain, noGitHistory bool) bool { + if fromMain { + return true + } + return noGitHistory +} diff --git a/cmd/bd/sync_test.go b/cmd/bd/sync_test.go index ea8e999f..6a95efd9 100644 --- a/cmd/bd/sync_test.go +++ b/cmd/bd/sync_test.go @@ -1105,3 +1105,50 @@ func TestHashBasedStalenessDetection_bd_f2f(t *testing.T) { t.Error("hasJSONLChanged should return false after hash is updated to match JSONL") } } + +// TestResolveNoGitHistoryForFromMain tests that --from-main forces noGitHistory=true +// to prevent creating incorrect deletion records for locally-created beads. +// See: https://github.com/steveyegge/beads/issues/417 +func TestResolveNoGitHistoryForFromMain(t *testing.T) { + tests := []struct { + name string + fromMain bool + noGitHistory bool + want bool + }{ + { + name: "fromMain=true forces noGitHistory=true regardless of flag", + fromMain: true, + noGitHistory: false, + want: true, + }, + { + name: "fromMain=true with noGitHistory=true stays true", + fromMain: true, + noGitHistory: true, + want: true, + }, + { + name: "fromMain=false preserves noGitHistory=false", + fromMain: false, + noGitHistory: false, + want: false, + }, + { + name: "fromMain=false preserves noGitHistory=true", + fromMain: false, + noGitHistory: true, + want: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := resolveNoGitHistoryForFromMain(tt.fromMain, tt.noGitHistory) + if got != tt.want { + t.Errorf("resolveNoGitHistoryForFromMain(%v, %v) = %v, want %v", + tt.fromMain, tt.noGitHistory, got, tt.want) + } + }) + } +}