Repository navigation
scaffold-project.py: the clone git submodule add makes of each leg names its remote origin, whatever clone.defaultRemoteName says, so --local-remote-dir no longer ends REFUSED a git or gh command failed ("No such remote 'origin'") after both legs are pushed (#1275) - #1277
Draft
brettheap wants to merge 1 commit into
Draft
brettheap wants to merge 1 commit into
brettheap wants to merge 1 commit into
Conversation
…names its remote `origin`, whatever `clone.defaultRemoteName` says, so `--local-remote-dir` no longer ends `REFUSED a git or gh command failed` ("No such remote 'origin'") after both legs are pushed (#1275)
`_build_assembly_root` mounts each leg with `git submodule add` and then runs
`git remote set-url origin` in the mount. `git clone` names the remote it
makes after `clone.defaultRemoteName`, so with `upstream` in the global git
config the mount had no `origin`: exit 2, `error: No such remote 'origin'`,
the root's bare repository still empty. A setting git refuses, `a..b`, ended
the run at `git submodule add` itself, exit 128.
`REMOTE_NAMED_ORIGIN = ["-c", "clone.defaultRemoteName=origin"]` goes ahead
of the verb on that call, as `adopt-project.py` does (#1261). It is the one
clone the scaffold makes. tests/test_scaffold_clone_default_remote_name.py
scaffolds with and without the setting, at one path with the commit dates
pinned, and compares every ref and object of both legs and the root, and the
remotes of the five repositories in the work directory.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewer's GuideImplementationForce the
TestsVerify the scaffold succeeds with default, alternate, and invalid clone remote-name settings, while preserving repository objects and expected remotes.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What changed
A v1.2.2 follow-up from lane 3's independent review of #1268 (#1275). It lands after the v1.2.1 tag, under Brett Heap's merge word, once reviewed and green.
With
clone.defaultRemoteNameset in the global git config,scaffold-project.py --local-remote-dirmade both legs, pushed them, and endedREFUSED a git or gh command failed(exit 2) atgit remote set-url originin the first mount, with the root's bare repository still empty._build_assembly_rootmounts each leg withgit submodule addand then addresses the clone it made asorigin;git clonenames that remote after the setting, so there was noorigin. It is the failure #1261 removed fromadopt-project.py.REMOTE_NAMED_ORIGIN = ["-c", "clone.defaultRemoteName=origin"]now goes ahead of the verb on thatgit submodule add(scaffold-project.py, the mount in_build_assembly_root). The-creaches the clonesubmodule addmakes, as #1268's reviewer measured and as the runs below show. The constant is the same spelling asadopt-project.py's; no shared helper exists (grep defaultRemoteNamefinds none outside tests), so it stays in this file. No new command or flag. The mount is the one clone this tool makes (grepof every"git"call inscaffold-project.py,scripts/shape_materialize.pyandscripts/repo_shape.pyfinds no otherclone); the table "Every other repository" says why the rest need nothing.Two files:
scaffold-project.py(+22 -3) and the newtests/test_scaffold_clone_default_remote_name.py.Measured, before and after
git 2.43.0, Python 3.12.3, a stub
gh(exit 97) first on PATH,GH_TOKEN=bogus,GIT_SSH_COMMAND=false.scaffold-project.py --org testorg --project Northwind --elected-by "Test Human" --elected-on 2026-09-02 --local-remote-dir <D>/remotes --work-dir <D>/work, the wayscaffoldedintests/conftest.pydrives it. Each run had aGIT_CONFIG_GLOBALof its own: the two maintenance-off lines, then the setting. Every run was made at ONE path (each moved aside before the next) with the commit dates pinned, because the leg templates carry the root's clone URL: another path is another set of bytes. The dumps aresymbolic-ref HEAD,for-each-refand the sortedcat-file --batch-all-objects --batch-checkof the two legs and the root, 50 lines.27397a4b5[clone]defaultRemoteName = upstream[clone]defaultRemoteName = a..bGIT_CONFIG_COUNT=1withclone.defaultRemoteName=fromcount, control globalGIT_CONFIG_PARAMETERS='clone.defaultRemoteName'='fromparams', control globalThe last lines on main:
With the change, all six runs print
Working trees are at <D>/workand nothing on stderr.The legs and root against the control (
cmpof the dumps):upstreamanda..b: the legs (the first 24 lines: HEAD, refs, objects ofNorthwind-specandNorthwind-code) arecmp-identical to the control's, since both were pushed before the mount refused. The root's bare repository holds HEAD and no ref, no object. The dump is 24 lines, the control's 50.upstream,a..b, the empty file,fromcountandfromparams: the whole dump, legs AND root, iscmp-identical to main's control (50 lines each). Those six ran with the change uncommitted over27397a4b5, so the shape commit the messages record is main's. On the committed headd07b6fc51the control, the empty file,upstreamanda..bwere run again: the four dumps and the four remotes lists arecmp-identical to one another, with nothing on stderr.<D>/work(the root, its two mounts, the two legs' own work trees):originin each;upstream: the root has none (itsgit remote add origincomes after the mounts), the spec mountupstream, the code mount never made, the two legs' work treesorigin;originin each,cmp-identical to the control. No work tree's config namesdefaultRemoteName(grep -rloverwork/andremotes/: none); the root's, both mounts' and.git/modules/*/configarediff-equal to the control's.Every other repository the scaffold makes
scaffold-project.py_build_assembly_root,git submodule add-c clone.defaultRemoteName=originseed_tree,git_init_commit(scripts/shape_materialize.py),_seed_legs,_build_assembly_root's rootgit initinitmakes no remote; each work tree is then givengit remote add origin <url>by name_create_remotesgit init --bareonly_commitgit init --bare --template=, thengit fetch --depth=1 <url> <commit>_take_held_seed(--reuse-empty-repo, a leg already holding the seed)git init,git remote add origin,git fetch origin <seed>,git resetThe held-seed path was run, not only read: on this change under
upstream, a first run exits 0; the root is made empty again and the run repeated with--reuse-empty-repo: exit 0,reused testorg/Northwind-spec (already there, holding this scaffold's seed ...)for each leg, and all five repositories nameorigin.Rows
tests/test_scaffold_clone_default_remote_name.py, 9 rows (5 test functions; three are parametrized). One module fixture scaffolds the project three times at one path: without the setting, withupstream, and witha..b.test_the_premise_a_plain_clone_names_its_remote_after_the_setting: asked of git. A plaingit clonenames its remoteupstreamunder theupstreamconfig andoriginunder the control's; undera..bit refuses. Robust to a developer whose own global config holds the setting (RV1268's N3 on 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, whose premise row failed['upstream'] == ['origin']for such a person): the control's file holds the two maintenance lines and NOTHING of the developer's config, andGIT_CONFIG_NOSYSTEM=1.test_the_scaffold_exits_0_with_or_without_the_setting[without|upstream|refused]: exit 0, noREFUSED, noNo such remote,Working trees are at.test_the_legs_and_root_are_the_bytes_a_run_without_the_setting_pushes[upstream|refused]: the dumps above, threerefs/heads/mainin the control's.test_each_repository_the_scaffold_made_has_the_remotes_it_has_without_it[upstream|refused]: the five remotes lists equal the control's, each["origin"].test_the_setting_goes_to_the_row_s_own_child_only: everyGIT_CONFIG*variable of the session, the bytes of the fileGIT_CONFIG_GLOBALnames, and git's own answer toconfig --get clone.defaultRemoteNameare what they were before the three scaffolds (a developer's own value included); each child's file is its own, and only theupstreamanda..bfiles name the setting.The setting goes to each run's own child, through its
GIT_CONFIG_GLOBAL. Nothing is exported, nothing writes the session's file.On main (
origin/main'sscaffold-project.pyin this tree):6 failed, 3 passed: theupstreamanda..bcases of the exit, bytes and remotes rows fail (No such remote 'origin';'a..b' is not a valid remote name); the premise, the control's exit and the own-child row pass, by design. Restored withcmpandgit diff --quiet HEAD.Mutants
The change is one
-con the one clone call, which runs once for each leg. Each mutant was applied to the committedscaffold-project.py, the 9 rows run, and the file restored withcmpagainst a copy andgit diff --quiet HEAD(each restore clean):-cfrom the mount'ssubmodule addupstream,a..b)=orignwithoutclone.defaultRemoteNam[]-caftersubmodulewithout(git refuses it)-cafteradd-cbeforeprotocol.file.allow(order only)-cis not a behaviourGates
Head
d07b6fc51, clean tree. git 2.43.0, Python 3.12.3, git-filter-repo 2.47.0 first on PATH after a stubgh(exit 97),GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=false,GIT_CONFIG_GLOBAL=/dev/null,EMAIL=tests@openreposhape.invalid. One pytest at a time,-rs, a basetemp per group on/tmp. The full suite was not run.tests/test_scaffold_clone_default_remote_name.py9 passed in 4.34s--local-remote-dir, one pytesttest_scaffold_clone_default_remote_name,test_scaffold_pin_and_reuse,test_scaffold_e2e,test_scaffold_reuse_seeded_legswithtest_ruleset_hint_tracking_branch(it imports that module's fixture),test_scaffold_manifest_values,test_scaffold_init_empty_values,test_scaffold_mount_ignoredwithtest_scaffold_ignored_shape_files,test_scaffold_device_nameswithtest_dry_run_parity,test_scaffold_referent_chain_links,test_scaffold_pin_collisions,test_scaffold_leg_paths,test_scaffold_free_text_values,test_scaffold_partial_pin_source921 passed in 320.07s (0:05:20), 0 skippedtests/test_scaffold_leg_path_collisions.py104 passed in 4.58stest_repo_hygiene,test_agents_md_codes,test_git_output_decoding,test_stdout_encoding,test_cli_reference,test_no_auto_maintenance343 passed, 3 skipped in 73.78s (0:01:13)GIT_CONFIG_GLOBALnaming a file that holdsdefaultRemoteName = upstream, thena..b9 passed in 3.83s,9 passed in 4.08sThe 3 skips:
test_stdout_encoding.py:285(the default it is about is Windows' own), and 2 attest_no_auto_maintenance.py:222(the session began withGIT_CONFIG_GLOBALset, which the lane sets to/dev/null; CI leaves it unset).Merges
One
gh pr list -R opensoft/openRepoShape --state open --limit 200 --json number,headRefOid,files: 46 open PRs, none with 100 files, so no list is cut. None of the 46 touchesscaffold-project.pyortests/test_scaffold_clone_default_remote_name.py, so no PR needs a merge check for these files.git merge-tree --write-tree)d07b6fc51xorigin/main27397a4b5(not moved since the branch was cut)a2e4c81e8d49, the head's own treefaa29987a(the model PR; touchesadopt-project.pyandtests/test_adopt_*, so not required)f4630d20b4edOut of scope
Read, not run, at main
27397a4b5. Nothing here is changed by this PR, and nobody has measured these under the setting. Each is for a further issue.setup-project.py:3178runsscaffold-project.py ... --no-next-steps, so step (5) of a setup run gets this change with no edit of its own.:3345-3350(step 7)git [-c ...] clone -q [--no-local] --recurse-submodules <root url> <clone>: a clone of the new root, with its two mounts. Under the setting the root's remote and the mounts' remotes are named after it. Nothing in the file addresses them asoriginright after, butbump-leg.pyandfamily.py(below) do later, andwhat_stands_atdoes at:2491.:2417-2438origin_recorded_in(git config --file <clone>/.git/config --get remote.origin.url), called at:2491: it reads the clone a rehearsal left behind. Under the setting the clone of:3345records noremote.origin.url, so the call returns "" and the answer at:2493is "It is a directory holding a.git." in place of the rehearsal sentence at:2497-2500. The path is refused either way; only the advice sentence changes.:962(self-bootstrap)git clone --quiet [--depth 1] -- <repo> <temp checkout>: nooriginis addressed in it.:1779-1802remote_url(invocation_dir, "origin")reads the PERSON'S OWN checkout, not a clone this tool made. Under the setting their remote has another name, and_org_without_origin(:1826-1841) asks for--org.scripts/family.py:7290family.py add:git [-c protocol.file.allow=always] submodule add -q --force -- <url> <path>: a clone of the member, remote named after the setting. Nothing follows it that addressesorigin(noset-urlin the file), soaddis not refused.bumplater does:_has_origin:8885(git remote get-url origin),:8936(git ls-remote --tags origin),:9468-9469(git fetch -q --prune ... origin),:8786(for-each-ref --contains <commit> refs/remotes/origin/). By:9455-9475a mount with nooriginis not fetched and the line saysit has no remote named origin.:6637,:7391git clone -q --bare -- <url> <scratch>/member.git: throwaway clones read by commit id; nooriginis addressed in them.:3324,:3337the holder:git remote add origin <url>, thenpush -u origin: by name, not a clone.:8208,:8227submodule update --init --no-fetch, in the rollback of a mount: restores from the store that is there.scripts/bump-leg.pymakes no clone. It addressesoriginof a leg's mount, which a clone made under the setting names otherwise (the clone ofsetup-project.py:3345, a memberfamily.py addmounted, or the person's owngit clone --recurse-submodulesfrom the NEXT STEPS)::1246(git remote get-url origin,_has_origin),:1101(for-each-ref --contains ... refs/remotes/origin/),:1503and:1520(remote = "origin"andgit fetch -q --prune ... <remote>). On a failed fetch:1528sayshas no remote named origin, and the refusals at:1353,:1384and:1407say it too.:2560printspush -u originfor the root.scripts/bootstrap.py(run atsetup-project.py:3367) was not read.Closes #1275
Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code
Summary by Sourcery
Make local scaffold creation resilient to custom Git remote-name configuration.
Bug Fixes:
originremote even when Git is configured with a different default remote name.Tests:
clone.defaultRemoteNamesettings, including repository contents, remotes, exit status, and configuration isolation.