cli.sgit.ai / Research / Git parity, priority 1 and 2: index, shared foundations and reconciliation (28 Oct 2026)

Git parity, priority 1 and 2: index, shared foundations and reconciliation (28 Oct 2026)

Date2026-10-28 · 03:19 UTCKinddesignResearch topicsgitWritten bythe sgit-ai CLI team (Claude Code sessions), for Dinis CruzSourceteam/humans/dinis_cruz/claude-code-web/10/28/design__00__index-and-reconciliation.md in SGit-AI__CLI at 036e33b. Published here as written; the CLI repository is where the work happened.

What this is: the entry point to the seven design documents for the Git-parity roadmap (roadmap__git-parity.md). Each document was written against the code at 640069a and ends with an acceptance checklist. This index does three things:

Where this index and a design disagree, this index wins. The designs were written in parallel, and section 2 records the outcome of checking them against each other.


1. The documents

DocRoadmap itemsPriorityEstimateDecisions
design__01__line-merge-and-cherry-pick.md2.1 line-level merge (opt-in), 2.8 cherry-pick1 (merge), 2 (cherry-pick)4.5 d (3.25 + 1.25)D1–D11
design__02__signing-with-your-own-key.md2.2 SSH/GPG signing, Git's config, allowed signers14 dD1–D12
design__03__partial-commits-and-hooks.md2.3 commit <paths>, commit -p; 2.9 hooks1 (paths, -p), 2 (hooks)7.5 d (4.5 + 3)D1–D10
design__04__renames-blame-and-grep.md2.4 renames, 2.5 blame, 2.7 grep and log -S/-G27.5 dD1–D12
design__05__bisect.md2.6 bisect25–6 dD1–D8
design__06__worktree.md2.13 worktree (shared objects), local branch merge23.25 d + 1–1.5 dD1–D8
design__07__rebase.md2.12 rebase, pull --rebase, rebase -i26.5–7.5 dD1–D10, X1

Totals:

ScopeAs writtenAfter building the shared parts once (section 2)
Priority 1 (2.1, 2.2, 2.3)~11.75 days—
All seven documents~40 days~36–37 days

The roadmap's estimates were roughly half of this. Each design explains its own gap; the same three causes recur:


2. Shared foundations and reconciliation

Build these once, before the features that use them. Each one names the design that owns it.

#FoundationUsed byOwnerWhat was reconciled
F1One line differ: Vault__Line_Diff in sgit_ai/core/text/. Myers diff, linear space, with a cost cap and Vault__Line_Diff__Too_Different. Includes Vault__Line_Splitter and Schema__Line_Hunk.merge (01), blame, diff, pickaxe (04), rebase (07 via 01)0101 and 04 each specified a Vault__Line_Diff: 01 in core/text/ with diff(a_ids, b_ids), 04 in core/actions/diff/ with split_lines, opcodes(), matching_blocks and -w. 01's location and core win. 04's needs become methods on the same class: opcodes() and matching_blocks() derived from the hunks, and the whitespace-ignore mode as a splitter option. 04's step 3 already allows "if design 01 lands first, step 3 uses it".
F2One commit graph: Vault__Commit_Graph, with parents, generation numbers (from index packs, else computed and memoised), a generation-ordered painting walk, ancestry, and a merge base with no 200-commit cap.bisect (05), rebase range (07), cherry-pick ranges (01), log --follow (04)0505 named it Vault__Bisect__Graph; 07 D9 asked for it to be shared. It is named Vault__Commit_Graph, and the bisect-only parts (weights, best, skip_away) stay in Vault__Bisect__Graph on top of it.
F3Tree building that costs the change.partial commits and write (03), merges (01), rebase replays (07), cherry-pick (01)0303 adds folder reuse to build_from_flat(..., old_tree_ids=); 07 adds Vault__Sub_Tree.apply_changes(tree_id, changes, read_key), which walks only the spine. Both are kept, in one commit: apply_changes for callers that hold a change list (replays, picks, path commits), old_tree_ids reuse for callers that hold a full flat map. A shared test pins the invariant that both give exactly the ids build_from_flat gives today (07's I6).
F4Same content on both sides is not a conflict.every merge path: pull, branch merge, revert, cherry-pick, rebase, rewind re-apply01Found independently by 04 and 07 (X1). Blobs use a random IV, so two clones writing the same content get different blob ids. three_way_merge compares ids only, so today it reports a conflict. Fix: when ids differ, compare content_hash and size; if equal, decrypt both and compare bytes (04's verify_exact), then take ours. This changes the default whole-file merge too, so it is decision X-D1 (section 5).
F5One "operation in progress" guard: Vault__Operation_Guard.check(directory, action). It reads merge_state.json (01: merge, cherry-pick), bisect.json (05) and rebase/state.json (07), and refuses an action with the one message pattern each design already uses ("Cannot <action>: a <op> is in progress … finish it with … or abort with …").commit, write, pull, push, branch switch/checkout, stash, rebase, bisect, cherry-pick, worktree remove0101, 05 and 07 each specify their own refusals (01 D3, 05 D1, 07 D8 and check_not_in_progress). The table in 2.1 below is the combined rule; each design's own list follows it.
F6Hooks runner: Vault__Hooks, Enum__Hook_Name, VERIFY_HOOKS, Vault__Hooks.environment (secrets stripped).commit (03), rebase exec and hooks (07), cherry-pick (01)0303 D9 says "commit hooks run for commit only"; 07 runs pre-commit / commit-msg on a reword, as Git does. Resolution: follow Git. Automatic commits (pull merges, pick, cherry-pick without an edit) run no commit hooks. A commit whose message the user edits (reword, an interactive squash, cherry-pick -e) runs pre-commit, prepare-commit-msg, commit-msg and post-commit. 07 adds PRE_REBASE and POST_REWRITE.
F7Signing at commit creation: create_commit(..., user_signer=) (02).every commit path, including merges (01), picks (01), replays (07)02Consistent. Replayed and picked commits are re-signed by this clone's key and, if configured, the current user's key (07's --sign/--no-sign). 02 D10 (drop user signatures and report the count) applies to whole-history rewrites only: pack import/export, migrate.
F8Atomic object writes: temp file, fsync, rename.worktree (06), and any two sgit processes on one store0606 found object files are written in place, so a concurrent reader can see half a file. This lands first, on its own (06 commit 0).
F9Pruned diff: history show / diff / log --patch read only the changed paths.blame, rename display (04), and the cost of every rebase and cherry-pick report04It is 04's step 3; it moves into the foundations because 07 and 01 print diffs too.

2.1 What is allowed while an operation is in progress (F5)

Git's rules, with one deliberate difference: Git allows push during these operations, but sgit's push runs an implicit pull that writes the working copy, so sgit refuses it (it already does for a merge, Vault__Sync__Push.py:50). "✗" means refused by name, with the way out.

Action ↓ / in progress →merge or cherry-pick conflictbisectrebase (stopped)
commitonly once every conflict is resolved (01 D3, D4)✗ (05 D1)✗: use rebase --continue (07 D8)
write✗✗✗
pull, branch merge, cherry-pick, rebase✗✗✗
push✗✗✗
branch switch / checkout <commit>✗✗ (bisect moves the working copy itself)✗
bisect start✗—✗
stash✗✗✗ (--autostash is rebase's own)
status, log, diff, show, blame, grep, cat, ls✓, and status shows the operation (01's status block)✓✓
worktree add / remove of another worktree✓ (state is per clone)✓✓

Every operation's state lives under that clone's .sg_vault/local/, so each worktree (06) has its own and they never block each other.


3. Bugs and gaps found in today's code

The designs found these while reading the code. Fixes for real bugs can ship before any feature.

#Found byWhatKindShip
B101 (reproduced)sgit commit during an unresolved merge commits the .conflict files into the vault; every teammate who pulls gets thembugnow, as F5's first use
B201sgit status never shows an in-progress merge, though pull's output points therebugwith B1
B304, 07Same content on both sides is reported as a conflict (random-IV blob ids)bugF4
B406Object files are written in place, not atomicallybug (concurrency)F8, now
B506The working-copy scan descends into a nested vault folder and would commit its filesbugnow
B605, 07find_lca and _walk_commit_ids stop after 200 commitslimitF2
B703build_from_flat rebuilds every folder, so sgit write of one file costs the whole vaultperformanceF3
B804history show/diff/log --patch decrypt every file of both commits; log --file flattens the whole tree per commitperformanceF9
B903sgit push refuses any uncommitted change, so a partial commit could never be pushedbehaviour (prerequisite of 2.3)with 2.3
B1004The snapshot index's file-commits member is written but never readunusedblame uses it (04)

4. Implementation order

Each step leaves pytest tests/unit -n auto and pytest tests/security green. Each feature lands with its CHANGELOG entry and any threat-model rows its design lists.

Phase 0: foundations and bug fixes (~4 days)

  1. F8 atomic object writes (B4); B5 nested-vault scan.
  2. F5 operation guard, with B1 and B2 (merge state only at first).
  3. F4 same-content merge (B3), after X-D1 is decided.
  4. F3 tree building (B7).
  5. F1 line differ and F2 commit graph (B6).

Phase 0 status (28 Oct): done on claude/charming-babbage-5hzcxq (not yet merged to dev):

StepCommitWhat
16ea436dF8 atomic object writes (B4, TM-F39); B5 nested vault never scanned or committed (TM-F38)
2e4e05fbF5 Vault__Operation_Guard; B1 commit refuses unresolved conflicts (01 D3); B2 status merge block
3dac3416F4 same content is not a conflict (B3, X-D1)
4bbc3f92F3 build_from_flat folder reuse and apply_changes (B7)
5a8a1d6ecF1 Vault__Line_Diff, Vault__Line_Splitter (sgit_ai/core/text/)
5b21a4bf6F2 Vault__Commit_Graph, no 200-commit cap (B6)

F9 (pruned diff) stays with design 04, as planned. Each step's tests fail on the code before it.

Phase 1: priority 1 (~10 days after phase 0)

  1. 2.1 line-level merge, opt-in (01 steps for 2.1).
  2. 2.3 commit <paths> and commit -p, with the push prerequisite B9 (03 steps for 2.3).
  3. 2.2 signing with your own key (02); the signing half waits on the web UI check (02 D7).

Phase 2: priority 2 (~22 days)

  1. F9 pruned diff, then 2.4 renames, 2.5 blame, 2.7 grep (04).
  2. 2.8 cherry-pick (01).
  3. 2.9 hooks (03), then F6 extended by rebase.
  4. 2.12 rebase (07).
  5. 2.6 bisect (05).
  6. 2.13 worktree, phases 1 and 2 (06).

A release can follow each phase. Phase 1 is the "line merges, signing with your own key, partial commits" release.


5. Decisions waiting on Dinis

72 decisions are recorded across the seven documents, each with a recommendation. Most follow Git and need only a glance. These are the ones that change behaviour, the command surface or a dependency:

#DecisionRecommendationDoc
X-D1Same content on both sides stops being a conflict, including in the default whole-file mergeYes. It is a bug fix: the .conflict file would hold the same bytes as the working file.this index (F4)
01 D3commit refuses while conflicts are unresolved, including for the default .conflict styleYes (fixes B1)01
01 D1 / D10How line merge is switched on: --merge lines, merge_style in local config, SGIT_MERGE_STYLE; no config command yetAs recommended01
02 D2What turns user signing onsgit.gpgSign in Git config, off by default. Not Git's own commit.gpgSign, which would make agents start signing, or failing, on upgrade.02
02 D7Hold signing until the web UI team confirms its signing bytes exclude the new field; verification ships regardlessYes02
03 D1Staging areaNone for now: pathspec plus -p03
03 D2sgit commit -m msg a b makes a b paths (sgit commit "msg" unchanged)Yes03
03 D5 / D6Hooks from inside the vault only with an explicit opt-in; configured under sgit vault hooksYes03
04 D1grep becomes a top-level command (edit TOP_LEVEL_COMMANDS), also file grepYes04
04 D2Python regex syntax, not Git's basic regexYes04
04 D4Renames detected by default; JSON gains files_renamedYes04
05 D1Commits refused during a bisectYes05
05 D3bisect run takes argv; a shell only with --shellYes05
06 D1worktree becomes a top-level commandYes06
06 D2The worktree copies the vault key (0600) rather than pointing at the main'sYes06
06 D6No worktrees of read-only clones in v1Yes06
07 D2Rebase never rewrites pushed commits in v1Yes07
07 D3Replaying another clone's commits needs --allow-foreignYes07
07 D5pull_rebase is off by default (merge stays the pull default)Yes07

Top-level command changes in total: grep and worktree (CLAUDE.md rule 9). Everything else goes under history (blame, bisect, cherry-pick, rebase), vault (hooks) or existing commands (commit, pull, branch merge).

This page as markdown · its source, admin/content/research/git-parity-designs-index.md