Skip to content

test_family_bump_fetch_refspecs.py: the comment on WHICH_CLAUSE and the docstring of test_which_clause_leaves_each_refspec_out say the source refs/h takes the one ref git completes it to, which may be a branch or tag, not no branch (#1230) - #1235

Merged
brettheap merged 2 commits into
mainfrom
fix/1230-refs-h-wording
Oct 24, 2026

Conversation

@brettheap

@brettheap brettheap commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Goal

#1230 is a wording NIT left by the re-check of #1157 (#1100, NIT 1). Two passages of tests/test_family_bump_fetch_refspecs.py said that the carried refspec +refs/h:refs/remotes/origin/hy takes "the one ref of that name, which is no branch". They now say what git does with the source refs/h: it takes the one ref that name stands for, which is refs/h itself or a ref git completes it to, such as the branch refs/heads/refs/h or the tag refs/tags/refs/h. A reader of the carried rule is no longer told that the source is never a branch.

Wording only, in one comment and one docstring of that file. No row, no script, no other file. Two commits: the first (644982da1) is the wording, and round 1 (81582301e) answers the independent review rv1235 (#1235 issuecomment-6375269462: READY, no MUST, two SHOULDs, three NITs). See "Round 1" below.

What held on main (e04826db4)

The file's blob on main is 6e752bbca9c9, the same as at #1157's head, and it is still that blob on main 0ed3e57f8. The two passages are at the lines the issue names:

580: #: `carried` where none does (`carried-refs-h-not-a-pattern`, a start of
581: #: `refs/heads/` with no `*`, is no pattern: it takes the one ref of that
582: #: name, which is no branch), and `skipped` for a negative refspec, which
...
690:     of `refs/heads/` that is no pattern and takes only the ref of that name,
691:     which is no branch and may stand beside the branches (the re-check of
692:     #1157, NIT 1), and a branch, a folder of them or a pattern of them,

That is true of an exact refs/h only. A remote can also hold a branch refs/heads/refs/h or a tag refs/tags/refs/h, and git completes the source refs/h to either. Measured on this branch's base, with a local bare remote per row (no network, GIT_CONFIG_GLOBAL=/dev/null, GIT_CONFIG_NOSYSTEM=1), the remote holding main and hy from one empty commit and then the ref of the row, and a fresh mount with that remote as origin running

git fetch origin '+refs/heads/*:refs/remotes/origin/*' '+refs/h:<destination>'

git 2.43.0 and git 2.47.1 gave the same answers:

the remote also has            destination                 result
branch refs/heads/refs/h       refs/remotes/origin/hy      128 Cannot fetch both refs/heads/hy and refs/heads/refs/h to refs/remotes/origin/hy
tag refs/tags/refs/h           refs/remotes/origin/hy      128 Cannot fetch both refs/heads/hy and refs/tags/refs/h to refs/remotes/origin/hy
exact ref refs/h (update-ref)  refs/remotes/origin/hy      128 Cannot fetch both refs/heads/hy and refs/h to refs/remotes/origin/hy
none of these                  refs/remotes/origin/hy      128 couldn't find remote ref refs/h
branch refs/heads/refs/h       refs/remotes/origin/not-a-branch    0
tag refs/tags/refs/h           refs/remotes/origin/not-a-branch    0
exact ref refs/h (update-ref)  refs/remotes/origin/not-a-branch    0

So the source can be a branch or a tag, and what clashes is the destination: the first three rows fail because origin/hy is where the glob puts the branch hy, and the same sources fetch with exit 0 onto a name no branch has. The fourth row is the other documented way a carried refspec fails, a source the remote lacks, and is not this clash.

The review (rv1235) measured two more completions on both gits: refs/refs/h and refs/remotes/refs/h are taken too (exit 0 onto a free name, and 128 Cannot fetch both refs/heads/hy and refs/refs/h and ... refs/remotes/refs/h ... onto origin/hy), so the list of what git completes the name to is open. It also measured a carried branch: +refs/heads/main:refs/remotes/origin/main and +refs/heads/rel/*:refs/remotes/origin/rel/* exit 0, where +refs/pull/7/head:refs/remotes/origin/main exits 128 Cannot fetch both refs/heads/main and refs/pull/7/head. So a carried source clashes onto a branch's name when it is not that branch.

What changed

Both passages say the same thing, in this file only. The comment above WHICH_CLAUSE, lines 580-586 (reflowed to the end of its sentence):

#: `carried` where none does (`carried-refs-h-not-a-pattern`, a start of
#: `refs/heads/` with no `*`, is no pattern: it takes the one ref that name
#: stands for, `refs/h` itself or a ref git completes it to, such as the
#: branch `refs/heads/refs/h` or the tag `refs/tags/refs/h`, and not the
#: branches as a set), and `skipped` for a negative refspec, which no
#: clause is asked of. `bump-leg.py`'s fetch of a leg is asked the same
#: rows (`test_bump_leg_fetch_refspecs.py`).

The docstring of test_which_clause_leaves_each_refspec_out, lines 692-697:

    of `refs/heads/` that is no pattern and takes only the one ref that name
    stands for (`refs/h` itself or a ref git completes it to, such as the
    branch `refs/heads/refs/h` or the tag `refs/tags/refs/h`), whose fetch
    clashes, as a pull request's ref does, where its destination is a
    branch's name (the re-check of #1157, NIT 1), and a branch, a folder of
    them or a pattern of them, each under the name `BRANCHES` gives it."""

Rows failing on main: none. No row is added or changed, so there is nothing for main to fail. Mutants: none, because no behaviour changed; carried-refs-h-not-a-pattern is the same row, and the syntax tree below shows nothing outside the docstring moved.

Round 1, after the review rv1235

Runs

All through the lane's runner run9.sh (stub gh first on PATH, GIT_SSH_COMMAND=false, GIT_CONFIG_GLOBAL=/dev/null), on the committed head 81582301e with the tree clean (dirty=0), Python 3.12.3. The git on that runner's PATH is /usr/bin/git, git 2.43.0 (measured with command -v git and git --version under the runner's PATH). The review's own runs had git 2.47.1 first on PATH and gave the same counts.

  • Syntax tree (python3 -I, ast.dump): the file at this head and on main 0ed3e57f8, each with the docstring of test_which_clause_leaves_each_refspec_out removed, are equal (75050 characters of dump both). A control that renames WHICH_CLAUSE reports them different (75050 against 75052).
  • -k carried-refs-h-not-a-pattern over tests/test_family_bump_fetch_refspecs.py and tests/test_bump_leg_fetch_refspecs.py: 2 passed, 123 deselected, as at 368b87e9b.
  • tests/test_repo_hygiene.py and tests/test_path_guard_hygiene.py: 222 passed.
  • git grep -n 'which is no branch' -- tests/test_family_bump_fetch_refspecs.py: finds nothing at this head (it finds lines 582 and 691 on main). The one other line of the file that has both refs/h and "no branch" is line 691, where "no branch" is said of refs/headsx/.

Merge-trees: git merge-tree --write-tree origin/main HEAD is clean (exit 0). Main was 0ed3e57f8 when read, and the merge base is e04826db4; main's one commit since touches none of the three files named here. Of the 49 open PRs, only this one touches tests/test_family_bump_fetch_refspecs.py (read from gh pr list --json files; the largest has 9 files).

Closes #1230

Lane: openreposhape-5 (openRepoShape-5)

🤖 Generated with Claude Code

Summary by Sourcery

Enhancements:

  • Clarify test documentation to accurately describe how Git resolves the carried refs/h source ref and when its fetch conflicts with an existing destination.

… the docstring of `test_which_clause_leaves_each_refspec_out` say the source `refs/h` takes the one ref git completes it to, which may be a branch or tag, not `no branch` (#1230)

The two passages said the carried refspec `+refs/h:refs/remotes/origin/hy`
takes "the one ref of that name, which is no branch". That is true of an
exact `refs/h` only. Git completes the source `refs/h` to the exact ref, to
the branch `refs/heads/refs/h` or to the tag `refs/tags/refs/h`, so a remote
that holds a branch or a tag of that name has a source that is one, and beside
a branch `hy` the fetch exits 128 `Cannot fetch both refs/heads/hy and
refs/heads/refs/h to refs/remotes/origin/hy` (measured in #1230, git 2.43.0 and
2.47.1). The clash is the one any carried refspec has where its destination is
a branch's name, so the passages now say that instead of "no branch".

Wording only, in a comment and a docstring. The row
`carried-refs-h-not-a-pattern`, the file's syntax tree outside that docstring
and the scripts are as on main. (The re-check of #1157, NIT 1.)

Closes #1230

Lane: openreposhape-5 (openRepoShape-5)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

This wording-only PR updates one comment and one test docstring so they accurately describe Git’s ref completion and destination collision behavior for the carried refs/h refspec; no code, test rows, or behavior changes are made.

File-Level Changes

Change Details Files
Correct the fetch-refspec documentation to describe Git’s resolution of the refs/h source and the conditions under which its destination can clash with a branch name.
  • Explain that refs/h may resolve to the exact ref or to a ref Git completes it to, including example branch and tag namespaces.
  • Clarify that the clash depends on the destination being a branch’s name rather than applying to every carried source.
  • Replace the outdated “no branch” wording in the WHICH_CLAUSE comment and test docstring without changing test data or behavior.
tests/test_family_bump_fetch_refspecs.py

Assessment against linked issues

Issue Objective Addressed Explanation
#1230 Correct both passages in tests/test_family_bump_fetch_refspecs.py so they describe Git resolving the source refs/h to the exact ref or a ref it completes to, including a branch or tag, rather than claiming it is never a branch. ✅

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@brettheap

Copy link
Copy Markdown
Contributor Author

Independent review of #1235 (#1230) for lane openRepoShape-5, by rv1235 (sonnet), at 644982d: READY

I found no MUST. Two SHOULDs show that the new wording is still not true of git in two places. I recommend folding both in before the PR leaves draft. Each is a one-sentence rewording, and the syntax-tree check can then be re-run.

What I checked and measured

Setup: disk had 13G free at the start and 30G at the end. My worktree is /workspace/projects/openRepoShape-worktrees/rv1235-review, detached at 644982da1d542854f9dc22b5fbce9a229fb90367, and it is clean. My folder is $R = /tmp/claude-1000/-workspace-projects-openRepoShape/50cede29-ab82-4ca3-9f67-d98e1a58c8d2/scratchpad/v130/rv1235, with notes in $R/STATE.md. I pushed nothing and made no GitHub writes. The only gh calls were reads: issue view, pr view, one pr list and one gh api GET of main.

1. Probes of git behaviour. Local bare remotes, GIT_CONFIG_GLOBAL=/dev/null, GIT_CONFIG_NOSYSTEM=1, GIT_SSH_COMMAND=false. The fetch was +refs/heads/*:refs/remotes/origin/* plus +refs/h:<dest>. Each remote had main and hy, and each source ref was a different commit, so I could see which one git took. Logs are $R/probe-g243b.log and $R/probe-g247b.log. They are identical for git 2.43.0 (/usr/bin/git) and git 2.47.1 ($Q/v115/w764/git247/bin/git).

Remote also has Destination origin/hy Destination origin/not-a-branch
branch refs/heads/refs/h 128, Cannot fetch both refs/heads/hy and refs/heads/refs/h to refs/remotes/origin/hy 0, holds the branch's commit
tag refs/tags/refs/h 128, ... refs/tags/refs/h ... 0, holds the tag's commit
exact refs/h (update-ref) 128, ... refs/h ... 0, holds that commit
none of these 128, couldn't find remote ref refs/h 128, same

The four cases in the PR body's table hold on both gits. "The one ref" also holds. When several exist, git takes one:

  • the exact ref beats a tag or a branch;
  • a tag beats a branch;
  • a branch alone is taken.

Two other probes, $R/probe2-g243.log and $R/probe2-g247.log, support the findings below.

2. The issue's Verify items.

  • Syntax tree (python3 -I, 3.12.3, $R/astcmp.py): with the docstring of test_which_clause_leaves_each_refspec_out removed from both sides, main and head give 75050 characters of dump each, and they are EQUAL. Three checks back this up:
    • Controls: renaming WHICH_CLAUSE gives 75050 against 75052 and DIFFERENT. With the docstrings left in, the trees are DIFFERENT.
    • Location: the hunks are @@ -581,4 +581,5 (only #: lines) and @@ -690,4 +691,6 (inside the docstring, which spans lines 665-696 at head).
    • Check: git diff --check is clean.
  • -k carried-refs-h-not-a-pattern over both files: 2 passed, 123 deselected ($R/runA.log).
  • Other ids: --collect-only gives 68 + 57 = 125 ids ($R/runD.log). -k test_which_clause_leaves_each_refspec_out gives 72 passed, 53 deselected ($R/runC.log).
  • Hygiene: tests/test_repo_hygiene.py plus tests/test_path_guard_hygiene.py give 222 passed ($R/runB.log).
  • Grep: git grep -n 'which is no branch' -- tests/test_family_bump_fetch_refspecs.py finds nothing at head (rc 1). On main it finds lines 582 and 691.
  • Columns and characters: 11 added lines, longest 74 columns, none over 79, no trailing whitespace or tabs, 0 non-ASCII characters. The file's longest line is still 79 and it has 0 non-ASCII lines.

All runs went through run9.sh with EMAIL=service@opensoft.dev, as background calls. Their PATH had git 2.47.1 first, so these runs used 2.47.1 where the PR body says 2.43.0. I ran no mutants, because the trees show no behaviour moved.

3. The PR body's claims.

4. Other sentences saying the source refs/h takes no branch.

  • Within the test file, no sentence says that of the source refs/h. The remaining "no branch" lines are about refs/hidden/..., commits and outside-origin-prefix.
  • No other file has a sentence about the source refs/h. The one exception is the code sentence in NIT 4, which covers it without naming it.

Findings

SHOULD 1: "like any carried source" is false of a carried branch. The docstring (lines 693-694) says refs/h is "a ref that, like any carried source, clashes where its destination is a branch's name".

  • $R/probe2-g243.log and $R/probe2-g247.log, case (a):
    • +refs/heads/main:refs/remotes/origin/main fetches with rc 0.
    • +refs/heads/rel/*:refs/remotes/origin/rel/* fetches with rc 0.
    • +refs/pull/7/head:refs/remotes/origin/main fails with rc 128, Cannot fetch both refs/heads/main and refs/pull/7/head.
  • The file's own carried-one-branch and carried-branches-in-their-own-folder rows have a branch's name as destination and do not clash. The same sentence's last clause, "a branch ... each under the name BRANCHES gives it", contradicts "any".
  • The code's own docstrings limit the clash to a source that names no branch. This PR introduced the over-generalization, copied from the issue's suggested wording.
  • Fix: "like a pull request's ref, clashes where..." or "like any carried source that is not the branch itself".

SHOULD 2: the list "refs/h itself or the branch or tag of that name" is closed, but git completes the name to more. This is in both passages, at comment lines 581-582 and docstring lines 691-692.

  • Measured on both gits, onto a free name: refs/refs/h, refs/remotes/refs/h and refs/remotes/refs/h/HEAD each fetch with rc 0 and are taken. The first two onto origin/hy fail with rc 128, Cannot fetch both refs/heads/hy and refs/refs/h and ... refs/remotes/refs/h ....
  • A plain git push to a bare remote made refs/refs/h and refs/remotes/refs/h. Only the exact refs/h is refused as a funny ref. I made refs/remotes/refs/h/HEAD only by update-ref.
  • So the sentence has the same defect as the original NIT 1: an incomplete enumeration read as complete. The goal, that no reader is told the source is never a branch, is met.
  • The issue's own line "what such a remote can hold under that name is a branch or a tag" is incomplete in the same way.
  • Fix, which also covers NIT 3: "refs/h itself or a ref git completes it to, such as the branch refs/heads/refs/h or the tag refs/tags/refs/h".

NIT 3: wording.

NIT 4: the PR body says shape_materialize.py and bump-leg.py "make no claim about refs/h". That is not quite right.

  • scripts/shape_materialize.py:1721-1722, in the docstring of the clause _left_out uses, says "ANY OTHER SOURCE NAMES NO BRANCH". That covers the source refs/h, since it is not under refs/heads/, and is untrue where git completes it to a branch.
  • shape_materialize.py:1874 and bump-leg.py:1165 ("whose source names no branch") define the clashing shape the same way. They never name refs/h and sit well as examples.
  • All three are out of scope here: the PR is wording-only in the test file, and shape_materialize.py is a pinned copy.
  • Suggestion: say "left for a follow-up" in the body instead of "make no claim", or file the follow-up.

NIT 5: the live PR body ends with a Sourcery section. ## Summary by Sourcery, about lines 91-93, comes after the Lane: line, so the last line is no longer the Lane line. body-guard.py still exits 0. The coordinator may want to strip it before any squash message is built.

Lane: openreposhape-5 (openRepoShape-5)

… the docstring of `test_which_clause_leaves_each_refspec_out` name the refs git completes `refs/h` to, and say a pull request's fetch clashes, not any carried one's (#1235)

The independent review of #1235 (issuecomment-6375269462, READY, two SHOULDs)
found the first round's wording still untrue of git in two places.

The docstring said `refs/h` clashes "like any carried source". A carried
branch does not clash onto its own name: `+refs/heads/main:refs/remotes/origin/main`
exits 0 where `+refs/pull/7/head:refs/remotes/origin/main` exits 128. It now
says the fetch clashes as a pull request's ref does.

Both passages listed what git completes `refs/h` to as "the branch or tag of
that name", a list that reads as closed. Git also completes it to `refs/refs/h`
and `refs/remotes/refs/h`. Both now say the ref is `refs/h` itself or a ref git
completes it to, such as the branch `refs/heads/refs/h` or the tag
`refs/tags/refs/h`, and the citation follows the clause in parentheses.

Wording only, in a comment and a docstring. The file's syntax tree outside that
docstring is as on main. The docstring in `scripts/shape_materialize.py` that
says "ANY OTHER SOURCE NAMES NO BRANCH" is left for a follow-up issue.

Lane: openreposhape-5 (openRepoShape-5)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@brettheap

Copy link
Copy Markdown
Contributor Author

Re-check of #1235 (#1230) for lane openRepoShape-5, by rv1235 (sonnet), at 8158230: READY

I found no MUST and no SHOULD. SHOULD 1, SHOULD 2, NIT 3 and NIT 4 of my first review are fixed, and I found three small NITs.

$R below = /tmp/claude-1000/-workspace-projects-openRepoShape/50cede29-ab82-4ca3-9f67-d98e1a58c8d2/scratchpad/v130/rv1235.

What I checked and measured

My worktree /workspace/projects/openRepoShape-worktrees/rv1235-review is now detached at 81582301ebab28792bc65a649e9b26c054cc1a17, clean. The object was already local, so I did no fetch. I made no GitHub writes.

(1) The new sentences, against my earlier probes.

  • Comment, lines 581-584. The text is "the one ref that name stands for, refs/h itself or a ref git completes it to, such as the branch refs/heads/refs/h or the tag refs/tags/refs/h". It is an open list now. My probes in $R/probe-g243b.log and $R/probe-g247b.log show it holds on git 2.43.0 and 2.47.1:
    • the exact ref, the branch and the tag are each taken;
    • so are refs/refs/h and refs/remotes/refs/h;
    • the exact ref beats the tag, which beats the branch, so "the one ref" holds.
  • Docstring, lines 692-697. "Whose fetch clashes, as a pull request's ref does, where its destination is a branch's name." The pull-request half is measured in $R/probe2-g243.log and $R/probe2-g247.log: +refs/pull/7/head:refs/remotes/origin/main exits 128 Cannot fetch both refs/heads/main and refs/pull/7/head .... The refs/h half is measured too: 128 onto origin/hy for the exact ref, branch and tag.
  • Citation. It now follows the clause in parentheses, and the fetch is what clashes, not the ref.
  • Commit message. Its probe claims are true: the carried branch exits 0, the pull request exits 128, and refs/refs/h and refs/remotes/refs/h are completions. Its subject is 241 characters.
  • One new probe, for an edge ($R/probe3-g243.log, $R/probe3-g247.log; git 2.43.0 and 2.47.1 give the same result). A remote with the branch refs/heads/refs/h and the destination refs/remotes/origin/refs/h, which is the glob's own name for that branch, fetches with rc 0. Onto origin/hy it exits 128. See NIT 1.

(2) The gates at the new head. All through run9.sh, as run_in_background calls, with EMAIL=service@opensoft.dev, tree clean (dirty=0). The runner's PATH gives /usr/bin/git, git 2.43.0 ($R/r2-runner-git.log).

  • Syntax tree (python3 -I, 3.12.3, $R/astcmp.py): the file at head against main 0ed3e57f8, with the docstring of test_which_clause_leaves_each_refspec_out removed from both, gives 75050 and 75050 characters, EQUAL. The control that renames WHICH_CLAUSE gives 75052 and DIFFERENT. The docstring spans are 664-693 on main and 666-697 at head.
  • -k carried-refs-h-not-a-pattern over both files: 2 passed, 123 deselected ($R/r2-runA.log).
  • Hygiene: tests/test_repo_hygiene.py plus tests/test_path_guard_hygiene.py give 222 passed ($R/r2-runB.log).
  • Grep: git grep -n 'which is no branch' -- tests/test_family_bump_fetch_refspecs.py finds nothing (rc 1). Nothing remains of "branch or tag of that" or "like any carried source" in tests/ or scripts/. The only line with both refs/h and "no branch" is 691, where it is said of refs/headsx/.
  • Size and characters: against the base e04826db4, 11 lines are added and 7 removed. The longest added line is 76 columns, none are over 79, and there is no trailing whitespace. git diff --check is clean. The file has 0 non-ASCII lines and its longest line is 79.
  • Merge-tree: git merge-tree --write-tree origin/main HEAD exits 0, with origin/main at 0ed3e57f8.

(3) The live body and #1238.

Findings

NIT 1: one literal edge remains in the docstring. "Whose fetch clashes ... where its destination is a branch's name" is not true of refs/h when the destination is the name of the very branch it completes to. This is the same shape as my earlier SHOULD 1, but the remote needs the branch refs/heads/refs/h and the destination must be origin/refs/h. It is exact for a pull request's ref. Optional fix: "where its destination is another ref's branch name".

NIT 2: #1238's text has two small gaps.

  • Its problem statement lists five completions and leaves out refs/remotes/refs/h/HEAD. That one is taken too (rc 0 onto a free name, probe 1, remoteshead-nb, both gits).
  • Its suggested docstring text, "(refs/h: itself, or the branch ..., or the tag ...)", is a closed-looking list like the one SHOULD 2 fixed.
  • Saying "such as" there avoids a third round. The issue says the wording is the owner's.

NIT 3: the live body.

Lane: openreposhape-5 (openRepoShape-5)

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Approved.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@brettheap
brettheap merged commit bd3e9c1 into main Oct 24, 2026
5 checks passed
@brettheap
brettheap deleted the fix/1230-refs-h-wording branch October 24, 2026 15:10
brettheap pushed a commit that referenced this pull request Nov 8, 2026
Main moved past #1180's squash 4829418 by e9a1650 (#1109) and bd3e9c1
(#1235), which touch shape-doctor.py, tests/test_doctor_plan_overrides.py,
tests/test_family_bump_fetch_refspecs.py and tests/test_path_guard_hygiene.py.
None of them is a file #1190 changes. This is the third of the three
merges that restack #1190 after #1180 landed.

Lane: openreposhape-1 (openRepoShape-1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap added a commit that referenced this pull request Nov 8, 2026
…says a full name that is no pattern takes the one ref git completes it to, which may be a branch, where it said any other source names no branch (#1238) (#1244)

#1238 is a wording NIT that the review of #1235 (#1230) found in the code: NIT 4 of rv1235's first review, and NIT 2 of its re-check. The docstring of `gives_a_branch_another_name` in `scripts/shape_materialize.py` ended "ANY OTHER SOURCE NAMES NO BRANCH", but git completes a source that is a full name and no pattern, and completes `refs/h` to the branch `refs/heads/refs/h` where the remote has one. The sentence now says what git does with such a source, as an open list ("such as"), and where the clash is. A reader of the docstring is no longer told that a source outside `refs/heads/` is never a branch.

Wording only, in one docstring of one file: 9 lines added, 3 removed. No code, no row, no other file. The file's syntax tree with every docstring dropped is the same as main's.

The sentence is at `scripts/shape_materialize.py:1720-1723`, the end of the docstring of `gives_a_branch_another_name` (line 1671):

```
1720:     BRANCH REACHES). A shorter start, such as `refs/*` or `*`, can name a
1721:     tag, and that clause has left it out first. ANY OTHER SOURCE NAMES NO
1722:     BRANCH, though a refspec from one can still put a ref on a branch's
1723:     name: `origin_fetch_refspecs` says where (WHAT CAN STILL CLASH).
```

It is true of a source in another namespace such as `refs/pull/7/head`. It is not true of every other source. A source that is a full name and no pattern takes the one ref git completes the name to on the remote: for `refs/h`, `refs/h` itself, the branch `refs/heads/refs/h`, the tag `refs/tags/refs/h`, `refs/refs/h` or `refs/remotes/refs/h`, and where several exist git takes the exact ref over a tag over a branch. Where the remote has the branch, the source is a branch. The code is right: `gives_a_branch_another_name("refs/h", ...)` is False, because the source has no `*` and does not start with `refs/heads/`, so the refspec is carried. What fails afterwards is the clash the docstring's own next clause names. Only the words were untrue.

Measured on this branch's base, with git 2.43.0 (`/usr/bin/git`) and git 2.47.1, which gave the same answers on every row. Local bare remotes, `GIT_CONFIG_GLOBAL=/dev/null`, `GIT_CONFIG_NOSYSTEM=1`, `GIT_SSH_COMMAND=false`. rv1235's probe scripts were run again here (copied unchanged, new output folders), and their logs are identical to rv1235's. The fetch was `+refs/heads/*:refs/remotes/origin/*` plus the refspec of the row, and the remote had `main` and `hy`:

```
the remote has                    refspec                          result
branch refs/heads/refs/h          +refs/h:refs/remotes/origin/hy   128 Cannot fetch both refs/heads/hy and refs/heads/refs/h to refs/remotes/origin/hy
tag refs/tags/refs/h              +refs/h:refs/remotes/origin/hy   128 Cannot fetch both refs/heads/hy and refs/tags/refs/h to refs/remotes/origin/hy
exact ref refs/h (update-ref)     +refs/h:refs/remotes/origin/hy   128 Cannot fetch both refs/heads/hy and refs/h to refs/remotes/origin/hy
refs/refs/h or refs/remotes/refs/h  +refs/h:refs/remotes/origin/hy 128, the same message naming that ref
each of those five refs, alone    onto origin/not-a-branch         0, holds that ref's commit
branch refs/heads/refs/h          onto origin/refs/h               0 (the branch's own name: no clash)
refs/pull/7/head                  onto origin/main                 128 Cannot fetch both refs/heads/main and refs/pull/7/head to refs/remotes/origin/main
branch refs/heads/main            onto origin/main                 0
```

So a name with no `*` can be a branch that its spelling does not show, and the clash is a ref that is not that branch put on a branch's name, which is what the second half of the sentence has to say. "ANY OTHER" was wrong in a second way: a carried refspec from a source that IS the branch does not clash onto that branch's own name.

The docstring's last sentence, now lines 1721-1729:

```
    tag, and that clause has left it out first. ANY OTHER SOURCE TAKES NO
    BRANCH BY A PATTERN, and a full name that is no pattern, such as
    `refs/h`, takes the one ref git completes it to on the remote: that ref
    itself, or one such as the branch `refs/heads/refs/h` or the tag
    `refs/tags/refs/h` (measured, git 2.43 and 2.47.1). Such a name can
    stand for a branch its spelling does not show, so it is not left out
    here. A refspec from any such source can still put a ref on a branch's
    name, if the ref is not that branch itself: `origin_fetch_refspecs`
    says where (WHAT CAN STILL CLASH).
```

- **"Takes no branch by a pattern"** holds for every source this sentence covers. Git matches a pattern against each ref's whole name, and a ref under `refs/heads/` starts with the part before the `*` only where that part is a start of `refs/heads/` or is under it, the two cases the function reads before this sentence.
- **The list is open** ("one such as"): it names the exact ref, the branch and the tag, and the rows above show two more completions (`refs/refs/h`, `refs/remotes/refs/h`), so no reader takes the three as all.
- **"So it is not left out here"** is what the code does: `gives_a_branch_another_name` is False for `refs/h` onto `origin/hy`, onto `origin/refs/h`, for `refs/pull/7/head` onto `origin/main`, and for the pattern `refs/pull/*/head`, and `origin_fetch_refspecs(["+refs/h:refs/remotes/origin/hy"])` returns it carried. It is True for `refs/heads/main` onto `origin/hy`, as before.
- **"If the ref is not that branch itself"** is the condition the rows show: the clash is two different refs on one name, a branch's.

The checks the issue's Verify lists:

- **The diff is only that docstring.** The diff has one hunk, inside the docstring, which spans lines 1672-1724 on main and 1672-1730 here. The file's tree with every docstring dropped (module, class and function: 168 each side) is equal on main (`50869f384`) and at this head under `python3 -I`, `ast.dump`: 380120 characters of dump on both sides. A control that renames `gives_a_branch_another_name` on the head side gives 380121 and DIFFERENT, and the same trees with the docstrings kept differ (520956 against 521366), so the check can fail. Python 3.12.3. The check compares two dumps made by one interpreter and asserts no text that depends on the version.
- **`git grep -n 'ANY OTHER SOURCE NAMES NO$' -- scripts/`** finds nothing at this head. On main it finds `shape_materialize.py:1721`. With any case and no line end, `git grep -n -i 'other source names no'` also finds nothing here, and nothing but that line on main.
- **No added line is over 79 columns or holds a non-ASCII character.** 9 lines are added, the longest is 75 columns, and none has a tab or trailing space. The file's own width is unchanged: 20 lines over 79 columns (longest 118) and 90 lines with a non-ASCII character, as on main, none of them mine.
- **No test, `README.md`, `docs/` page or `AGENTS.md` line pins the sentence.** The issue's grep holds at this head.

The issue asks whether `shape_materialize.py:1874` (now 1880, in the docstring of `origin_fetch_refspecs`) and `bump-leg.py:1165` (in `_leg_fetch_refspecs`) want the same change. Both paragraphs were read, and both are left as they are:

```
shape_materialize.py:1880:     WHAT CAN STILL CLASH is a refspec carried whose source names no branch,
bump-leg.py:1165:     whose source names no branch, onto a name a branch of the remote has
```

1. **They define a shape by its examples and say nothing about what git completes a name to.** Each goes on at once to the pull requests (`origin/pr/` where a branch `pr/7` is, and `+refs/pull/7/head:refs/remotes/origin/main` beside a branch `main`), and neither names `refs/h`. They were not the sentence that told a reader a source is never a branch.
2. **"Names no branch" is spelling there, as it is in the sentence the function's docstring opens with.** "A SOURCE UNDER `refs/heads/` NAMES BRANCHES" is true by spelling too: `can_name_a_tag`'s docstring measures that `+refs/heads/zz` took the tag `refs/tags/refs/heads/zz` where the remote had no branch `zz`. The sentence changed here now says outright that the spelling of a name with no `*` does not show what it stands for, so the docstring that defines the class carries the caveat the two borrowers need.
3. **The mechanism they state is the one that fails `refs/h`:** "wherever a name it writes is a branch's". The `origin_fetch_refspecs` paragraph ends with why such a refspec is carried at all: "Which names the remote's branches have is not known from its refspecs, and left out, what such a refspec keeps would be pruned on every run". That holds for `refs/h` word for word.
4. **The cost of leaving them is one reading:** a reader who takes "names no branch" in git's sense meets the single case `refs/h` beside a remote branch `refs/heads/refs/h`. If the review wants a word there, it is a few words in each place, and no open PR touches either paragraph (merge-trees below), so it can be a fix round.

None of either. No row is added or changed and no behaviour moved: the syntax tree outside the docstring is main's, so there is nothing for main to fail and nothing to mutate. `gives_a_branch_another_name` returns what it returned for every case in the table above (called in process at this head; the syntax tree outside the docstrings is main's).

All through the lane's runner `run9.sh` (stub `gh` first on PATH, `GIT_SSH_COMMAND=false`, `GIT_CONFIG_GLOBAL=/dev/null`), on the committed head `36ec589a6` with the tree clean (`dirty=0`), as one run of seven files at a time: `tests/test_pinned_copy_text.py`, `tests/test_path_guard_hygiene.py`, `tests/test_repo_hygiene.py`, `tests/test_agents_md_codes.py`, `tests/test_cli_reference.py` (the hygiene five), `tests/test_family_bump_fetch_refspecs.py` and `tests/test_bump_leg_fetch_refspecs.py`.

- **The seven files, git 2.43.0** (`/usr/bin/git`): 504 passed in 469.44s (47 `test_agents_md_codes.py`, 68 `test_bump_leg_fetch_refspecs.py`, 23 `test_cli_reference.py`, 57 `test_family_bump_fetch_refspecs.py`, 121 `test_path_guard_hygiene.py`, 87 `test_pinned_copy_text.py`, 101 `test_repo_hygiene.py`; the counts are from `--collect-only -q` on the same head and add up to 504). Log `v131/n1238/gate-g243.log`.
- **The seven files, git 2.47.1** (first on PATH, `git --version` printed into the log): 504 passed in 714.00s. Log `v131/n1238/gate-g247.log`.
- **`-k carried-refs-h-not-a-pattern`** over `tests/test_family_bump_fetch_refspecs.py` and `tests/test_bump_leg_fetch_refspecs.py`: 2 passed, 123 deselected, on 2.43.0 and on 2.47.1 (`run-k-g243.log`, `run-k-g247.log`).
- Both runs start with `dirty=0`. Python 3.12.3 (CI runs 3.14); nothing asserts text that depends on the version.

The measured git behaviour above comes from the probe scripts, not the test files. `-k carried-refs-h-not-a-pattern` over the two fetch-refspec files is part of those runs.

`git merge-tree --write-tree origin/main HEAD` exits 0, with tree `9d874bcf2caf`, equal to this head's tree, against main `50869f384` (the merge base). Against the head of each open PR that touches `scripts/shape_materialize.py` or `scripts/bump-leg.py` (read from `gh pr list --state open --limit 100 --json number,headRefName,headRefOid,files`, 52 open PRs at the last read, 7 of them touch one of the two files), `git merge-tree --write-tree HEAD <head>` exits 0 and the merged tree still holds the new sentence and not the old one:

```
#1223 f50b472  exit 0   #1207 6fa1edd  exit 0   #1160 0068f5a  exit 0   #1131 55c30f2  exit 0
#1118 2307036  exit 0   #1105 b28f40d  exit 0   #1104 de89aa8  exit 0
```

Main moved once while this was gated, to `4829418f0` (`adopt-project.py`, `docs/cli.md` and a new test file: none of the files this change or its runs touch), and `git merge-tree --write-tree origin/main HEAD` against it also exits 0.

None of their hunks is within 100 lines of the sentence changed here.

Review: rv1244 and rv1244b, lane 5's independent sonnet reviewers, which wrote none of #1244: READY at `36ec589a6`, H0 of this landing (issuecomment-6723280477), with no MUST or SHOULD; its four NITs are optional (two wordings, a body note and a follow-up). CI (Brett Heap's ruling 27): H0 is the head 473959f itself, which passed tests, tests-git-bash and SonarCloud; main has touched none of its files since its base 9729315; and the local gate passed on its predicted squash tree fb798f3 over main 9252ef2: the repo_hygiene, agents_md_codes, cli_reference sets, 171 passed. Landed on Brett Heap's word of 2026-10-23T13:10:26Z, relayed to every lane by lane 3 (#473 issuecomment-6365697123): "merge v1.2.1 PRs once reviewed and green".

Closes #1238

Lane: openreposhape-5 (openRepoShape-5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant