Repository navigation
family.py: bump to the commit a member is already pinned at says nothing to do and exits 1, so a re-run of a bump that landed is no longer REFUSED a git or gh command failed, exit 2, around git's nothing to commit (#210) - #219
Conversation
…othing to do` and exits 1, so a re-run of a bump that landed is no longer `REFUSED a git or gh command failed`, exit 2, around git's `nothing to commit` (#210) `cmd_bump` checked the member out at `--to`, rewrote the `members:` block with the row it already had and handed `git commit -- <member path> family.yaml` nothing. git's own `nothing to commit` (exit 1) reached `main`'s generic handler as `REFUSED a git or gh command failed`, exit 2 - where this file's docstring documents exit 1 for nothing and `bump-leg.py` answers the same re-run with `nothing to do`, exit 1. A lane or script re-running a landed bump read a refusal to act on. After that rewrite and before `_commit_one`, `cmd_bump` now asks git whether the commit has anything to record: `_nothing_to_record` runs `git diff --quiet --ignore-submodules=dirty HEAD -- <member path> family.yaml`, reads its exit 1 as "there is a difference" and lets any other failure propagate. - When there is nothing to record, `bump` prints its usual `old -> new tree` line, then `bump-leg.py`'s sentence - `nothing to do: <org>/<Project> is already pinned at <sha12> and every fact agrees.` - on stdout, says nothing on stderr and exits 1. It writes no commit and stages nothing. - `family.yaml` goes back byte for byte as it was found. The rewrite recorded nothing, and an uncommitted hand edit it happened to overwrite is not this run's to discard (on main the same run threw the edit away before it refused). - git is asked, and `was == --to` is not read. A row that names `--to` is not by itself a bump with nothing in it, and three cases keep committing: a gitlink that moved alone and a digest that does not recompute (`validate-family.py` names `bump --member <Project> --to <commit>` as the repair for both), and a bump an earlier run wrote and staged but never committed. - `--ignore-submodules=dirty` is what `git commit` itself ignores: files the member holds untracked or modified are not recorded by this commit, its HEAD is, so they do not make a bump to the pinned commit look like one with something in it. - The module docstring's EXIT CODES says which bump is `nothing`. Measured with a throwaway family in local bare remotes, main a5b09c3 versus this head: - `bump --member IRRS --to <the pinned commit>` (the issue's repro): exit 2 and `REFUSED ... git commit -q -F - -- members/IRRS family.yaml ... nothing to commit` on stderr, then exit 1 and the two stdout lines above with an empty stderr; HEAD, gitlink and `git status --porcelain` identical before and after in both. - The same bump run again after it landed, with `--trailer`: exit 2, then exit 1; `family.yaml` byte-identical before and after in both. - An uncommitted edit to the member's `id:` row, then a bump to the pinned commit: exit 2 and the edit gone from the working copy, then exit 1 and the edit still there (` M family.yaml` before and after). - An ordinary bump to a new commit, and a bump back to the commit a row already names after the gitlink moved alone: output and exit code diffed against main after normalizing the 12-hex shas and the scratch paths, byte-identical (exit 0, one commit, `validate-family.py` green after). Unchanged: every flag and `--help` text (`docs/cli.md` is not touched), the commit message and trailers of a bump that commits, `add` and `remove`, and the README and AGENTS.md, which do not describe `bump`'s exit codes. Left out: `family.py bump` has no `--dry-run`, though the docstring promises one prints and exits 0 and `bump-leg.py` has one; a nothing-to-do answer still costs the member fetch and checkout it always did. Lane: openreposhape-3 (openRepoShape-3) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-3, 2026-10-11T18:56Z) @codex review |
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 1 day and 4 hours by commenting |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Reviewer's Guide
Sequence diagram for family bump no-op detectionsequenceDiagram
participant User
participant Family as family.py
participant Git
participant Files as Member and family.yaml
User->>Family: cmd_bump
Family->>Files: write_members
Family->>Git: git diff --quiet --ignore-submodules=dirty HEAD -- <member path> family.yaml
alt No differences
Git-->>Family: exit 0
Family->>Files: write_bytes(original)
Family-->>User: print nothing to do
Family-->>User: exit 1
else Differences exist
Git-->>Family: exit 1
Family->>Git: _commit_one
Git-->>Family: commit changes
Family-->>User: exit 0
end
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
…hether there is anything to record, so a stray staged gitlink and a bump to the pinned commit say `nothing to do` and no longer end in `REFUSED ... nothing to commit`, exit 2 (#210) Fix round 1 on #219, from the independent review. `validate-family.py` reads the INDEX first. A member somebody moved and `git add`ed without committing is reported as `member-gitlink-mismatch`, and the validator names `family.py bump --member <Project> --to <commit>` as the repair. `bump` checks the member out at `--to`, which for the pinned commit is HEAD's gitlink, but the index still held the stray one, so `git diff --quiet HEAD` found a difference, `_commit_one`'s own `git add` re-staged the pinned gitlink and `git commit` found nothing: the refusal this branch closes, exit 2, reached through the validator's remedy. `cmd_bump` now runs `git add -- <member path>` immediately before `_nothing_to_record`, as `_commit_one` does first anyway. The checkout already moved the member to `--to`, so this stages that gitlink and nothing else: on a submodule path `git add` stages the gitlink and never the member's files, and `family.yaml`'s own index state is left as found. The commit path is unchanged, because `_commit_one` stages the same path again. - A stray gitlink staged in the index and a bump to the pinned commit: exit 1, the `nothing to do` line, `git status --porcelain` empty, the index back at the pinned gitlink, `family.yaml` byte-identical and `validate-family.py` green. On the previous head the same run was exit 2 with `REFUSED ... git commit -q -F - -- members/IRRS family.yaml ... nothing to commit`, as on main. - A new test, `test_bump_to_the_pinned_commit_unstages_a_stray_gitlink_and_says_nothing`, stages a stray gitlink, checks the validator reports `member-gitlink-mismatch`, bumps to the pinned commit and asserts the line above. It fails on 66ace9b with the refusal and passes here. Unchanged: every other bump path (the ordinary bump, a gitlink that moved alone, a digest that does not recompute and a bump an earlier run staged and never committed all still commit), every flag and `--help` text. Lane: openreposhape-3 (openRepoShape-3) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-3, 2026-10-11T19:31Z) fix round 1 (independent review, codex is out of quota): |
|
(lane openRepoShape-2, 2026-10-11T19:34:42Z) This lane reviewed PR #219 at 66ace9b read-only, in its own detached worktrees against local bare repositories. Nothing was authored and no claim is held. The branch has since moved to c7e8f5d (19:21Z), which this review did not cover. MERGEABLE WITH NITS, no sequencing constraint. The issue's repro on main gives exit 2, Findings:
Tests:
Merges, ls-remote 19:2xZ:
Report: |
|
…, #252 via #257, #195 via #226, #210 via #219, #211 via #220, #248 via #265, #212 via #223) into fix/adopt-record-submodule-placement Brings in 06b5204 (#214), 60d08a1 (#217), 2727b08 (#209), 4b2add8 (#252), aaa40eb (#195), d89fffa (#210), eb6df75 (#211), c3f4987 (#248) and 5751460 (#212) on top of 39ac59c. No rebase. 5751460 landed while the merge of c3f4987 was being checked, and it changes adopt-project.py, scripts/shape_materialize.py and tests/test_adopt_submodules.py, as this branch does, so this one merge takes it too. No conflicts. Both sides change adopt-project.py, scripts/repo_shape.py, scripts/shape_materialize.py, shape-doctor.py, tests/test_adopt_submodules.py and tests/test_shape_doctor.py. Every top-level definition only one side changed is that side's text in the merge, byte for byte. The one thing both changed is shape_materialize.py's imports, and both are kept: main's `codecs` and this branch's `path_spelling_fault` and `PATH_*` codes. Checked: - adopt-project.py: main calls `escape_what_stdout_cannot_encode()` first in `main` (#214). It makes a `.gitmodules` git's submodule reader cannot read a finding where the tip holds no submodule and the history does, or where `ignore`, `update` or `branch` has no value (#195). And every path a finding names is now `_spelled` (#212). This branch's `submodule_record`, `_resolution_text` and the record's preamble are unchanged by the merge, and so are `_answered_legs`, `_kept_in_a_leg`, `_commit_the_split`, `_refuse_what_check_finds`, `cmd_execute` and `Plan`. The record and #195's finding read the kept submodules the same way, `_kept_in_a_leg(_answered_legs(...))` over the tip's gitlinks. `execute` refuses #195's finding in `_refuse_what_check_finds`, before any leg exists and before `_commit_the_split` writes the record. So a source #195 refuses gets no record, and so does a source whose tip holds no submodule. - scripts/repo_shape.py: main refuses an explicit key in a flow mapping and reads an empty flow-mapping value as null (#252). A block scalar's record is now a (key, body) pair, and `_BLOCK_SENTINEL` is gone (#248); nothing on this branch names it. The record is a block sequence of double-quoted scalars, and it reads back through the merged reader with 0 mismatches (`submodule_record`, then `parse_yaml`, path and resolution): - 4,525 names, plus 35 more; - 240 aimed at #252 and #248 (`? a`, `{a: }`, `|`, `a: |`, a NUL, the old sentinel). - scripts/shape_materialize.py: main adds `escape_what_stdout_cannot_encode`, `run()` reading UTF-8 (#217), and `first_and_more(paths, spell)` (#212). This branch's `LEG_SEGMENT_REASONS` and `_spelling_reason` are untouched. - shape-doctor.py: main adds the `manifest kinds` finding for a `family.yaml` beside `project.yaml` (#211), and the stdout escaping. This branch's placement row and `recorded_submodules` are untouched. - tests: #212 respells the finding text that the earlier tests in test_adopt_submodules.py assert. None of this branch's tests quotes a finding. - No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py, scripts/shape_materialize.py, shape-doctor.py, validate-manifest.py or update-shape.py. Each merged module's names are the base's, plus what either side added, minus what either side removed. No side added a name the other side also added, in the tests either. - AGENTS.md is untouched by main: 472 lines, under the cap of 472. - The branch's diff against main is still #189 alone: 12 files, +1670 -156, file by file the same as against 39ac59c. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, #209, #252, #195, #210, #211, #248, #212, #251 and #206 merge with one hunk, the materializer import list, kept on both sides, and `_entry_findings` still hands `_leg_findings` the tree and ends with the root-symlink findings origin/main moved 39ac59c -> 60588d5: 06b5204 (#214 via #246), 60d08a1 (#217 via #230), 2727b08 (#209 via #218), 4b2add8 (#252 via #257), aaa40eb (#195 via #226), d89fffa (#210 via #219), eb6df75 (#211 via #220), c3f4987 (#248 via #265), 5751460 (#212 via #223), 23e16ae (#251 via #271) and 60588d5 (#206; its subject names no pull request). Conflict, resolved by keeping both sides: - adopt-project.py, the `shape_materialize` import list. This branch adds BOOTSTRAP, SYMLINK_HOPS and template_files; #214 adds escape_what_stdout_cannot_encode. The resolution is the union. Auto-merged: scripts/shape_materialize.py, where #214's escaping, #217's UTF-8 `run()` and #212's `first_and_more(paths, spell=None)` sit apart from this branch's #183 section, and adopt-project.py's other hunks. #195 adds `import os` at the line where this branch added it, and the merge keeps it once. Checked on the merged tree: - `_entry_findings` still calls `_leg_findings(plan, tree)` and ends with `_root_symlink_findings(plan, source, tree)`. `main` still calls `escape_what_stdout_cannot_encode()` first. - No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py or scripts/shape_materialize.py, and none is loaded but bound nowhere (AST). `first_and_more` gives its new `spell` a default, so this branch's call is unchanged. - Both sides are intact by +/- line sets: each side's change against the base reappears against the other side, apart from the import hunk and the shared `import os`. - #212 says this branch's `plan-root-symlink` names its paths raw, and that whichever pull request lands second should spell them. That was 04792d2. Since d11cf9f every path the finding names goes through `_spelled`. One clash the textual merge does not show is fixed in the next commit: #217's tests/test_git_output_decoding.py refuses a shipped text-mode `subprocess` call that names no codec, and this branch's `_checks_out_symlinks` probe names none. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e-notice It merged with no conflict. Main brings nine commits since 39ac59c: - 06b5204, six tools escape a name their stdout cannot encode (#214 via #246), with tests/test_stdout_encoding.py; - 60d08a1, `shape_materialize.run()` and 26 other git and gh calls read the child's output as UTF-8 (#217 via #230), with tests/test_git_output_decoding.py; - 2727b08, update-shape.py's `gh pr create --base` (#209 via #218), with tests/test_update_shape_pr_base.py; - 4b2add8, scripts/repo_shape.py's flow mappings (#252 via #257), with tests/test_yaml_flow_mappings.py; - aaa40eb, adopt-project.py refuses a `.gitmodules` the history's gitlinks make unreadable (#195 via #226), with tests/test_adopt_history_gitlinks.py; - d89fffa, family.py `bump` to the current pin (#210 via #219); - eb6df75, shape-doctor.py's family.yaml beside a project.yaml (#211 via #220); - c3f4987, scripts/repo_shape.py's block-scalar records (#248 via #265), with tests/test_yaml_block_scalar_keys.py; - 5751460, adopt-project.py spells every path a FINDING or a refusal names (#212 via #223), with tests/test_adopt_finding_spelling.py. Three files changed on both sides: adopt-project.py, tests/test_adopt_ignored_shape_files.py and tests/test_adopt_verify.py. In each, `git diff -U0` of this merge against 5a8b649 equals main's diff against 39ac59c, and this merge against 5751460 equals the branch's diff against 39ac59c, apart from the hunk headers. Neither side's lines are changed. How main's changes meet this branch's failure handling: - Every definition the late failures go through is byte-identical to 5a8b649's: LATE_FAILURES, the notice (`_nothing_rolled_back`), `_failed_loudly`, `_report_late`, the three builders' handlers, `_verify` and `cmd_execute` among them. Three definitions differ, each by main's hunk alone. - `main()` gains `escape_what_stdout_cannot_encode()` as its first statement (06b5204). The branch writes every REFUSED block and notice to stderr, which Python opens with `backslashreplace` whatever the locale. - `run()` names its codec (60d08a1). Output that is not UTF-8 now reaches the branch's handlers as `CommandFailed`, not as `UnicodeDecodeError`. LATE_FAILURES keeps `UnicodeError`, because `git_out` in scripts/repo_shape.py still decodes strictly. - `Source.registered_submodules` reads the history (aaa40eb). Its one caller in `execute` is `_refuse_what_check_finds`, before `_create_leg_remotes`, so the new refusal comes before any leg repository exists and needs no notice. - 5751460 spells the paths of the verification's findings. The branch's footer and notice follow those findings unchanged. No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py or scripts/shape_materialize.py, and a `symtable` check finds no name they read that nothing binds. On the merged tree, ten test files give 439 passed and 1 skipped (Windows' own stdout default): the two shared ones, main's four new ones that read adopt-project.py (finding_spelling, history_gitlinks, stdout_encoding, git_output_decoding), and test_adopt_late_failure, test_adopt_filter_repo_failure, test_adopt_execute_refusals and test_adopt_plan_escapes. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…`git --literal-pathspecs add -f`, so an operator whose own excludes name `members`, `members/`, `*` or the member gets one commit instead of exit 2, `bump` moves a pin under them too, and each refusal `--force` turns off is made before the member is read (#242) (#308) `add` ran `git submodule add` with no `--force` and `_commit_one` a plain `git add`, and both read a global `core.excludesFile`. `members/` is a fixed path, so an operator whose excludes named `members`, `members/`, `*` or the member's name had every add of every family refused: git's dry run refused the first four before the clone, and a directory-only rule on the member's name (`Atlas/`) refused it only at git's last `git add`, after the clone. `bump` died the same way under `members` or `*`: it stages the gitlink twice, at the `git add` #219 put before its nothing-to-record check and in `_commit_one`, and both read the excludes. This is #174 (adopt, fixed by #200) and #229 (scaffold) on the family side. Every reader is forced now. The mount passes `--force`, and every stage `add`, `bump` and `remove` make is `shape_materialize.force_stage` (#233), `git --literal-pathspecs add -f -- <paths>`: `-f` reaches only the named paths, each taken literally, so no ignored neighbour a glob character would match is staged with them (#202). One definition: `_commit_one` and `bump`'s re-stage both call it. `--force` turns off more than the ignore check, so each other thing it changes is refused before the member is read, the holder left exactly as it was, and a comment at the mount names each guard: - a `.git/modules/<path>` left by a removed member, which `--force` reactivates: `member-git-dir-cached` (#236), at the store `git rev-parse --git-path` names, a linked worktree's included; - an index entry at the path or under it with nothing on disk, which `--force` accepts and replaces with the member's gitlink: `member-path-taken` now asks the index too, and names the `git rm -r --cached` that clears it; on disk it asks `lexists`, so a dangling link is refused as well; - git's own dry run, which `--force` skips (a symlinked `members` had the member cloned through the link, outside the holder; a `members` that is a submodule had it cloned into that submodule's tree): `member-path-unaddable` makes the same dry run first, with `-f`, so an ignore rule passes it and nothing else does; the refusal quotes git's line, and its remediation presumes neither a link nor a submodule (a held `index.lock` fails it too); - a name another submodule already has, which git 2.51 and later rename the mount past and git 2.43 answers by rewriting the other section: `member-name-taken`, reading `.gitmodules` where git reads it (the working tree, else the index's copy, else HEAD's). The PRIVATE/access sentence is printed only for the probe's failed clone of the member, which no ignore rule reaches. tests/test_family_add_excludes.py: under `members`, `members/`, `*`, `IRQS`, `IRQS/` and none, `add` is ONE commit of `.gitmodules`, `family.yaml` and a 160000 gitlink at the member's tip, read with `git ls-tree`, that validates under the same excludes and clones recursively for an operator with none; `bump` under `members` moves the gitlink in one commit and `remove` is the control; each guard above is refused before the member is read with the holder unchanged. Mutations: without `--force` at the mount every rule's row fails; without `-f` in `_commit_one` the `members`, `members/` and `*` rows and the bump test fail; with `bump`'s re-stage a plain `git add` the bump test fails; dropping the index check, the dry run, the name check, its index/HEAD fallback or `lexists` fails exactly that guard's tests. Closes #242 Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-3 (openRepoShape-3)
Claim: #210 (comment)
What
family.py bumpto the commit a member is already pinned at no longer ends in git'snothing to commitsurfaced asREFUSED a git or gh command failed, exit 2.cmd_bumpnow asks git, after itsmembers:rewrite and before_commit_one, whether the commit would record anything:_nothing_to_recordrunsgit diff --quiet --ignore-submodules=dirty HEAD -- <member path> family.yaml, reads its exit 1 as "there is a difference" and lets any other failure propagate.bumpprints its usualold -> new treeline, thenbump-leg.py's sentence on stdout,nothing to do: <org>/<Project> is already pinned at <sha12> and every fact agrees., says nothing on stderr and exits 1, which is what the module docstring saysnothingis. No commit, nothing staged.family.yamlgoes back byte for byte as it was found. An uncommitted hand edit the rewrite happened to overwrite is not this run's to discard (on main the same run threw the edit away before refusing).was == --tois not read, so three cases keep committing: a gitlink that moved alone and a digest that does not recompute (validate-family.pynamesbump --member <Project> --to <commit>as the repair for both), and a bump an earlier run wrote and staged but never committed. Each has a test.git add -- <member path>) before git is asked, as_commit_onedoes first anyway, so a stray gitlink somebody staged and never committed is not a difference either.validate-family.pyreads the index first, reportsmember-gitlink-mismatchand namesbumpas the repair, and without the re-stage that remedy, run with the pinned commit, still ended in the refusal this PR closes (fix round 1, found by the independent review). It stages the gitlink and nothing else:family.yaml's index state is left as found.--ignore-submodules=dirtyis whatgit commititself ignores: files the member holds untracked or modified do not make a bump to the pinned commit look like one with something in it.nothing.InkRouter/IRRS) asbump-leg.pydoes, not the bare<Project>the issue sketched, so a lane reads one wording from either tool.No README, AGENTS.md or handbook change: neither describes
bump's exit codes (orbump-leg.py'snothing to do); the exit-code contract lives in the module docstring only, which this updates.--helptext is unchanged (docs/cli.mdis not touched), README stays at its 1416-line cap and AGENTS.md at 464.Why
Measured with a throwaway family (two scaffolded members, local bare remotes,
bump/irrsbranch), on maina5b09c3and on this head, driver and raw output kept by the lane. The staged-gitlink row was measured on main54aa69aand on headc7e8f5d;a5b09c3..54aa69atouches onlyadopt-project.pyand two adopt tests, so it cannot change any of these rows.a5b09c3bump --member IRRS --to <the pinned commit>(the issue's repro)REFUSED a git or gh command failed ... git commit -q -F - -- members/IRRS family.yaml ... exit 1 ... nothing to commit, working tree clean; stdout emptyIRRS <sha12> -> <sha12> tree <sha12>…andnothing to do: InkRouter/IRRS is already pinned at <sha12> and every fact agrees.; stderr empty; HEAD, gitlink andgit status --porcelainidentical before and after--trailerREFUSED;family.yamlbyte-identicalnothing to do;family.yamlbyte-identicalid:row, then a bump to the pinned commitgit statusclean)M family.yamlbefore and after)git -C members/IRRS commit --allow-empty, thengit add members/IRRS, not committed;validate-family.pyrc 1,member-gitlink-mismatch), thenbump --to <the pinned commit>, the validator's own remedyREFUSED ... git commit -q -F - -- members/IRRS family.yaml ... nothing to commit; the index ends at the pinned gitlink and the validator at rc 0IRRS <sha12> -> <sha12> tree <sha12>…andnothing to do: InkRouter/IRRS is already pinned at <sha12> and every fact agrees.lines; stderr empty;git status --porcelainempty, index gitlink at the pinned commit, validator rc 0members/IRRSandfamily.yamlvalidate-family.pyrc 1 (member-gitlink-mismatch) before; bump exit 0 and one commit; rc 0 afterTests
Targeted set on head
c7e8f5d40940cc1c648fcb119915234fa0b9c091(two commits on basea5b09c3:66ace9band the fix round):tests/test_family.py tests/test_shape_doctor.py tests/test_repo_hygiene.py tests/test_cli_reference.pygive328 passed in 315.35s (0:05:15), exit 0 (84 + 181 + 50 + 13). On66ace9bthe same set was327 passed in 582.45s (0:09:42). Eight new tests intests/test_family.py(the first is parametrized plain and with--trailer): bump to the pinned commit isnothing to dowith no commit, nothing staged,family.yamlbyte-identical andvalidate-family.pygreen; it ignores what the member holds untracked or modified; it leaves an uncommittedfamily.yamledit alone; it unstages a stray gitlink and says nothing (fix round 1: fails on66ace9bwith theREFUSEDtext, passes here); it finishes a bump an earlier run staged and never committed; it repairs a gitlink that moved alone; it repairs a digest that does not recompute. One existing test gained an assertion that an ordinary bump does not saynothing to do. No new test carries a Windows skip marker (WINDOWS_SKIP), so the Windows job runs all of them; they usegit,run_scriptandgit status --porcelainthe waytests/test_bump_leg.pydoes. The full suite was not run here; CI runs it on three OSes.Composes with
Re-run on head
c7e8f5d:git merge-tree --write-tree <sibling> HEAD, then the whole oftests/test_family.pyin the merged tree.tests/test_family.pymergedfix/family-add-member-case(#206, same file, also touchescmd_bump)9725bd269d47local (6c917d60774cpushed: also CLEAN)origin/fix/decode-git-output-utf8(#217, touches_commit_one)b6f7cfdcfbd6origin/fix/family-add-check-before-mount(lane 4's #203)d25dd4d1a40dfix/plan-report-stdout-encoding(local branch, not pushed, touchesscripts/family.py)300630329549origin/main54aa69a370f3(adopt-project.pyand adopt tests only)No sibling's assertions break this change, and this change breaks none of theirs.
Left out
family.py bumphas no--dry-run, though its docstring says a dry run prints and exits 0 andbump-leg.pyhas one; anothing to doanswer still costs the member fetch and checkoutbumpalways did.Closes #210
🤖 Generated with Claude Code
Summary by Sourcery
Make
family.py bumpdistinguish completed no-op reruns from actual repairs, reporting consistent pins asnothing to dowithout creating commits or discarding user changes.New Features:
family.py bumpat an already consistent pinned commit as a no-op with exit code 1 and a clearnothing to domessage.Bug Fixes:
Enhancements:
Tests: