token_for_url (src/git/auth.rs:16) picks the forge with a bare substring test against the whole URL:
if url.contains("gitlab") {
if let Ok(token) = std::env::var("GITLAB_TOKEN") { ... }
} else if let Ok(token) = std::env::var("GITHUB_TOKEN") { ... }
contains is applied to the full URL, not the host, so the owner and repo name are in scope. A remote like:
https://github.com/acme/gitlab-migration-tool
https://github.com/gitlab-ce-mirrors/whatever
takes the GitLab branch, finds no GITLAB_TOKEN, and returns None. configure_git_command then attaches no credential helper at all and the push fails with an authentication error, despite GITHUB_TOKEN being set correctly. The failure message points at credentials, not at the repo name, so it is unpleasant to diagnose.
The mirror case is milder: with FERRFLOW_TOKEN set, the same test only picks the username (oauth2 vs x-access-token), which GitHub tolerates for basic-auth-over-HTTPS in most cases — so it usually works and hides the bug.
src/forge/mod.rs already does this properly: detect_forge_from_url + extract_host parse the URL and match on the host. git/auth.rs is the only place that reimplements detection by substring.
Fix
Reuse the existing detection. token_for_url should call forge::detect_forge_from_url(url) and map ForgeKind to the username and env var, falling back to the GitHub shape when detection returns None (self-hosted instances that neither module recognises). That also picks up Gitea/Forgejo and Bitbucket, which token_for_url currently has no branch for at all — a Gitea push today gets x-access-token + GITHUB_TOKEN, which is wrong on both counts.
While in there: the FERRFLOW_TOKEN branch does not filter empty values, unlike forge::resolve_token, so FERRFLOW_TOKEN="" shadows a perfectly good GITHUB_TOKEN.
Tests
token_for_url("https://github.com/acme/gitlab-migration-tool") with only GITHUB_TOKEN set returns the GitHub pair.
token_for_url("https://gitlab.com/acme/repo") with only GITLAB_TOKEN set returns ("oauth2", …).
- A Gitea remote with
GITEA_TOKEN set returns a token rather than falling through to the GitHub branch.
FERRFLOW_TOKEN="" falls through to the forge-specific var.
token_for_url(src/git/auth.rs:16) picks the forge with a bare substring test against the whole URL:containsis applied to the full URL, not the host, so the owner and repo name are in scope. A remote like:https://github.com/acme/gitlab-migration-toolhttps://github.com/gitlab-ce-mirrors/whatevertakes the GitLab branch, finds no
GITLAB_TOKEN, and returnsNone.configure_git_commandthen attaches no credential helper at all and the push fails with an authentication error, despiteGITHUB_TOKENbeing set correctly. The failure message points at credentials, not at the repo name, so it is unpleasant to diagnose.The mirror case is milder: with
FERRFLOW_TOKENset, the same test only picks the username (oauth2vsx-access-token), which GitHub tolerates for basic-auth-over-HTTPS in most cases — so it usually works and hides the bug.src/forge/mod.rsalready does this properly:detect_forge_from_url+extract_hostparse the URL and match on the host.git/auth.rsis the only place that reimplements detection by substring.Fix
Reuse the existing detection.
token_for_urlshould callforge::detect_forge_from_url(url)and mapForgeKindto the username and env var, falling back to the GitHub shape when detection returnsNone(self-hosted instances that neither module recognises). That also picks up Gitea/Forgejo and Bitbucket, whichtoken_for_urlcurrently has no branch for at all — a Gitea push today getsx-access-token+GITHUB_TOKEN, which is wrong on both counts.While in there: the
FERRFLOW_TOKENbranch does not filter empty values, unlikeforge::resolve_token, soFERRFLOW_TOKEN=""shadows a perfectly goodGITHUB_TOKEN.Tests
token_for_url("https://github.com/acme/gitlab-migration-tool")with onlyGITHUB_TOKENset returns the GitHub pair.token_for_url("https://gitlab.com/acme/repo")with onlyGITLAB_TOKENset returns("oauth2", …).GITEA_TOKENset returns a token rather than falling through to the GitHub branch.FERRFLOW_TOKEN=""falls through to the forge-specific var.