Repository navigation
adopt-project.py: the #654 rehearsal asks nothing about the case twins plan-case-twins names, so check names folder twins once under core.ignorecase=true, never again as plan-file-reencoded over git's will not add file alias, and names the files beside them as it does without that setting (#1184) - #1297
brettheap wants to merge 2 commits into
Conversation
…s `plan-case-twins` names, so `check` names folder twins once under `core.ignorecase=true`, never again as `plan-file-reencoded` over git's `will not add file alias`, and names the files beside them as it does without that setting (#1184) Measured on f981468 with git 2.43, on a disk that keeps case under a global `core.ignorecase=true`, 3 of 3 runs each: folder twins (`D/x`, `d/x`) of one blob or two, three paths of one blob (`AB`, `Ab`, `ab`) and two `.gitattributes` in folder twins, all kept in the root, were `plan-case-twins` and then `plan-file-reencoded`, git saying `fatal: will not add file alias` at the rehearsal's `git add -u`; and a CRLF `notes.txt` kept beside the folder twins went unnamed, since git stopped that `git add` at the alias. Twins whose own bytes git would store otherwise were named a second time too, with the setting or without it. `_case_twins_kept` now gives both `_case_twin_findings` and `_reencoded_files_findings` the sets each repository holds. The rehearsal leaves the sets the finding names out of the files it asks about, and their `.gitattributes` out of what its `git add` stores again (their rules still reach the files beside them). The sets of one blob the NOTE takes are asked as before. Every other line `check` prints is byte for byte as it was. Rows in tests/test_adopt_check_throwaway_repos.py, 7 failing on main, and a control that passes on both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideImplementationShare the case-twin classification between the case-twin finding and the re-encoding rehearsal, so sets already named as
TestsDocument and test that named case-twin sets are reported only once, while adjacent re-encoded files, twin rules, and NOTE-only sets retain their existing checks.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-3, 2026-11-10T06:33:14Z) RV1184: READY AFTER FIXES at 9607ef6 Independent review of #1297 (for #1184, v1.2.1), lane openRepoShape-3. I wrote none of #1297. The head is still The code fix is correct and every claim I could measure held. Nothing is a MUST. Three SHOULDs remain: two rows that guard behaviour the body claims but no row pins (three non-equivalent mutants survive every existing row), and two body sections lane 3's template asks for. MUSTNone. SHOULDS1. No row pins twins in a leg whose own bytes re-encode. The body's third bullet says "in the root or a leg", but @pytest.mark.parametrize(("prefix", "answers", "where"), [
pytest.param("", {"A.md": ROOT, "a.md": ROOT}, ROOT_TWINS, id="root"),
pytest.param("src/", {}, CODE_TWINS, id="code-leg"),
])
def test_twins_git_would_store_as_other_bytes_are_the_set_alone(
tmp_path, prefix, answers, where):
source = a_source(tmp_path, files={
ROOT_ATTRIBUTES: TEXT_EOL_LF, f"{prefix}A.md": CRLF,
f"{prefix}a.md": b"three\r\n"})
plan = planned(source, tmp_path, answers)
for env in (None, ignoring_case(tmp_path)):
result = checked(plan, env)
assert finding_lines(result) == [twins_kept(
"1 set", where, "2 paths",
f"`{prefix}A.md` and `{prefix}a.md`")], result.stderrS2. No row pins "its rules still reach the files beside it". The body and the new comment above
In a live probe, each turned def test_the_twins_rules_still_reach_the_files_beside_them(tmp_path):
rule = b"*.txt text eol=lf\n"
paths = ("D/.gitattributes", "d/.gitattributes", "D/n.txt", "d/m.txt")
source = a_source(tmp_path, files=dict(zip(paths,
(rule, rule, CRLF, CRLF))))
plan = planned(source, tmp_path, dict.fromkeys(paths, ROOT))
without, ignored = checked(plan), checked(plan, ignoring_case(tmp_path))
assert codes(without) == [PLAN_CASE_TWINS, REENCODED_FILES], \
without.stderr
assert "`D/n.txt` and `d/m.txt`" in finding_lines(without)[1]
assert finding_lines(ignored) == finding_lines(without), ignored.stderrI ran both rows (3 rows in all) from a scratch module in my own worktree:
S3. Body: lane 3's opening line and "Out of scope" section are missing. Insert this above
Add this before
NITN1. The #1237 sentence is wrong. #1237 does not change
N2. The Merges table is against
N3. Say the side effect plainly. After the third bullet's last sentence, add:
N4. Say what a disk that ignores case does with disagreeing twin rules. After "It is still checked out, so its rules still reach the files beside it.", add:
Evidence1. Before and afterMy probe built each source under an empty scratch
Every line the head prints other than the dropped twin
2. The
|
| Mutant | Result |
|---|---|
| M6: moved twins asked again | survived (S1) |
M7: twin .gitattributes not checked out |
survived (S2) |
M8: refused or () to () |
survived; equivalent (item 4) |
| M9: whole twin folders left out | survived (S2) |
7. The gates
Head 9607ef6e, one pytest at a time, -rs, under the stub gh, git-filter-repo (a40bce548d2c), GIT_CONFIG_GLOBAL=/dev/null:
- throwaway +
test_adopt_root_symlinks.py, one run:435 passed, 5 skipped in 368.21s (0:06:08)(2 need a disk that ignores case, 3 need git 2.45 for reftable) - the 8 other modules that grep either code:
443 passed, 2 skipped in 316.85s (0:05:16)(2eol_block_through_linksrows for a root that keeps a link) tests/test_shape_doctor.py, in the background:446 passed, 3 skipped in 463.77s (0:07:43)(Windows PowerShell)tests/test_scaffold_leg_path_collisions.py:104 passed in 3.02s- the guards:
470 passed, 3 skipped in 54.49s(1 Windows default, 2 becauseGIT_CONFIG_GLOBALwas set)
All of these match the body's numbers.
8. Merges
I measured #1237 at both heads, as asked.
- The head with main: clean against
de55beb0(tree3526f2aa) and against3bf35b22(tree62b854ef). - Clean, as in the body: adopt-project.py and update-shape.py: a revision git is asked about ends its argv in
--, so a root file namedHEADno longer stopsexecutewith exit 128 before that leg is pushed, nor blanks whatapply --branchsays it committed (#1215) #129439d5607a, adopt-project.py:plan-wrong-kind,plan-unsupported-mode,plan-bad-legandplan-bad-idsay a plan value that is not text as_read_assays what the plan's reader read,kind is the number 7anddeclares leg a list of 2 item(s)wherereprspelled it7,Trueand['a', 'b'], and a plan with nokind:ormode:line saysabsentwhere it saidNone(#1199) #1281d3c15436, adopt-project.py: a stale split branch in a source refs directory this user cannot list is refusedsource-unreadableby name, neverplan okfromcheckor two legs made byexecute(#1257) #1265a6af6f56, repo_shape.py and adopt-project.py: a plain integer of more digits than this Python reads, or a decimal too large to be finite, isyaml-unparsableby its line, where it was aValueErrortraceback or "the number inf", and_read_assays a float in digits with no exponent, where it said "the number 1e+300" (#1212) #1249430be99e, adopt-project.py: a refused command's REFUSED block spells each character a terminal acts on in the command, its directory and what it printed, as_first_saidspells gh's, so--local-remote-dirno longer reaches the terminal raw there; a NOTE beforeNEXT:says when the source's path in its clone command holds a character a terminal does not show as itself; and rows pin six sites #1177's 148 left to chance (#1195) #1225324aa8ce, adopt-project.py:checkandexecuteread one list of a plan's problems against its source, in the order a person mends them (another repository, a refused name, a moved source, an unanswered entry), socheck's first finding isexecute's refusal for each pair (#1113) #1210107512c4, adopt-project.py:_pin_row_problemsays a pin field whose value is not text as_read_assays what the plan's reader read, "recordscommit:as the number 7", where it spelled it as Python does,commit: 7andTrue, socheckandexecutename it in the words the plan's other refusals use under the sameplan-pin-malformed(#1114) #1198cd04e7bf, adopt-project.py:_github_namereads no owner over 39 characters or repository over 100, soplan --source <50 b's>/Thingnever asksghfor a name GitHub cannot hold, no remedy offers--org <50 b's>, andcheckandexecutenever clone such a record again (#1129) #119460ccce31, adopt-project.py and repo_shape.py: a name that holds a default-ignorable character, which a terminal draws as nothing, is spelled by its code point inplan's lines and in theunsafe-valuerefusal, so a branchma<U+034F>inno longer prints asmain(#1128) #11719c225f20. adopt-project.py: each cloneexecutemakes names its remoteorigin, whateverclone.defaultRemoteNamesays, soexecuteno longer ends inREFUSED mounting the legs("No such remote 'origin'") after both legs are pushed (#1261) #1268 wasf685fe87at908e2dbd, and its new head1ce9c4f9is clean too (ed8c470a). - adopt-project.py: a source whose only remote is not named
origin, asclone.defaultRemoteNamenames it, is read from that remote, soplanreads its organisation, default branch and branches where it refusedadopt-no-org, took HEAD's branch as the default and asked--tracking-branchof the source's own branches, and one with several remotes and nooriginnames them and guesses none (#1276) #1280: 3 hunks inadopt-project.py, at 3598, 3693 and 5796. They are byte-identical (labels dropped) with mainde55beb0, with main3bf35b22and with the head, so none of them is this PR's. - adopt-project.py: the mount removes a path a case twin is at or under with
git rm -fand writes the twins the root keeps back from the index, so on a disk that ignores caseexecutekeeps a set the plan splits across repositories or drops (#1183) #1237 at73b45ee5: with main, 1 hunk (imports, line 135); with the head, hunks at 145 and 1035. The resolution (the union of the imports, then both sections) gave47 passed, 4 skipped, 223 deselected in 21.15sover both PRs' new rows. - adopt-project.py: the mount removes a path a case twin is at or under with
git rm -fand writes the twins the root keeps back from the index, so on a disk that ignores caseexecutekeeps a set the plan splits across repositories or drops (#1183) #1237 atfa6d189f: clean with main3bf35b22(tree976f9184); with the head, one hunk at 1031 (treed52eebb8). Both sections kept gave47 passed, 4 skipped, 223 deselected in 31.23s. Keeping both sections is the right advice.
9. The body
- It says what a person sees change, why (A) over (B), and that the disk that ignores case is read, not measured.
- It has one closing keyword (
Closes #1184) and is ASCII except the robot line. A Sourcery summary follows that line, added by the bot. - Every number I re-measured matches.
- Fixes: S3, N1 to N4.
Not measured
- Anything on a disk that ignores case: none is writable here.
- CI
testswas still running when I last read the checks;tests-git-bash,packageand Sonar were SUCCESS.
Paths
- Clone, detached at the head:
/workspace/projects/openRepoShape-worktrees/_lane3-scratch/RV1184, clean. - Scratch:
/workspace/projects/openRepoShape-worktrees/_lane3-scratch/RV1184-scratch. It holds:probe.py,compare.py,compare.txt,main.json,head.json,edge-*.json,rename_probe.pymutate.py,run-mutants.sh,mutants.logtest_rv1184_extra.ran.py,run-extra.sh,extra.logrun-gates.sh,gates.log,gate-*.logresolve1237.py,hunks-*.txt
- Worktrees in the scratch:
main-wtandhead-wt: clean.m1237-wtandm1237b-wt: the adopt-project.py: the mount removes a path a case twin is at or under withgit rm -fand writes the twins the root keeps back from the index, so on a disk that ignores caseexecutekeeps a set the plan splits across repositories or drops (#1183) #1237 resolutions, never committed.
- Basetemps:
/tmp/claude-1000/l3bt/RV1184-*. - Nothing is pushed, posted or written to a register. No background job is left running.
Lane 3's response (lane openRepoShape-3): all of it is taken. A fix round, FX1184, adds S1's parametrized row and S2's row to tests/test_adopt_check_throwaway_repos.py and gates them. It also rebuilds the body with S3 and N1 to N4 and the new counts. An opus delta review follows. The follow-up candidate in S3's "Out of scope" goes to Brett Heap for his word before anything is filed. #1297 stays a draft with no milestone until it reads READY.
…n bytes re-encode runs in the root and in the code leg, and a new row holds that twin `.gitattributes` still reach the files beside them, so the rehearsal asking moved twins again, leaving out twin `.gitattributes`, or leaving out a whole twin folder fails a row (#1184) RV1184's review of #1297 found three mutants of the rehearsal's twin exclusion that every row let through. M6 asks the twins a leg takes again: `test_twins_git_would_store_as_other_bytes_are_the_set_alone` is now parametrized over the root and the code leg, and fails M6 at `[code-leg]`. M7 stops checking out twin `.gitattributes` and M9 leaves out every file in a folder that holds a named twin: `test_the_twins_rules_still_reach_the_files_beside_them` keeps `D/n.txt` and `d/m.txt` named beside `plan-case-twins`, with `core.ignorecase=true` as without it, and fails both. adopt-project.py is not touched. 10 rows for #1184, 9 failing on main and a control that passes on both. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-3, 2026-11-10T06:58:37Z) RV1184b: READY at 2e020b4 This is a delta review of #1297's fix round 1 ( MUST / SHOULDNone. NITN5. The #1237 bullet under "Merges" says "over both PRs' new rows" twice. Replace its last sentence with:
Evidence1. S1 and S2: the rowsThe two rows are the ones the round-0 review gave, word for word in their code; only the docstrings are new.
I ran the #1184 rows at
The main run's 9 failures are the 4 2. Nothing else changed
3. The body (
|
|
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 4 days and 14 hours by commenting |



What changed
A v1.2.1 fix for #1184, a follow-up lane 1 filed from its measurements on #1161's branch (PR #1189). It lands under Brett Heap's v1.2.1 merge word (2026-10-23T13:10:26Z) once reviewed and green.
What a person sees change
check, andexecute's refusal, name a set of case twins once, asplan-case-twins, and no longer a second time asplan-file-reencoded, a finding about bytes:core.ignorecase=true(the default on a disk that ignores case, or set globally on one that keeps it), folder twins (D/x,d/x) of one blob or two, three paths of one name (AB,Ab,ab) and two.gitattributesin folder twins, kept in the root, wereplan-case-twinsand thenplan-file-reencodedwith git'sfatal: will not add file alias 'D/x' ('d/x' already exists in index). Now they areplan-case-twinsalone, andexecute's refusal lists 1 finding where it listed 2.git add -uat that alias, so a file that really re-encodes beside the twins went unnamed under the setting: a CRLFnotes.txtunder* text eol=lf, kept besideD/xandd/x. Now it is named, andcheckprints what the same source prints without the setting.text), in the root or a leg, were also named a second time, asplan-file-reencoded, with the setting or without it. Now the set isplan-case-twinsalone. Its remediation, a rename, comes first, and theplanandcheckafter it ask about the renamed files. That second line was true -- their bytes do re-encode -- so a person now learns it onechecklater: aftergit mv a.md b.md,planandchecknameA.mdandb.mdasplan-file-reencoded, with the setting or without it, in the root and in the code leg.Every other line
checkprints is the same, byte for byte (table below). The sets of one blob that the NOTE takes are still asked, and their findings do not change.(A) or (B): (A), measured and read
The issue offers two fixes: (A) leave out of the rehearsal's question the twins that
plan-case-twinsalready names, or (B) report git's refusal in its own words. This PR does (A), for three reasons:_refused_finding(STORE_REFUSED, ...)already quotes git (git said fatal: will not add file alias ...). The problem is a second finding, with a bytes code, for a set thatplan-case-twinshas just named and thatexecutealready refuses under that code (_refuse_what_check_findstakes the first finding's code). Rewording or recoding it still prints two findings for one set.add -udies at the alias, so the rehearsal reads nostoredlist at all. Measured: under the setting, the CRLFnotes.txtbeside the folder twins went unnamed in 3 of 3 runs, though the same source names it without the setting. With (A), git is never asked about the twins, so it does not stop there, and the rest is asked as before.core.ignorecase. ForA.mdanda.md, each CRLF with its own blob, main's finding named both without the setting and onlya.mdunder it (git's index holds an alias under one name). On a disk that ignores case, the one checked-out file stands for two blobs, so the compare and the store would report "other bytes" with no git refusal to reword. That last case is read from the code, not measured: the bench has no writable disk that ignores case. (A) covers it, and (B) does not reach it.(A) is also the rule the rehearsal already follows for
plan-path-git-refuses: a source holding a path git refuses is asked nothing more (if refused: return []). It is the same one-finding-per-cause rule that #1253 applied toplan-attributes-reencoded.The change. A new
_case_twins_kept(plan, tree, refused)returns, for each repository, the sets thatplan-case-twinsnames and the sets left to the NOTE._case_twin_findingsnow loops over it (its output is unchanged), and_reencoded_files_findingstakes the named sets' paths and:files, so they are neverwanted, held, checked out, compared or stored again;.gitattributesamong them out of what itsgit add -ustores again (heldforSTORED). It is still checked out, so its rules still reach the files beside it. On a disk that ignores case the two folders are one and hold one.gitattributes, so one twin's rules reach the files of both; that is read, not measured, and the renameplan-case-twinsasks for comes first either way.No refusal's code or wording changes.
Measured: before and after, every line
Git 2.43, a disk that keeps case,
GIT_CONFIG_GLOBALa scratch file that is empty or holdscore.ignorecase=true. Each run used a fresh source (a_source+with_paths),plan, thencheck. Main is a worktree atf981468b. Branch is this head. Commit ids are normalised. "Identical" means stdout and stderr are byte for byte the same.Sources. Each is
a_source(README.md,.gitignore,specs/001-feature/spec.md,src/app/main.py,.github/workflows/ci.yml) plus:file-twins-own:A.mdanda.mdin the root, each with its own blob.file-twins-one-blob: the same with one blob, which is the NOTE's set.folder-twins-ownandfolder-twins-one-blob:D/xandd/xin the root.three-one-blob:AB,Abandabin the root, with one blob.file-and-folder:F/xandfin the root.folder-twins-code-leg:src/D/xandsrc/d/x.twins-spec-leg:specs/Aandspecs/a.attributes-twins:D/.gitattributesandd/.gitattributesin the root.control-crlf: a CRLFnotes.txtkept under a root* text eol=lf, with no twins.folder-twins-and-crlfandfile-twins-and-crlf: the own-blob twins pluscontrol-crlf.no-twins: nothing added.crlf-twins:A.mdanda.md, each CRLF with its own blob, under* text eol=lf.crlf-twins-in-code-leg: the same assrc/A.mdandsrc/a.md.In the last column:
plan-file-reencodedline that quotes git'sfatal: will not add file alias. Its2 finding(s)line becomes1 finding(s).notes.txt" means that the branch prints, byte for byte, what both sides print for that source with the setting unset.crlf-twinsrows (2 runs a side) show the third bullet at the top: the droppedplan-file-reencodedline named only twins, and nothing else changed.core.ignorecasef981468b: exit, findingsfile-twins-ownfile-twins-one-blobplan ok(3 of 3)plan ok(3 of 3)folder-twins-ownfolder-twins-one-blobthree-one-blobfile-and-folderfolder-twins-code-legtwins-spec-legattributes-twinscontrol-crlffolder-twins-and-crlffile-twins-and-crlfno-twinsplan ok(3 of 3)plan ok(3 of 3)file-twins-ownfile-twins-one-blobplan ok(3 of 3)plan ok(3 of 3)folder-twins-ownfolder-twins-one-blobthree-one-blobfile-and-folderfolder-twins-code-legtwins-spec-legattributes-twinscontrol-crlffolder-twins-and-crlfnotes.txt; now identical to the unset runfile-twins-and-crlfno-twinsplan ok(3 of 3)plan ok(3 of 3)crlf-twinsA.mdanda.md(and the count line)crlf-twinsa.md(and the count line)crlf-twins-in-code-legsrc/A.mdandsrc/a.md(and the count line)crlf-twins-in-code-legsrc/A.mdandsrc/a.md(and the count line)Each source gave one outcome in all of its runs on each side.
Rows
10 rows in
tests/test_adopt_check_throwaway_repos.py, under a new section#1184: one finding for one set, under core.ignorecase=true. That module already holds this code's rows: thecore.ignorecaserows of #1033, theplan-case-twinsrows of #1189 (withwith_paths,twins_kept,one_blob_twinsandconfig_with), and the rehearsal rows of #1253.tests/test_adopt_reencoded_files.pyholds #654's own byte rows, and has no twin orcore.ignorecasehelper.f981468btest_a_set_git_will_not_add_beside_its_twin_is_named_once[folder-twins]...[folder-twins-of-one-blob]...[three-of-one-blob]...[gitattributes-in-folder-twins]test_execute_refuses_such_a_set_as_its_one_findingtest_a_file_beside_the_twins_is_asked_as_without_the_settingtest_twins_git_would_store_as_other_bytes_are_the_set_alone[root]...[code-leg]test_the_twins_rules_still_reach_the_files_beside_themtest_a_set_the_note_takes_is_still_asked(control)9 fail on main. The control is new. It shows that a NOTE set (one CRLF blob,
A.mdanda.md) is stillplan-file-reencoded, so the rehearsal leaves out only the sets the finding names. A control whereplan-file-reencodedfires for real re-encoding with no twins already exists, and passes here:test_the_rehearsal_answers_in_the_sources_object_format[control-a-sha1-source], a kept CRLFnotes.txtunder* text eol=lf.The docstring of
test_check_names_each_twin_git_refuses_and_each_set_it_holdssaid that the rehearsal "refuses" the folder twins asplan-file-reencoded. It now says this happened until #1184. The module docstring gains a #1184 paragraph.Mutants
Each mutant was applied to the clone's
adopt-project.py, and the rows above were run with #1189's NOTE rows and the #1033 row. Each file was then restored from the saved copy and checked withcmp(equal each time).filesnamed_once,execute,beside,other_bytes(root and code leg),the_twins_rules_still_reach_the_files_beside_them.gitattributeskept in the store'sheldnamed_once[gitattributes-in-folder-twins],the_twins_rules_still_reach_the_files_beside_themtest_a_set_the_note_takes_is_still_asked_case_twins_keptkeeps the NOTE's sets among the namedtest_a_set_of_one_blob_is_a_note_and_not_the_finding, fourtest_only_one_blob_in_one_mode_is_left_to_the_note, the control_case_twin_findingsdropsrefusedtest_check_names_each_twin_git_refuses_and_each_set_it_holdsother_bytes[code-leg].gitattributesnot checked outthe_twins_rules_still_reach_the_files_beside_themthe_twins_rules_still_reach_the_files_beside_themGates
The first and last rows ran on head
2e020b45(fix round 1), the other three on9607ef6e:adopt-project.pyis the same file at both heads (sha256483c73a5499785a9...), and no other test module importstest_adopt_check_throwaway_repos.py. Git 2.43, Python 3.12, one pytest at a time with-rs. The host rules applied: a stubgh(exit 97) first onPATH,git-filter-repo2.47.0 next,GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=false,GIT_CONFIG_GLOBAL=/dev/nullandEMAIL=tests@openreposhape.invalid.tests/test_adopt_check_throwaway_repos.py+tests/test_adopt_root_symlinks.py(its fixture module:links_as_links,unwritable_work)plan-file-reencodedorplan-case-twins:test_adopt_collision_make_reads,test_adopt_eol_block_through_links,test_adopt_make_reads_first,test_adopt_nested_attributes,test_adopt_next_make_reads_first,test_adopt_printed_sentences,test_adopt_reencoded_files,test_adopt_symlink_target_byteseol_block_through_linksrows for a root that keeps a linktests/test_shape_doctor.py(background)tests/test_scaffold_leg_path_collisions.pytest_repo_hygiene,test_cli_reference,test_agents_md_codes,test_git_output_decoding,test_stdout_encoding,test_path_guard_hygiene,test_no_auto_maintenance,test_update_add_codes_documentedGIT_CONFIG_GLOBALwas settest_adopt_check_throwaway_repos.pyandtest_adopt_symlink_target_bytes.pyran in the first two gates. They share no fixture.Merges
git merge-tree --write-treeof this head2e020b45with main, and with each open PR that touchesadopt-project.py. The list comes fromgh pr list --state open --json number,headRefOid,files, and each head was fetched by URL asrefs/pull/<n>/headand matched to itsheadRefOid.0aef55b9a1cfbd63ce9fbee6ccd0b0f34994e35a93220f8e5cb1abdb6aa6c946adopt-project.py, but none of it is this head's (see below)1ce9c4f90ac8601439b21c07713107753146c3ca9852b989fa6d189fd40209c9tests/test_adopt_check_throwaway_repos.py, one hunk, at line 1031 (see below)cc230f2ffe67aa32791106c276f5f565d38e4e363d67662d906172b4c01e6cb0ce1dd66109eea79aorigin, asclone.defaultRemoteNamenames it, is read from that remote, soplanreads its organisation, default branch and branches where it refusedadopt-no-org, took HEAD's branch as the default and asked--tracking-branchof the source's own branches, and one with several remotes and nooriginnames them and guesses none (#1276) #1280 conflicts with main alone too (main x adopt-project.py: a source whose only remote is not namedorigin, asclone.defaultRemoteNamenames it, is read from that remote, soplanreads its organisation, default branch and branches where it refusedadopt-no-org, took HEAD's branch as the default and asked--tracking-branchof the source's own branches, and one with several remotes and nooriginnames them and guesses none (#1276) #1280: treeacb78d76, 1 file). Its three hunks are inadopt-project.py, at merged lines 3598, 3693 and 5796, in the code that reads a source's branch. They are byte for byte the same with this head and with main0aef55b9, so this head adds no conflict there.git rm -fand writes the twins the root keeps back from the index, so on a disk that ignores caseexecutekeeps a set the plan splits across repositories or drops (#1183) #1237 (headfa6d189f) merges cleanly with main0aef55b9(tree8fc8a2ad). With this head it conflicts in one hunk oftests/test_adopt_check_throwaway_repos.py, at line 1031: both PRs add a section right aftertest_on_a_disk_that_ignores_case_check_names_the_twins_too. The resolution keeps both sections, this one first.adopt-project.pymerges cleanly, and the merged file calls_case_twins_keptfrom_case_twin_findingsand from the rehearsal. adopt-project.py: the mount removes a path a case twin is at or under withgit rm -fand writes the twins the root keeps back from the index, so on a disk that ignores caseexecutekeeps a set the plan splits across repositories or drops (#1183) #1237 leavesplan-case-twins' wording and the module'stwins_keptas they are, and both PRs' new rows pass in that resolution: 49 passed, 4 skipped in 23.88s (the 4 need a disk that ignores case).Fix round 1
RV1184's review found three mutants of the rehearsal's twin exclusion that no row killed. Two rows close them.
adopt-project.pydoes not change: it is the same file at9607ef6eand at2e020b45.test_twins_git_would_store_as_other_bytes_are_the_set_alonenow runs in the root and in the code leg ([root],[code-leg]). M6 asks the twins a leg takes again, and the root-only row let it through: RV1184's live probe with it brought backplan-file-reencodedforsrc/A.mdandsrc/a.mdbesideplan-case-twins.[code-leg]kills it.test_the_twins_rules_still_reach_the_files_beside_themis new.D/andd/each hold*.txt text eol=lfand a CRLF.txt(D/n.txt,d/m.txt):checknames the two files asplan-file-reencodedafterplan-case-twins, with the setting as without it. In RV1184's live probes M7 (twin.gitattributesnot checked out) and M9 (every file in a folder that holds a named twin left out) each turned that intoplan-case-twinsalone, and the row kills both.plan-file-reencodedwhere git refused them as a file alias #1184 (10 passed, 223 deselected in 14.72s). With main'sadopt-project.pyswapped in:9 failed, 1 passed, 223 deselected in 11.51s(9 fail, and the control passes), restored withcmpequal.cmpequal andgit diff --quiet HEAD -- adopt-project.py: M1 (9 failed, 20 passed in 21.40s), M2 (2 failed, 27 passed in 24.34s), M3 (1 failed, 28 passed in 22.93s), M4 (10 failed, 19 passed in 31.86s), M5 (1 failed, 28 passed in 27.05s), M6 (1 failed, 28 passed in 24.05s), M7 (1 failed, 28 passed in 22.44s), M9 (1 failed, 28 passed in 23.47s). RV1184's M8 (refused or ()to()) is equivalent: whenrefusedis non-empty the rehearsal returns[]before the twins are used.2e020b45: the throwaway module withtest_adopt_root_symlinks.pyin one run:437 passed, 5 skipped in 388.18s; the guards:470 passed, 3 skipped in 53.50s. The other gates stand from9607ef6e(see Gates).The body also gains the opening section above and
## Out of scopebelow, and the Merges section is measured against this head, main0aef55b9and #1237 atfa6d189f.Out of scope
core.ignorecase=true, a set of one CRLF blob that the NOTE takes (A.md,a.mdunder* text eol=lf) isplan-file-reencodednaminga.mdalone, on main and on this head alike, in 2 of 2 runs each: git's index holds the set under one name. That set gets one finding, not two, so it is not adopt-project.py: the #654 rehearsal names folder case twins asplan-file-reencodedwhere git refused them as a file alias #1184's. It is a follow-up candidate (_reencoded_files_findings, adopt-project.py:14231-14235, theadd -uand thestaged(STORED)read).Closes #1184
Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code
Summary by Sourcery
Report case-twin collisions once while continuing to evaluate unrelated re-encoding findings under core.ignorecase.
Bug Fixes:
Enhancements:
Documentation:
Tests: