You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
shape-doctor.py: run from a partial clone of the standard, the shape currency row fetches what the clone lacks, and with its remote out of reach calls each copy the standard changed since the pin both, so a doctor that fetches nothing fetches, and offline says DRIFTED of copies nobody edited #683
Goal: the doctor reads nothing from a partial clone of the standard that the clone does not hold, and never reads a failed read as a copy's edit.
Filed by lane openRepoShape-3 on Brett Heap's word of 2026-10-16T12:24:41Z ("give #460 to lane 3").
What happens (measured on main d4a7fab9, git 2.43.0):
The setup.
The checkout of the standard is a git clone --no-local --filter=blob:none of it at d4a7fab9. git 2.43 writes remote.origin.promisor=true and remote.origin.partialclonefilter=blob:none, and no extensions.partialclone.
The project, Atlas, was scaffolded by scaffold-project.py at 77bb5f0f (d4a7fab9~30).
Online, the doctor fetches.shape-doctor.py --root Atlas, run from that clone, answers COMPLIANT, SHAPE BEHIND (5 upstream-changed, 0 upstream-added) with exit 1, and it FETCHES while doing so.
The objects missing at 77bb5f0f go from 1217 to 1212 (git rev-list --objects --missing=print, which never fetches).
A trace2 log shows five lazy git -c fetch.negotiationAlgorithm=noop fetch origin --no-tags --no-write-fetch-head --recurse-submodules=no --filter=blob:none --stdin.
Each fetch runs under a git show 77bb5f0f…:<source> for one of the five copies the standard changed since the pin: templates/assembly-root/scripts/validate-pins.py, validate-manifest.py, bootstrap.py, scripts/repo_shape.py and scripts/validate-repository-naming.py.
Offline, the verdict is wrong. A second such clone whose remote.origin.url leads nowhere reads the same root as DRIFTED (5 both), exit 1: 6 unchanged / 0 upstream-changed / 0 locally-modified / 5 both / 0 upstream-added.
The same five copies, which nobody edited, are called both.
The detail says the copy "is not a verbatim copy of at the pinned commit", which is false.
update-shape.py check --upstream <that clone>, which is the row's own NEXT, prints the same five both rows, exit 1.
shape-doctor.py:4073-4230, check_shape_currency. us.prepare(args) at :4109 reads each copy's blob at the pinned commit and at the target, and never asks partial_clone (:3745-3756). pin_restore does ask it, at :3555.
update-shape.py:391-396, Upstream.blob. It runs git show <rev>:<path>, which lazily fetches a missing promisor object, and returns None for ANY failed read.
update-shape.py:718-719 (_classify_row) passes that None on as "the standard carried no such source at the pinned commit". update-shape.py:774-782 (_row_verdict) then reads upstream_changed and not verbatim_at_pin as both.
update-shape.py:402-416, the docstring of open_upstream: "A PATH IS USED IN PLACE AND NOTHING IS FETCHED".
The change:
Check before reading. Where partial_clone(ctx.shape) holds, first ask, without fetching, whether the objects the comparison reads are present at the pinned commit and at the target.
git rev-list --objects --no-walk --missing=print <commit> answers that on git 2.43. Measured: it marked the five blobs missing, with no fetch in a trace2 log.
Ask it before any ls-tree. In a --filter=tree:0 clone a tree is missing too, and ls-tree would fetch it.
Where something is missing, the row is the CANNOT ANSWER environment row the doctor already gives a checkout short of the pin's commit (shape-doctor.py:4110-4124, detail environment: True, exit 3). It gets an error code of its own, and a why saying that the checkout is a partial clone and a read would fetch.
That precedent row already prints a NEXT for a person to run (git -C <shape> fetch --all). Choose this row's NEXT the same way, and say why: none, the installed openRepoShape --doctor <root>, or a full clone.
Where nothing is missing (a partial clone whose pin names its own HEAD), the row answers as it does today.
A failed read is never a copy's edit.Upstream.blob should tell "no such path at that commit" from "a read that failed", and raise on the second, so that update-shape.py check and apply refuse rather than print both.
R586's mutant R7b, which drops that alternative from the regexp, survives every test.
Measured: a repository whose config holds only [extensions] partialclone = origin gives partial_clone() True on main, and False under R7b.
Decide what promisor = false means.partial_clone matches remote.<name>.promisor whatever its value, so promisor = false counts as partial. Decide whether it should, and pin that decision. Once the row is gated on it, a false positive costs only the presence check.
GIT_NO_LAZY_FETCH=1 does not replace the presence check. It needs git 2.44, and this container has 2.43. With it set, a missing blob is just a failed read.
Pinned shape copies: none.shape-doctor.py and update-shape.py are not copied into projects.
Tests, in a new file such as tests/test_doctor_partial_clone.py, so that this PR and the one for #460 L11 do not both append to tests/test_doctor_pin_restore_merges.py:
A partial clone behind its pin. Make a --filter=blob:none clone of a standard that has moved on (uploadpack.allowFilter and FILE_PROTOCOL, as tests/test_doctor_pin_restore_merges.py:702-743 makes one), with a root pinned at an older commit.
The row is the CANNOT ANSWER row, exit 3.
rev-list --missing=print lists the same missing objects before and after the run.
The same clone with its remote out of reach gives the same row, and no both.
A partial clone pinned at its own HEAD gives the verdict a full clone gives, and fetches nothing.
partial_clone itself:
True on a full --no-local clone with [extensions]\n\tpartialclone = origin\n appended to .git/config as bytes;
False on a plain full clone;
whichever the change decides with remote.origin.promisor = false.
If Upstream.blob changes:update-shape.py check against the unreachable partial clone refuses, and prints no both.
CLAIMED — lane openRepoShape-3, session b30e8b49-f249-4668-adb1-1dbfa93c09e1@Eagle, 2026-10-16T13:05:51Z, for #683
Logged in lanes/log/openRepoShape-3.md at 2a219cc593698d61b2fc2641c97dc3bb4c1e1854.
The three reads (lane-collision-protocol Rule 1):
1. existing `CLAIMED —` comments on opensoft/openRepoShape#683:
none
2. `gh pr list --repo opensoft/openRepoShape --state all --search "#683"`, kept where the row or its body names `#683`:
none
3. `git ls-remote --heads git@github.com:opensoft/openRepoShape` branches naming `683` as a whole token:
none
Activity
brettheap commentedon Oct 16, 2026
CLAIMED — lane openRepoShape-3, session b30e8b49-f249-4668-adb1-1dbfa93c09e1@Eagle, 2026-10-16T13:05:51Z, for #683
Logged in
lanes/log/openRepoShape-3.mdat2a219cc593698d61b2fc2641c97dc3bb4c1e1854.The three reads (lane-collision-protocol Rule 1):
Merge origin/main (66b230e) into l3-683-partial-clone
README.md, the handbook and the v1.1.0 notes: the doctor's exit 3 nam…
README.md, the handbook and the v1.1.0 notes: lane 2's review of roun…
README.md, the handbook and the v1.1.0 notes: RV630's four nits, and …