sgit-ai 0.26.5
┌─ sgit-ai 0.26.5 ── dev build ── 28 Oct 2026, 11:25 UTC ─────────────────────────
│ tag v0.26.5 = 43e1889c (dev) · PyPI package == tag
│ new pull --merge lines · branch merge --merge lines · --conflict diff3
│ tests 5,030 unit+security ✓ · X1–X4 ✓ · 18,000 merges vs git merge-file
│ open BR-2 (medium, opt-in) · BR-3 (low) · BR-1 (all builds) · R3-1
│ maturity #######--- good for an opt-in: read line-merged files before a push
└──────────────────────────────────────────────────────────────────────────────────
What is new
Git-parity item 2.1, from design 01.
$ sgit pull --merge lines # line by line, for files both sides changed
$ sgit pull --merge lines --conflict diff3 # markers carry the base version too
$ sgit branch merge feature --merge lines
$ export SGIT_MERGE_STYLE=lines # per session; or merge_style in .sg_vault/local/config.json
- How a file both sides changed is merged:
- changes to different lines merge cleanly;
- lines both sides changed get Git's markers in the file:
<<<<<<< ours (this clone …),=======,>>>>>>> theirs (current …). - Only files both sides changed are line-merged, so work in separate folders costs nothing more.
- Fall back to the whole-file
.conflict, and the pull says why: - binary files;
- files over 8 MiB;
- versions this clone does not hold.
- Resolving a conflict:
- edit the markers out and
sgit commit; commit refuses while a marker block remains; sgit resolve <file> --oursor--theirstakes one side;sgit resolve <file>marks a hand edit as resolved.- The default is unchanged: without the flag, a pull keeps yours and writes theirs to
<file>.conflict, as before and as the web UI does. The flag wins over the session and the clone setting. - Safety:
- an uncommitted edit to a file that would get markers refuses the pull before anything is written;
- marker labels come from vault data and can never start a line;
- marker files are written through the path guard.
Compatibility
No format change and no gate. A line-merged result is an ordinary commit, which every client reads.
Review by cli.build
Verdict: good for an opt-in feature.
- On unique lines, the engine is Git's: every one of 8,925 random merges was byte-identical to
git merge-file -p, in both styles. - Where lines repeat, as
}, blank lines andreturn x;do in code, it can occasionally disagree with Git about whether two changes touch. - In 2 of 4,544 code-like merges, that turned a Git conflict into a silent clean merge (BR-2).
- Agents using
--merge linesshould read the merged files before pushing, as they would review any merge.
┌─ line merge vs git merge-file · 18,000 random three-way merges, 4 seeds ─────────────
│ verdict unique code-like 3-letter alphabet
│ byte-identical 8,925 4,501 4,209
│ both conflict, blocks differ 0 41 258 ── BR-3
│ sgit conflict, git clean 0 0 36
│ sgit clean, git conflict 0 2 23 ── BR-2
│ both clean, bytes differ 0 0 5
├──────────────────────────────────────────────────────────────────────────────────────
│ ✓ X1 different lines merge ✓ X2 markers; commit refuses ✓ X3 default unchanged
│ ✓ X4 an edit that would get markers refuses the pull, edit kept
└──────────────────────────────────────────────────────────────────────────────────────
code-like: 60% of lines from {"}", "", "{", " return x;", " i++;", "else", …}
3-letter: every line is a, b or c, the worst case for any line differ
BR-2 (medium, opt-in): a clean merge where Git reports a conflict, when lines repeat
Why it happens:
- The engine is a port of xdiff's merge, run on sgit's own Myers differ, plus Git's change compaction.
- When the changed region is surrounded by repeated lines, the two differs can place a change one line apart.
- Changes that touch in Git's placement then do not touch in sgit's, so sgit merges where Git asks.
- The CHANGELOG's claim is scoped correctly ("byte-identical … on random merges of unique lines"). The property agents care about is different: "sgit never merges silently what Git would ask about". That one does not hold.
One of the two code-like cases, seed 4, diff3:
base ours theirs
line 34408 line 34413 line 34408
{ line 34412 {
end { end
if (a) { if (a) { if (a) {
line 34409 line 34408
{
end
line 34409
line 34414
if (a) {
git merge-file: a conflict around "line 34409"; theirs deleted it, ours moved "if (a) {" past it
sgit: clean, = ours, so "line 34409", which theirs deleted, is still there
Either reading is a valid alignment. Git's is the one users expect.
For the CLI team's agent, in order of cost:
- Conservative rule. Treat two one-sided changes as touching when the base lines between them, or within one line of them, are not unique in the base. A clean result then needs unambiguous context. It costs a few more conflicts and no silent merges. Re-run
fuzz_line_merge.py: the target is 0 in the "sgit clean, git conflict" row. - Exact placement. Port the rest of xdiff's diff (
xdl_recs_cmpwith its split heuristics andXDF_NEED_MINIMALoff), so that hunks land where Git puts them on repeated lines too. More work, and it also closes BR-3. - Add the repeated-line generators (the "code" and "tiny" kinds in the fuzzer) to the CLI's own tests against
git merge-file.
BR-3 (low): conflict blocks drawn differently from Git's
Where both report a conflict, the blocks differ in 41 of 4,544 code-like merges (0.9%): where a block starts and ends, or how many blocks there are. The content is not lost either way. Exact placement (BR-2, option 2) fixes this too.
Scripts: fuzz_line_merge.py <seed> <cases> [merge|diff3], check_build.py. Results: results/lm_*.json with up to three samples per verdict, and results/check_0.26.5.json. All builds: the builds page.
This page as markdown · its source, admin/content/builds/0-26-5.md