DRAFT: .git-aware fast-forward conflict resolution (closes bidirectional repo-roam R3; G5-git-5 FF half) — agent-drafted, needs review#513
Draft
Jesssullivan wants to merge 2 commits into
Draft
Conversation
…ints Behavior-preserving cleanup of the conflict-resolution chokepoints ahead of the (separate) .git-aware conflict-resolution feature. No sync/conflict behavior changes. - reconcile.rs classify_path: delete the stale dead comment block trailing the match (the case it describes is already handled by the (Some, None, Some) arm). Comment-only. - reconcile.rs list_remote_index: use the existing DIR_MARKER_SUFFIX const instead of the byte-identical literal "/.tcfs_dir". Value-identical. - conflict.rs compare_clocks: collapse the Some(Ordering::Equal) and None arms into one Some(Ordering::Equal) | None arm; their ConflictInfo construction was byte-identical, so both still produce the same Conflict outcome. - reconcile.rs: extract the identical outcome -> ReconcileAction tail shared by compare_both_exist and compare_both_exist_symlink into one private helper outcome_to_action(). Pure code motion. - git_safety.rs: delete the dead pub fn restore_git_from_bundle (zero callers; superseded by restore_git_bundle_into). Deliberately excludes the rejected-unsafe items (the Conflict-arm/mark_conflict swap, the (true,true)=>None exhaustiveness arm, the UpToDate-on-hash-failure policy, and the refs/heads/*.lock literal) — those are feature-PR territory. Refs the G5-git-5 concurrent-write corruption gap in docs/ops/repo-roam-test-plan-2026-06-08.md.
Add a fast-forward reclassifier at the reconcile conflict boundary for raw git-sync mode. When a repo's .git/* paths conflict purely because each device ticked its own vclock, and the local/remote branch tips are in a strict ancestor (fast-forward) relationship, reclassify the repo's .git conflicts atomically toward the FF-ahead winner: - remote tip ancestor of local -> Push (LocalNewer) - local tip ancestor of remote -> Pull (RemoteNewer) - divergent / equal-but-different / missing object -> stays Conflict (fail-closed) Generic compare_clocks for normal files is untouched. The reclassifier only engages when git_ff_resolution is set and git_sync_mode is "raw". Ordering: in both the sequential execute_plan path and the concurrent new-remote pull fast path, .git objects/packs now apply before refs (.git/refs/**, packed-refs, HEAD) so a ref never advances to an object not yet present locally. TOCTOU: wire acquire_git_lock (previously zero callers) into execute_plan via an RAII GitLockGuard, holding a per-repo .git/tcfs.lock across the apply window so a commit mid-collection cannot tear the push. Config: thread git_sync_mode + git_ff_resolution through ReconcileConfig and an encryption context through reconcile() (needed to read remote ref blobs for the ancestry probe); wired in the daemon and CLI reconcile paths. New test git_ff_resolution.rs reproduces R3: FF converges (B pushes, A pulls, both end at C1, fsck clean) and a divergent case stays Conflict. Stacked on #512 (cleanup of the conflict/reconcile chokepoints).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Agent-drafted, needs review.
What
Implements
.git-aware fast-forward conflict resolution for raw git-dir sync, closing the bidirectional repo-roam R3 case (the common "work on honey, pick up on neo" handoff). This is the FF half of G5-git-5. Divergent keep-both (T10/T11) is intentionally left as a separate follow-up — for divergent.gitstates this PR leaves theConflictas-is (fail-closed).Why bidirectional
.gitroam failedcompare_clockssaw each device independently tick its vclock on.git/refs/heads/<branch>+.git/index+.git/logs, so concurrent edits classified asConflictwith no git-ancestry check to demote a fast-forward; and theConflictarm ofexecute_planonly records, never resolves.Design
reclassify_git_ff_conflictsinreconcile.rs), run as a post-classification pass — only whengit_ff_resolutionis set andgit_sync_mode == "raw". Genericcompare_clocksfor normal files is untouched..gitpath: find the enclosing repo root + the branch ref, read the local commit SHA (live repo) and the remote commit SHA (remote ref blob), and probe ancestry locally viagit merge-base --is-ancestor(classify_fast_forwardingit_safety.rs):.gitconflicts are resolved atomically toward one winner; a repo is never split half push / half pull.execute_plan(sequential) and the concurrent new-remote pull fast path,.git/objects/**+ packs apply before refs (.git/refs/**,packed-refs,HEAD) so a ref never advances to an object not yet present locally.acquire_git_lock(previously zero callers) is now wired intoexecute_planvia an RAIIGitLockGuard, holding a per-repo.git/tcfs.lockacross the apply window.git_sync_mode+git_ff_resolutionthreaded throughReconcileConfig; an encryption context threaded throughreconcile()(needed to read remote ref blobs). Wired in the daemon + CLI reconcile paths (FF on whensync_git_dirs+raw).Test
crates/tcfs-sync/tests/git_ff_resolution.rs:git fsck --fullclean both sides..git/refs/heads/mainconflict remains a Conflict and is never reclassified.Gate (local)
cargo fmt --all -- --check: cleancargo clippy --workspace --all-targets: only the 2 pre-existingneedless_borrowwarnings intcfsd/src/grpc.rs; no new warningscargo test -p tcfs-sync(incl. new FF test) +cargo test -p tcfsd: all pass🤖 Generated with Claude Code