# sgit-ai 0.26.6: commit some paths, commit some hunks, push with uncommitted changes, cli.sgit.ai

> Dev build of 28 October 2026, from the CLI's dev at 036e33b5, and what a plain pip install gives today. sgit commit takes paths, as git commit -- paths does, and leaves every other change on disk. sgit commit -p picks hunks interactively; agents use --list-hunks and --hunk ID, with ids that refuse to pick a different hunk once the file has changed. sgit push no longer refuses uncommitted changes. One behaviour change for scripts: with -m, extra words are now paths. Reviewed by cli.build. 5,144 unit and security tests pass. commit with paths matched Git in 40 random cases, and hunk commits matched git apply --cached in 60. Maturity good. One high finding, in every build since at least 0.26.0: a push of more than 100 commits leaves a gap in the host's history (BR-1).

*Source: <https://cli.sgit.ai/builds/0-26-6.html> · generated from the same content as the page, so the two cannot drift.*

---
# sgit-ai 0.26.6

```text
┌─ sgit-ai 0.26.6 ── dev build ── 28 Oct 2026, 11:47 UTC ─────────────────────────
│ tag v0.26.6 = 036e33b5 (dev) · PyPI package == tag · a plain install gets this
│ new     commit <paths> · commit -p · --list-hunks / --hunk · push when dirty
│ tests   5,144 unit+security ✓ · 18/18 end-to-end ✓ · 100 Git differentials ✓
│ open    BR-1 (high, every build) · BR-2, BR-3 (0.26.5) · R3-1
│ maturity  #######---  good: use it; push at most 100 commits at a time
└──────────────────────────────────────────────────────────────────────────────────
```

```text
$ pip install sgit-ai==0.26.6        # or: sgit update, which takes the newest build
$ sgit version
sgit-ai v0.26.6
```

## What is new {#new}

This build is Git-parity item 2.3, from [design 03](../research/git-parity-design-partial-commits.md). With 0.26.5's line merge, it closes the two gaps that mattered most in daily use.

### Commit only some paths

```text
$ sgit commit -m "fix the intro" docs/intro.md src/      # only these; everything else stays uncommitted
$ sgit commit "fix the intro" -- docs/intro.md           # the same, message first
$ sgit commit -m "docs" "*.md"                           # * crosses / as in Git
$ sgit commit -m "rm" old.txt --json                     # a deleted file, named, is committed as a deletion
```

- **What it commits:** HEAD's tree with only the named paths taken from the working copy, as `git commit <pathspec>` does. The output lists what stays uncommitted.
- **Folders, files and patterns:**
  - a folder takes every change under it;
  - a path is relative to the current folder;
  - **unlike Git,** naming a new file commits it, since sgit has no `add`.
- **Refused, with nothing written:**
  - a pattern that matches nothing refuses the commit;
  - a path commit during a merge is refused, as in Git.
- **`--json` prints one object:** `committed` (with `paths` and `left_uncommitted`), `nothing_to_commit`, or `refused`.

### Commit some hunks of a file

```text
$ sgit commit -p                                  # interactive: y n a d s j k q ?   (s splits a hunk)
$ sgit commit --list-hunks --json notes.md        # agents: every hunk with an id
$ sgit commit -m "first fix" --hunk 3f9c… --hunk 81ab…
```

- **Ids come from the hunk's content and base, not its position.** An id listed before the file or HEAD changed is refused (exit 2) and nothing is written.
- **The working copy is never written.** The hunks not committed show as uncommitted changes.
- **The same hunks as Git:** headers match `git diff -U3`, and any subset gives the bytes `git apply --cached` gives.
- **Committed whole, as one hunk:** binary files, new files, deleted files and files over 8 MiB.

### `sgit push` with uncommitted changes

- **It pushes your commits** and says how many changes stay local. It used to refuse, or offer to commit them.
- **The implicit pull still protects the working copy:**
  - an edit that the incoming commits do not touch is kept;
  - one they would change refuses the push before anything is written.

### A behaviour change for scripts

- `sgit commit "fix typo"` still commits everything.
- **With `-m`, other words are now paths**, as in Git. `sgit commit -m x y` used to ignore `y`; now it fails if `y` is not a file.
- Two words without `-m` is a usage error that shows both correct forms.

## Compatibility {#compat}

- **Nothing changes in the vault format, and there is no new gate.** A commit made with paths or hunks is an ordinary commit.
- **Checked:** 0.26.0 and 0.26.3 clone, pull and push against a vault where 0.26.6 committed by path and by hunk. Both read the same trees, and a file left uncommitted by 0.26.6 stays local.

## Review by cli.build {#review}

**Verdict: good.**
- Every claim of this build held, including the two checked against Git as the oracle.
- Use it, including for agents.
- One high finding is not this build's own: it is in every build tested, 0.26.0 to 0.26.6 (BR-1).
- Until it is fixed, **push at least once every 100 commits.**

```text
┌─ checks on the PyPI package 0.26.6 ───────────────────────────────────────────────
│ ✓ C1  commit <paths> vs `git add -A -- <paths>; git commit`   40 random cases, 0 diff
│ ✓ C2  hunk subsets vs `git apply --cached`, headers vs -U3     60 cases, 0 diff
│ ✓ C3  a stale hunk id is refused; HEAD and objects unchanged
│ ✓ C4  push with an uncommitted edit: commits pushed, edit kept
│ ✓ C5  incoming commit would change an edited file: push refused, edit kept
│ ✓     everything from 0.26.1–0.26.5 (G1–G2, M1–M5, L1–L2, X1–X4) still holds
│ ✓     cross-version with 0.26.0 and 0.26.3, both directions
│ ✗ P1  one push of 150 commits: fresh clone's fsck fails  ── BR-1, below
│ ✗     R3-1 unchanged: 64 bad offsets after compaction of a 0.25.0 vault
└──────────────────────────────────────────────────────────────────────────────────────
```

### BR-1 (high, every build since at least 0.26.0): a push of more than 100 commits leaves a gap on the host {#br-1}

**What happens.**
- `Vault__Sync__Push` collects the commits to upload with `Vault__Fetch.fetch_commit_chain(…, limit=100)`.
- Push calls it from two places (`Vault__Sync__Push.py:252` and `:666`) without passing a limit, so a push of 101 or more new commits uploads only the newest 100, with their trees and blobs.
- The ref moves to the new head. The older commits exist only in the pushing clone.
- **The gap never fills.** The next push stops at the remote head.

```text
one push of N commits, then one more push; a fresh clone walks the history and runs fsck
  ✓ N=100  walks  102 of 102 commits   fsck ok
  ✓ N=101  walks  103 of 103 commits   fsck ok        (100 new + the later push)
  ✗ N=102  walks  101 of 104 commits   fsck: missing 1
  ✗ N=151  walks  101 of 153 commits   fsck: missing 1   (normal and pack vaults alike)
```

**What it breaks:**
- a fresh clone's `check fsck`;
- `history log` past the gap;
- every merge whose base lies behind the gap. A teammate with their own commits who pulls gets a merge against an empty base: every file both sides touched conflicts, and the pushing side's change is not applied. My first deep-fork test failed this way at 101 commits, before I separated the two causes. 0.26.3's merge-base fix is real (M4 passes when pushes are at most 50 commits).
- **If the pushing clone is deleted, those commits are lost.**

**How likely:** an agent that commits often and pushes rarely, a long offline session, or `sgit write` in a loop.

**For the CLI team's agent:**
1. Drop the cap for push. Walk with the commit graph from 0.26.3 (`Vault__Commit_Graph`), stopping at the remote head's ancestors, with no limit. The stop set is capped at 100 too.
2. Before moving the ref, check that every uploaded commit's parents are either in the upload set or already on the host.
3. Give an existing gap a repair path. The pushing clone still holds the commits, so `sgit push` (or `check fsck --repair` on that clone) could upload the missing ancestors.
4. The repro is [`push_gap.py`](https://github.com/SGit-AI/SGit-AI__Website__Cli/tree/dev/admin/research/builds-0-26-x-2026-10-28/scripts): `python -I push_gap.py 100 101 150`, with `PACKS=1` for a pack vault.

### Also noted

- **The `-m` change** is right, and Git's rule. Say it in the release notes for 0.27.0: a script that passed extra words after `-m` now fails loudly instead of committing everything.
- **Not tested here:** `-p` interactively (it needs a terminal), and hooks (not built).

*Scripts: [`check_build.py`](https://github.com/SGit-AI/SGit-AI__Website__Cli/tree/dev/admin/research/builds-0-26-x-2026-10-28/scripts), `push_gap.py`, `xstep.py`. Results: `results/check_0.26.6.json`, `results/suite-v0.26.6.txt`. All builds: [the builds page](../builds/index.md).*

---

*[Site index for agents](../llms.txt) · [HTML version](https://cli.sgit.ai/builds/0-26-6.html)*
