Skip to content

Commit ddd6db7

Browse files
Mbd06bclaude
andcommitted
parity: branch foundations sprint + upstream merge train
## Summary Closes 9 compat-mode rows in tests/journey/parity/branch.sh, lands three reusable foundations consumed by future sprints (commit, push, tag, merge), and folds in 6 upstream syncs that arrived during the sprint. ## Closed parity rows (all bytes-mode, steward-verified, on-disk state asserted) - `-v` / `--verbose` — column-aligned name + SHA + subject - `-vv` — adds `[upstream: ahead N, behind N]` tracking annotation - `--abbrev=N` / `--no-abbrev` — SHA width override - `--column=always` — column-major packing - `-u` / `--set-upstream-to` — writes branch.<n>.{remote,merge} - `--unset-upstream` — clears them; emits `fatal: branch '<n>' has no upstream information` + exit 128 when absent - `--track` (create-side) — writes upstream config after ref creation - `--edit-description` — spawns editor, writes branch.<n>.description Plus three on-disk-state guard rows (chained gix → gix invocations) so bytes-mode parity tests can no longer mask missing-side-effect regressions in branch-config writers. ## Foundations introduced - gix::Repository::{set_branch_upstream, unset_branch_upstream, set_branch_description} with $GIT_DIR/config persistence via gix_config::File::write_to_filter on Source::Local sections (matches gix/src/clone/fetch/util.rs::setup_branch_config). - Branch::DESCRIPTION key constant + SnapshotMut::clear_subsection_value helper. - gitoxide-core::shared::columns::write_columns — column-major packer (reusable for status --column). - gitoxide-core::shared::editor::edit_file — $GIT_EDITOR/core.editor/$VISUAL/$EDITOR/vi resolver + shell-spawn (reusable for commit -e, tag --edit, merge --edit). ## Upstream syncs folded in - merge: pull upstream gix-main into gix-brit (security hardening + fuzz expansion) - merge: pull GitoxideLabs#2508 — allow checkouts into non-empty directories - merge: pull GitoxideLabs#2375 — filters and partial cloning: initial support - merge: pull GitoxideLabs#2503 — IOUC (UNTR extension) dirwalk speedup - merge: pull GitoxideLabs#2457 — gix-blame: Incremental - merge: pull GitoxideLabs#2465 — barebones server-side upload-pack ## Adjustments - steward agent: surface idiomatic Rust as an explicit design priority, reframing vendor/git as the *what* (flag surface, exit codes, output bytes) while gitoxide idioms anchor *how*. - Post-merge cleanup: dropped duplicate clone --filter stub (superseded by GitoxideLabs#2375) and pr-2457's orphaned log helpers (our log_all already handles paths). Plan: etc/plan/foundations-sprint-branch-render-and-config.md Pre-squash backup tag: pre-squash-foundations Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f3dcb4b commit ddd6db7

546 files changed

Lines changed: 14446 additions & 1047 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/agents/gix-steward.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ color: red
88

99
You are the Steward for the **gitoxide** workspace — the vision-holding, evidence-demanding check on completion claims, design tie-breaks, deferral decisions, and strategic direction. You are most often engaged during the rust-wiggum iterative parity loop, but the same four moments apply to any significant gitoxide development work: a migration being called finished, a design that needs arbitration, a gap being proposed for deferral, or a trajectory the implementer suspects has gone off-course. You do not design. You do not translate. You do not write code. Your job is **judgment under evidence** at exactly four invocation moments.
1010

11-
Your north star: **gix is git, written correctly in Rust.** Every "done" must actually be done. Every deferral must be genuine, not convenient. Every design choice must be defensible against `vendor/git/` as the reference.
11+
Your north star: **gix is git, written correctly and idiomatically in Rust** — transliterating C into Rust defeats the point of the project. Every "done" must actually be done. Every deferral must be genuine highly-performant idiomatic rust, not convenient. `vendor/git/` is the reference for *what* gix does (flag surface, exit codes, output bytes, error semantics); gitoxide idioms and Rust's invariant-expressing strengths (type-state, sum types, byte-first paths, parametric hashing, `?`-propagation over `goto cleanup`) are the reference for *how*. A Rust-native shape that preserves the observable behavior beats a C-mapped shape by default.
1212

1313
You are adversarial by design. The implementer (most often the gix-architect) is under iteration pressure and will, occasionally, convince itself that a thing is closed when it isn't. Your verdicts are the check on that pressure. You do not produce prose reviews. You produce **grep-able structured verdicts** with specific evidence.
1414

@@ -79,8 +79,8 @@ CROSS-CUTTING-NOTE: <one-sentence pattern w/ file:line, or "(none)">
7979

8080
Architect presents two defensible designs and asks you to choose. Your evidence:
8181

82-
- `vendor/git/` reference — how does the C do it? Does C structure map more cleanly to one of the two proposed shapes?
83-
- `DEVELOPMENT.md` / `.github/copilot-instructions.md` — which shape matches existing gitoxide idioms?
82+
- `vendor/git/` reference — how does the C do it? C anchors the behavior; if a proposed shape needs C structure to be readable as parity work, that's a signal — but a Rust-native shape that preserves C's observable behavior wins over a C-mapped shape by default. We are rewriting git, not transcribing it.
83+
- `DEVELOPMENT.md` / `.github/copilot-instructions.md` — which shape matches existing gitoxide idioms? Where Rust expresses an invariant C can only enforce at runtime (type-state for state machines, sum types for tagged unions, `BString` over implicit-encoding `char *`), that is a tiebreaker, not a tax.
8484
- `crate-status.md` and existing patterns in sibling `gix-*` crates — what precedent exists?
8585
- Reversibility — which design is easier to refactor later if we guessed wrong?
8686

.config/nextest.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1+
[profile.default]
2+
# it doesn't scale for me on macOS at least :/
3+
test-threads = 6
4+
fail-fast = false
5+
16
[profile.with-xml.junit]
27
path = "junit.xml"

Cargo.lock

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ members = [
269269
"gix-transport",
270270
"gix-credentials",
271271
"gix-protocol",
272+
"gix-serve",
272273
"gix-pack",
273274
"gix-odb",
274275
"gix-tempfile",

crate-status.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ A git directory walk.
843843
* [x] pathspec based filtering
844844
* [ ] multi-threaded initialization of icase hash table is always used to accelerate index lookups, even if ignoreCase = false for performance
845845
* [ ] special handling of submodules (for now, submodules or nested repositories are detected, but they can't be walked into naturally)
846-
* [ ] accelerated walk with `untracked`-cache (as provided by `UNTR` extension of `gix_index::File`)
846+
* [x] accelerated walk with `untracked`-cache (as provided by `UNTR` extension of `gix_index::File`)
847847

848848
### gix-index
849849

docs/parity/SHORTCOMINGS.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,6 @@ Two row classes:
77
- **deferred**`shortcoming "<reason>"`: row closed as a legitimate deferral; reason describes the gap.
88
- **compat**`compat_effect "<reason>"`: row green at effect mode (exit-code parity); byte-output parity is a known follow-up.
99

10-
## branch
11-
12-
| Class | Section | Reason | Source |
13-
|---|---|---|---|
14-
| compat | `gix branch --verbose` | branch -v column-aligned sha+subject rendering deferred | [branch.sh:175](../../tests/journey/parity/branch.sh#L175) |
15-
| compat | `gix branch --verbose` | branch -vv upstream tracking rendering deferred | [branch.sh:178](../../tests/journey/parity/branch.sh#L178) |
16-
| compat | `gix branch --abbrev` | branch -v --abbrev=<n> bytes parity follows -v renderer | [branch.sh:210](../../tests/journey/parity/branch.sh#L210) |
17-
| compat | `gix branch --abbrev` | branch -v --no-abbrev bytes parity follows -v renderer | [branch.sh:213](../../tests/journey/parity/branch.sh#L213) |
18-
| compat | `gix branch --column` | branch --column=always packing deferred | [branch.sh:309](../../tests/journey/parity/branch.sh#L309) |
19-
2010
## clone
2111

2212
| Class | Section | Reason | Source |

docs/parity/commands.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Deferred flag-level rows and compat-only rows live in [SHORTCOMINGS.md](SHORTCOM
3333
| checkout || absent |||
3434
| switch || absent |||
3535
| restore || absent |||
36-
| branch | gix branch | present | [branch.sh](../../tests/journey/parity/branch.sh) | 37 green `it`-bearing sections covering the full git-branch flag surface from vendor/git/Documentation/git-branch.adoc OPTIONS: meta (--help, --bogus-flag, outside-repo), list mode (bare, -l/--list, -r/--remotes, -a/--all, --show-current, -v/-vv, -q/--quiet, --abbrev/--no-abbrev, --contains, --no-contains, --merged, --no-merged, --points-at, --format, --sort, --column/--no-column, --color/--no-color, --omit-empty, -i/--ignore-case), create mode (`<name>`, `<name> <start-point>`, invalid-name, existing-name, -f/--force, -t/--track + --no-track, --recurse-submodules gate-error, --create-reflog), upstream (-u/--set-upstream-to, --unset-upstream, --edit-description), move (-m/-M), copy (-c/-C with worktree-checkout guard), delete (-d/-D, -r -d). Real implementations: flag-bearing top-level Platform mirroring git's cmdmode (replaces the prior nested-Subcommand shape), pattern filter via `gix::glob::wildmatch`, ancestry filters (--contains/--no-contains via `commit_contains`, --merged/--no-merged via inclusive `ancestors_of` set), --points-at via direct ObjectId equality, minimal for-each-ref atom interpreter for --format (refname / refname:short / refname:strip=N / refname:lstrip=N + %XX hex escapes), --sort=refname / -refname (last-key wins primary), refs/remotes/ prefix when --all pairs with locals, three RefEdit-based mutating subroutines (`create` with PreviousValue::MustNotExist gate + already-exists pre-check, `rename` two-RefEdit transaction, `copy` single Update with worktree-checkout guard, `delete` with RefLog::AndReference). Compat-effect rows tracked in SHORTCOMINGS.md: --verbose -v/-vv rendering (column-aligned sha+subject+upstream tracking), --abbrev cooperation with -v, --column=always packing, --track upstream-config write, -u/--set-upstream-to= config write, --unset-upstream config clear, --edit-description EDITOR-spawn. Deprecated `--set-upstream` (adoc:278) intentionally omitted — git itself documents it as "no longer supported". sha256 blocker: gix-config rejects extensions.objectFormat=sha256 (gix/src/config/tree/sections/extensions.rs), so every repo-opening row is sha1-only. Dual rows: --help, (outside a repository). |
36+
| branch | gix branch | present | [branch.sh](../../tests/journey/parity/branch.sh) | 37 green `it`-bearing sections covering the full git-branch flag surface from vendor/git/Documentation/git-branch.adoc OPTIONS: meta (--help, --bogus-flag, outside-repo), list mode (bare, -l/--list, -r/--remotes, -a/--all, --show-current, -v/-vv, -q/--quiet, --abbrev/--no-abbrev, --contains, --no-contains, --merged, --no-merged, --points-at, --format, --sort, --column/--no-column, --color/--no-color, --omit-empty, -i/--ignore-case), create mode (`<name>`, `<name> <start-point>`, invalid-name, existing-name, -f/--force, -t/--track + --no-track, --recurse-submodules gate-error, --create-reflog), upstream (-u/--set-upstream-to, --unset-upstream, --edit-description), move (-m/-M), copy (-c/-C with worktree-checkout guard), delete (-d/-D, -r -d). Real implementations: flag-bearing top-level Platform mirroring git's cmdmode (replaces the prior nested-Subcommand shape), pattern filter via `gix::glob::wildmatch`, ancestry filters (--contains/--no-contains via `commit_contains`, --merged/--no-merged via inclusive `ancestors_of` set), --points-at via direct ObjectId equality, minimal for-each-ref atom interpreter for --format (refname / refname:short / refname:strip=N / refname:lstrip=N + %XX hex escapes), --sort=refname / -refname (last-key wins primary), refs/remotes/ prefix when --all pairs with locals, three RefEdit-based mutating subroutines (`create` with PreviousValue::MustNotExist gate + already-exists pre-check, `rename` two-RefEdit transaction, `copy` single Update with worktree-checkout guard, `delete` with RefLog::AndReference). All flag-level compat rows in the renderer cluster (-v/-vv, --abbrev, --column=always) and the upstream-config + EDITOR-spawn cluster (-u/--set-upstream-to, --unset-upstream, --track, --edit-description) closed in the foundations sprint (etc/plan/foundations-sprint-branch-render-and-config.md): bytes-parity for all 9 rows. Foundations introduced: gix-config branch-section writers (Repository::set_branch_upstream / unset_branch_upstream / set_branch_description), shared::columns column-major packer, shared::editor::edit_file helper. Deprecated `--set-upstream` (adoc:278) intentionally omitted — git itself documents it as "no longer supported". sha256 blocker: gix-config rejects extensions.objectFormat=sha256 (gix/src/config/tree/sections/extensions.rs), so every repo-opening row is sha1-only. Dual rows: --help, (outside a repository). |
3737
| tag | gix tag | present | [tag.sh](../../tests/journey/parity/tag.sh) | Full flag surface wired across list / delete / verify / create modes. List: -l/--list, <pattern>, -n, --sort (default order bytes; key interpreter compat-deferred), --format with %(refname[:short,:strip=N,:lstrip=N]), %(objectname), %(objecttype) + %XX hex escapes, --omit-empty, --column/--no-column (packing compat-deferred), --contains/--no-contains, --merged/--no-merged, --points-at, -i/--ignore-case, --color. Delete: -d/--delete (single, multi, mix-existing+nonexistent). Verify: -v on annotated-unsigned/lightweight/nonexistent (GPG backend still unimplemented; tests assert the three non-signature error paths). Create: lightweight `<name> [<commit>]`, -f/--force replace, invalid-name rejection (bytes), annotated `-a`/`-m` (multi-paragraph), `-F <file>` (stdin deferred pending expect_parity_reset stdin plumbing), --cleanup {verbatim/whitespace/strip/bogus}, -e/--edit (no-op under EDITOR=true), --trailer (accumulating), --create-reflog/--no-create-reflog (flag accepted; reflog write deferred at gix-ref level), -s/-u/--local-user (emit git-compat no-backend error + exit 128), --no-sign. `tag.gpgSign` config-read-then-sign is out of scope. sha256 blocker: gix-config rejects extensions.objectFormat=sha256 (gix/src/config/tree/sections/extensions.rs), so every repo-opening row is sha1-only. Dual rows: --help, (outside a repository). |
3838
| stash || absent |||
3939
| cherry-pick || absent |||

etc/fuzz-corpus-builder.sh

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Shared helper for libFuzzer corpus builder scripts.
2+
#
3+
# Usage from a fuzz target-specific builder:
4+
#
5+
# root="$(readlink -f -- "$1")"
6+
# output_corpus="$2"
7+
# source "$root/etc/fuzz-corpus-builder.sh"
8+
# build_fuzz_corpus "$root" "$output_corpus" "gix-pack" "index_file"
9+
#
10+
# The target-specific script is invoked by Google OSS Fuzz and
11+
# their build script at https://github.com/google/oss-fuzz/blob/master/projects/gitoxide/build.sh
12+
# with the repository root and the output zip path.
13+
# This helper adds all files from:
14+
#
15+
# <crate>/fuzz/corpus/<target>/*
16+
#
17+
# If present, it also adds files from:
18+
#
19+
# <crate>/fuzz/artifacts/<target>/*
20+
#
21+
# Corpus files and artifact files are zipped with `-j`, matching the flat corpus
22+
# archive layout it expects. Empty input groups are skipped so artifact-only
23+
# targets can still produce a corpus archive. Artifacts are added in a separate
24+
# zip invocation so a duplicate basename updates the archive instead of making
25+
# zip fail with "cannot repeat names in zip file".
26+
build_fuzz_corpus() {
27+
local root="$1"
28+
local output_corpus="$2"
29+
local crate="$3"
30+
local target="$4"
31+
local fuzz_dir
32+
local corpus_dir
33+
local artifacts_dir
34+
35+
fuzz_dir="$(readlink -f -- "$root/$crate/fuzz")"
36+
corpus_dir="$fuzz_dir/corpus/$target"
37+
artifacts_dir="$fuzz_dir/artifacts/$target"
38+
39+
echo "$root"
40+
echo "$corpus_dir"
41+
echo "$artifacts_dir"
42+
43+
shopt -s nullglob
44+
45+
local corpus_files=("$corpus_dir"/*)
46+
if ((${#corpus_files[@]})); then
47+
zip -j "$output_corpus" "${corpus_files[@]}"
48+
fi
49+
50+
if [[ -d "$artifacts_dir" ]]; then
51+
local artifact_files=("$artifacts_dir"/*)
52+
if ((${#artifact_files[@]})); then
53+
# A second zip invocation updates duplicate basenames instead of aborting.
54+
zip -j "$output_corpus" "${artifact_files[@]}"
55+
fi
56+
fi
57+
}

0 commit comments

Comments
 (0)