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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

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

opened this

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-pack answers 308 to the same path under the new owner for a caller who may read the repository. src/http/moved.ts previously looked up name.git literally, so it never matched a moved repository. It now strips .git for the lookup and keeps it in the redirect, still going through redirectFor.
  • Strangers: someone who can't read the repository gets what a missing path answers, with no Location header. For a private repository that is 401 for an anonymous caller, not 404.
  • Push: both info/refs?service=git-receive-pack and POST git-receive-pack on the old URL return 403 with a sentence. It says the repository moved to bob/demo, nothing was pushed, and gives the git 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.ts transfers a public and a private repository and checks the 308. It then runs real git clone and git fetch through 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.md drops 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.

Closes #10130

commented

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-pack answers 308 to the same path under the new owner for a caller who may read the repository. src/http/moved.ts previously looked up name.git literally, so it never matched a moved repository. It now strips .git for the lookup and keeps it in the redirect, still going through redirectFor.
  • Strangers: someone who can't read the repository gets what a missing path answers, with no Location header. For a private repository that is 401 for an anonymous caller, not 404.
  • Push: both info/refs?service=git-receive-pack and POST git-receive-pack on the old URL return 403 with a sentence. It says the repository moved to bob/demo, nothing was pushed, and gives the git 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.ts transfers a public and a private repository and checks the 308. It then runs real git clone and git fetch through 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.md drops 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.

commented

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:, serving and done: 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.json has no top-level test command. The tests run through its check block (gate, tsc, and the sharded tests).
  • What I ran before pushing: bun x tsc --noEmit was clean, bun scripts/gate.ts printed nothing, and test/moved-git.test.ts and test/repo-transfer.test.ts passed (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.

commented

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

Approved

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs