Public: anyone can read this; the crew can change it.
A git clone or fetch of a moved repository's old HTTPS URL reaches the new one #10143
Check · Review · Land · Live done — Landed and live in production.
A moved repository's old HTTPS git URL now reaches the new repository for clone and fetch, and a push there is refused. The new test and `test/repo-transfer.test.ts` pass (19 tests) and `bun x tsc --noEmit` is clean. `bun scripts/gate.ts` printed nothing, which I read as clean without checking its exit code. I didn't run the full suite, and `scripts/design-lint.ts` wasn't needed (nothing under `sr…
shipped production is serving it deploy e37ceca
A moved repository's old HTTPS git URL now reaches the new repository for clone and fetch, and a push there is refused. The new test and test/repo-transfer.test.ts pass (19 tests) and bun x tsc --noEmit is clean. bun scripts/gate.ts printed nothing, which I read as clean without checking its exit code. I didn't run the full suite, and scripts/design-lint.ts wasn't needed (nothing under src/web/ changed). I didn't use the keep_plan tool; it wasn't available in this session.
- Clone and fetch:
GET /<old-owner>/<name>.git/info/refs?service=git-upload-packanswers 308 to the same path under the new owner for a caller who may read the repository.src/http/moved.tspreviously looked upname.gitliterally, so it never matched a moved repository. It now strips.gitfor the lookup and keeps it in the redirect, still going throughredirectFor. - Strangers: someone who can't read the repository gets what a missing path answers, with no
Locationheader. For a private repository that is 401 for an anonymous caller, not 404. - Push: both
info/refs?service=git-receive-packandPOST git-receive-packon the old URL return 403 with a sentence. It says the repository moved tobob/demo, nothing was pushed, and gives thegit remote set-url origin …to run. I chose refusal over a redirect so a push is never applied under the old name. A plain redirect would have let git follow it silently. - Test:
test/moved-git.test.tstransfers a public and a private repository and checks the 308. It then runs realgit cloneandgit fetchthrough the old URL and gets the new repository's commits (including one added after the move). It also checks that a stranger gets what a missing path answers and that a push is refused. - Docs:
docs/features/repo-transfer.mddrops git remotes over HTTPS from "Not yet" and describes the redirect and the push refusal.
I did not add redirects for SSH, because SSH has no redirect. src/tui/ssh.ts looks the repository up by its current path only, so an old SSH remote fails and has to be updated. I read the code but did not run an SSH test, so the doc sentence about SSH rests on that.
To try it, move a public repository, then run git clone <forge>/<old-owner>/<name>.git.
I made no change this time, because the log you pasted doesn't show a failure caused by my pull.
- The deploy worked: the log ends with
ready:,servinganddone: https://lab--10143--forge-trevato-apps.open-platform.sh. The process started, answered in 1.3s, and the forge came up with the preview seeded. - "No tests declared, none run": that line comes from the preview deploy, which doesn't run tests.
forge.jsonhas no top-level test command. The tests run through itscheckblock (gate,tsc, and the sharded tests). - What I ran before pushing:
bun x tsc --noEmitwas clean,bun scripts/gate.tsprinted nothing, andtest/moved-git.test.tsandtest/repo-transfer.test.tspassed (19 tests). I did not run the full suite.
If the forge's check run on my push failed, its log didn't reach me. Send that log and I'll fix whatever it names.
A git clone or fetch of a moved repository's old HTTPS URL now reaches the new repository, and a push to the old URL is refused with a message naming the new path. This push only adds a commit message. I haven't re-run the gate or the tests, and I haven't seen the forge's result for this push.
The last check failed on the gate rule that asks for a "Try:" line in a commit message when src/http/moved.ts changes. I added an empty commit carrying that line and left the code as it was.
Try: clone https://lab--10143--forge-trevato-apps.open-platform.sh/<old-owner>/<old-name>.git for a repository that has been moved. The clone should arrive at the new path. A push to the same old URL should be refused naming the new path.
🤖 Generated with Claude Code
Clean. Old HTTPS git URLs now 308 to the new owner through redirectFor for readers (the .git suffix is kept), pushes get a 403 sentence naming the new path and the set-url command, and strangers get exactly what a missing path answers. Real-git clone and fetch are tested, including a commit made after the move. No new dependency, no secrets, and the output is plain text built from stored names. Two non-blocking notes: the .git strip also applies to non-git page paths (harmless redirect), and url.origin in the push message is only right if the server sees the public origin.
A moved repository's old HTTPS git URL now reaches the new repository for clone and fetch, and a push there is refused. The new test and
test/repo-transfer.test.tspass (19 tests) andbun x tsc --noEmitis clean.bun scripts/gate.tsprinted nothing, which I read as clean without checking its exit code. I didn't run the full suite, andscripts/design-lint.tswasn't needed (nothing undersrc/web/changed). I didn't use thekeep_plantool; it wasn't available in this session.GET /<old-owner>/<name>.git/info/refs?service=git-upload-packanswers 308 to the same path under the new owner for a caller who may read the repository.src/http/moved.tspreviously looked upname.gitliterally, so it never matched a moved repository. It now strips.gitfor the lookup and keeps it in the redirect, still going throughredirectFor.Locationheader. For a private repository that is 401 for an anonymous caller, not 404.info/refs?service=git-receive-packandPOST git-receive-packon the old URL return 403 with a sentence. It says the repository moved tobob/demo, nothing was pushed, and gives thegit remote set-url origin …to run. I chose refusal over a redirect so a push is never applied under the old name. A plain redirect would have let git follow it silently.test/moved-git.test.tstransfers a public and a private repository and checks the 308. It then runs realgit cloneandgit fetchthrough the old URL and gets the new repository's commits (including one added after the move). It also checks that a stranger gets what a missing path answers and that a push is refused.docs/features/repo-transfer.mddrops git remotes over HTTPS from "Not yet" and describes the redirect and the push refusal.I did not add redirects for SSH, because SSH has no redirect.
src/tui/ssh.tslooks the repository up by its current path only, so an old SSH remote fails and has to be updated. I read the code but did not run an SSH test, so the doc sentence about SSH rests on that.To try it, move a public repository, then run
git clone <forge>/<old-owner>/<name>.git.Closes #10130