Your repository just moved — a GitHub org got renamed, you're migrating to GitLab, or
you've decided SSH beats typing a password every push — and now every git pull
and git push needs to land somewhere new. The command for git change
remote url is git remote set-url origin <new-url>, and it's a
one-line fix, but the mechanics around it trip people up: what it does to your cached SSH or
HTTPS credentials, what it leaves untouched (your commits, your tracking branches), and how
git remote add differs from set-url in ways that produce confusing
errors if you mix them up. This guide covers the exact command for every scenario, then goes
past the how-to into the parts most guides skip — credential fallout, fork setups with
upstream, and a full runbook for migrating a repository to a different host. If
you're also cleaning up branches while you're in your remote config, the
git delete local branch guide covers that
separately — a remote URL and a branch pointer are different objects, and this guide is
entirely about the former.
Quick Answer: Every Remote URL Command
Pick your scenario. Full explanations, verified against git-scm.com docs,
follow in the sections linked from each row.
| Situation | Command | What it touches |
|---|---|---|
| Add a brand-new remote | git remote add origin <url> | Creates a new remote entry in .git/config |
| Change an existing remote's URL | git remote set-url origin <newurl> | Rewrites the fetch (and push) URL of an existing remote |
| Set a different push-only URL | git remote set-url --push origin <newurl> | Push URL only; fetch URL unchanged |
| Add a second fetch URL to a remote | git remote set-url --add origin <url> | Adds another fetch URL — not a push URL |
| Delete one URL from a remote | git remote set-url --delete origin <url> | Removes a specific URL entry |
| Rename a remote (keeps tracking config) | git remote rename origin upstream | Remote name + branch.*.remote config + tracking refs |
| Remove a remote entirely | git remote remove origin | Remote entry + its remote-tracking refs; server untouched |
| List configured remotes | git remote -v | Read-only |
| Print one remote's URL | git remote get-url origin | Read-only |
| Inspect a remote in detail | git remote show origin | Read-only, contacts the server |
The single most common search here — "git change origin" or "git
set origin" — resolves to one command: git remote set-url origin
<new-url>. If you already have a remote named origin, that's the
entire fix. The confusion mostly comes from git remote add also existing and
looking similar — it does a related but different job, covered next. Note that
how to change remote origin github isn't a GitHub-specific procedure at
all — GitHub, GitLab, and Bitbucket all use the same local git command; nothing about it is
specific to one host's website.
What a Remote URL Change Does — and Doesn't — Touch
Backing up one step — what does git remote do in the first place? A remote
is just a named address, stored in .git/config, that fetch,
pull, and push use as a default destination so you don't have to
type a full URL every time. git remote set-url edits exactly one thing: the URL
string(s) stored under [remote "origin"]. That's it. Three things people assume
it also affects, and doesn't:
- Remote-tracking refs.
refs/remotes/origin/mainand every other cached ref underrefs/remotes/origin/stay exactly as they were. They only update on your nextfetchorpull— against the new URL. -
branch.<name>.remoteandbranch.<name>.merge. These two config keys are what makes a local branch "track" a remote branch, and they reference the remote by name (origin), never by URL. Change the URL and every existing tracking relationship keeps working unchanged —git pullandgit status's "ahead/behind" reporting both keep functioning against the new address without you touching a single branch setting. - Local commits. Nothing about your commit history, working tree, staged changes, or reflog is affected. A remote URL is metadata about where to send and fetch data — it carries no relationship to what's already committed locally. Rewriting or reversing commits is a separate job entirely: undoing the last commit for local history, or git revert once the commit has been pushed anywhere.
Here's the config file before and after, so it's concrete rather than abstract:
# Before: git remote set-url origin git@github.com:acme/app.git
[remote "origin"]
url = https://github.com/acme/app.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
# After
[remote "origin"]
url = git@github.com:acme/app.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
Only the url line moved. The [branch "main"] block — the actual
tracking relationship — is byte-for-byte identical before and after, which is exactly why
the change is safe to make mid-project without re-establishing every branch's upstream.
git remote add: Adding a New Remote
git remote add is for when no remote by that name exists yet — the first time
you're connecting a local repository to a server, or when you're git adding a
remote under a new name alongside an existing one (covered in the fork section
below). "Git add origin" is the shorthand people actually type into a search
box for this — the real command still needs the full remote keyword:
git remote add <name> <url>
# The overwhelmingly common case
git remote add origin https://github.com/user/repo.git
# Adding origin over SSH instead
git remote add origin git@github.com:user/repo.git
If a remote named origin is already configured, add refuses:
$ git remote add origin git@github.com:user/repo.git
error: remote origin already exists.
That error is the most reliable signal that you want set-url, not add
— you're not connecting a fresh repository to a remote, you're changing where an existing
one points. There's a separate, less common flag worth knowing: git remote add
--track <branch> <name> <url> pre-configures the remote's
fetch refspec to a specific branch rather than the default "all branches" glob
— useful when you want to git add repository as remote but only ever care
about one branch on it, rather than pulling in every branch the remote has.
git remote set-url: Changing an Existing Remote
This is the command underneath every phrasing of the core question — "git set url for remote," "git update remote url," "edit origin git," or "git remote set url origin" without the hyphen. All of them resolve to the same syntax, straight from git's own reference:
git remote set-url <name> <newurl> [<oldurl>]
The optional <oldurl> argument is a safety check for remotes with
multiple URLs already configured (via --add, covered below): if given, git only
rewrites the URL entries that match <oldurl> as a regex, leaving others
alone. For the ordinary single-URL case you'll use most of the time, you can omit it
entirely:
# The everyday form — git set remote origin to a new address
git remote set-url origin https://gitlab.com/user/repo.git
# Verify it landed
git remote get-url origin
# https://gitlab.com/user/repo.git Three flags, three distinct jobs — this is the part almost every other guide gets slightly wrong or skips, and it's the single most important caveat in this article:
| Flag | Effect | Common mistake |
|---|---|---|
set-url (no flag) | Replaces the fetch URL. If no separate push URL is set, this also becomes the push destination. | None — this is the default, correct behavior for a single-URL move |
set-url --push | Sets a URL used only for push; fetch keeps using the existing URL. | Assuming this is needed for a normal URL change — it's only for asymmetric setups |
set-url --add | Adds another fetch URL; the remote now has two (or more) fetch sources. | Assuming --add sets a push URL. It does not — it adds
a fetch URL. Asymmetric fetch/push needs --push instead, or in addition.
|
# Delete a specific URL from a remote that has more than one
git remote set-url --delete origin https://old-mirror.example.com/repo.git
If you're wondering "rm remote git" versus "git remove remote
origin" versus "git remove origin" — all three phrasings mean the
same operation, deleting the whole remote entry, and that's git remote remove,
not set-url --delete. set-url --delete removes one URL from a
remote that has several; it doesn't touch the remote's name or its other URLs. The full
remove-vs-rename tradeoff is covered later in this guide.
git remote add vs set-url vs rename vs remove
Four verbs, four different jobs. Confusing any two of them produces an error message that's accurate but unhelpful if you don't already know which one you needed.
| Command | Requires remote to already exist? | What it changes | Fails when |
|---|---|---|---|
git remote add <name> <url> | No — must NOT already exist | Creates a new remote entry with that name and URL | A remote with that name already exists |
git remote set-url <name> <url> | Yes | Rewrites the URL(s) of the existing remote, keeping its name | No remote with that name exists yet |
git remote rename <old> <new> | Yes (the old name) | Renames the remote; rewrites branch.*.remote; renames tracking refs | Old name doesn't exist, or new name is already taken |
git remote remove <name> (alias rm) | Yes | Deletes the remote entry and its remote-tracking refs | Name doesn't exist |
A useful mental shortcut: add and remove operate on whether the
remote exists at all; set-url and rename operate on an
existing remote's attributes — its address in one case, its name in the other.
Listing and Inspecting Remotes
Before changing anything, confirm what's actually configured. This answers "git list remotes," "git list origins," and "github list remotes" — all the same question, since remotes are a local git config concept, not something GitHub itself tracks or displays.
# Names only
git remote
# Names with URLs — separate lines for fetch and push
git remote -v
# origin https://github.com/user/repo.git (fetch)
# origin https://github.com/user/repo.git (push)
# Just one remote's URL
git remote get-url origin
# Full detail: tracking branches, ahead/behind, push/pull mapping
git remote show origin git remote show origin — the answer to "git show remote" — is
the one that actually contacts the server (unlike the others, which only read local config),
and its output is worth reading in full at least once per remote you manage:
$ git remote show origin
* remote origin
Fetch URL: git@github.com:acme/app.git
Push URL: git@github.com:acme/app.git
HEAD branch: main
Remote branches:
main tracked
dev tracked
Local branches configured for 'git pull':
main merges with remote main
Local refs configured for 'git push':
main pushes to main (up to date)
Two more read-only checks that don't require a full clone: git ls-remote
<url> lists every ref (branches and tags) on a remote without fetching any
objects, and git fetch --dry-run shows exactly what a real fetch would change
without changing anything. Both are the fastest way to confirm a URL actually resolves and
the credentials work before you commit to a full push or pull against it.
# List every branch and tag on a remote — no clone required
git ls-remote https://github.com/user/repo.git
# Preview what a fetch would do, without doing it
git fetch --dry-run origin Switching Between HTTPS and SSH
Both protocols reach the same repository; they differ in URL shape and, more importantly, in
how credentials get cached — which is where switching one for the other actually bites
people. Whether you call it "git switch origin" or a plain
git change origin url, the command is the same set-url call —
only the URL scheme changes.
| HTTPS | SSH | |
|---|---|---|
| URL shape | https://github.com/user/repo.git | git@github.com:user/repo.git or ssh://git@github.com/user/repo.git |
| Authenticates with | Personal access token (PAT) or password (deprecated on GitHub) | SSH key pair registered with the host |
| Credential cache | OS credential helper: osxkeychain (macOS), Git Credential Manager (Windows), cache/store (Linux) | SSH agent (ssh-agent) holds the decrypted key in memory per session |
| Typical firewall friction | Port 443 — rarely blocked | Port 22 — blocked on some corporate networks |
| Leak risk | A PAT embedded directly in the URL (https://TOKEN@github.com/...) gets written in plaintext into .git/config | Private key never enters the URL or the repo config |
# HTTPS to SSH
git remote set-url origin git@github.com:user/repo.git
# SSH to HTTPS
git remote set-url origin https://github.com/user/repo.git
# GitLab equivalents
git remote set-url origin git@gitlab.com:user/repo.git
git remote set-url origin https://gitlab.com/user/repo.git
# Bitbucket equivalents
git remote set-url origin git@bitbucket.org:user/repo.git
git remote set-url origin https://bitbucket.org/user/repo.git Verify SSH works before you switch to it, not after — the failure mode otherwise is a confusing permission error on your next push instead of a clear setup step:
ssh -T git@github.com
# Hi username! You've successfully authenticated, but GitHub does not
# provide shell access.
ssh -T git@gitlab.com
ssh -T git@bitbucket.org The credential trap going the other way (SSH to HTTPS, or a new HTTPS account):
credential helpers generally key off hostname, not the full remote URL. Switch from
git@github.com:old-org/repo.git to https://github.com/new-org/repo.git
and the OS credential manager may still hand git a token scoped to the old account or the
wrong organization, producing a 403 Forbidden on your very first push after
the switch — not a helpful "wrong credentials" message, just a flat rejection. Clear the
stale entry explicitly rather than guessing:
# macOS — remove a stale cached credential for github.com
git credential-osxkeychain erase
host=github.com
protocol=https
# (press Enter on an empty line to submit)
# Or, more bluntly, delete it from Keychain Access.app under "github.com"
# Windows — Git Credential Manager
git credential-manager github logout
# or: Control Panel → Credential Manager → remove the github.com entry
# Any platform — force git to re-prompt on next operation
git config --global --unset credential.helper
git fetch origin
git config --global credential.helper <previous-value>
A related, easy-to-miss leak: pasting a PAT directly into an HTTPS URL —
git remote set-url origin https://ghp_xxxx@github.com/user/repo.git — works, but
it writes the token in plaintext into .git/config, a file that isn't gitignored
by default and that anyone with filesystem access to the clone (or a leaked backup, or a
misconfigured dotfiles sync) can read. Use a credential helper instead of an embedded token so
the secret lives in an OS-managed store, not a plaintext repo file that's trivial to grep.
Asymmetric Fetch and Push URLs
Occasionally you want to fetch from one address and push to another — pulling from a fast
read-only mirror while pushing to the canonical repository, for instance. This needs
set-url --push specifically:
# Fetch stays pointed at the mirror (or whatever it currently is)
git remote set-url origin https://mirror.example.com/repo.git
# Push goes to the canonical repo instead
git remote set-url --push origin git@github.com:acme/repo.git
# Confirm the split
git remote -v
# origin https://mirror.example.com/repo.git (fetch)
# origin git@github.com:acme/repo.git (push)
Restating the caveat from earlier because it's the thing people get backwards:
set-url --add adds an additional fetch URL — running
git fetch with two fetch URLs configured queries both and merges the results.
It is not a way to set a push URL. If what you actually want is "push here, but not there,"
the flag is --push, not --add. Mixing the two up is the most common
reason someone's "asymmetric remote" setup silently pushes to the wrong place, or a push
fans out to servers it was never supposed to reach.
Fork Workflow: origin and upstream
The standard open-source contribution pattern keeps two remotes: origin is your
fork (where you push branches and open pull requests from), and upstream is the
original project (where you pull the latest changes from). Setting this up is
git remote add, not set-url, because upstream is a
second remote alongside origin, not a replacement for it:
# After cloning your fork, origin already points at your fork
git remote -v
# origin https://github.com/your-username/project.git (fetch)
# origin https://github.com/your-username/project.git (push)
# Add the original project as a second remote
git remote add upstream https://github.com/original-owner/project.git
# Confirm both are configured
git remote -v
# origin https://github.com/your-username/project.git (fetch)
# origin https://github.com/your-username/project.git (push)
# upstream https://github.com/original-owner/project.git (fetch)
# upstream https://github.com/original-owner/project.git (push)
From here, the day-to-day pattern is pulling from upstream to stay current and
pushing to origin to publish your work. When your fork's main branch has drifted
and you want it to match the original project exactly rather than merge into it,
a hard reset onto the upstream branch is the blunt
version of that sync — it discards local-only commits, so check what you have first:
# Pull the latest changes from the original project
git fetch upstream
git merge upstream/main
# Push your feature branch to your own fork
git push origin feature-branch
If you only ever want to track upstream's main branch and not its entire branch
list, git remote add --track main upstream https://github.com/original-owner/project.git
scopes the fetch refspec down to just that branch instead of the default "everything." This
is the same --track flag mentioned in the remote add
section above, applied to the specific case of a fork upstream where you don't need every
branch of a large project cluttering your remote-tracking refs.
Renaming vs Removing and Re-Adding a Remote
git remote change origin — meaning the remote's name, not its URL — is
git remote rename:
git remote rename origin upstream
# Confirm
git remote -v
# upstream https://github.com/original-owner/project.git (fetch)
# upstream https://github.com/original-owner/project.git (push)
This is the one operation in this entire guide that explicitly preserves tracking config.
Renaming origin to upstream rewrites every
branch.<name>.remote setting that referenced origin to now
reference upstream instead, and renames the corresponding
refs/remotes/origin/* refs to refs/remotes/upstream/*. Every branch
that was tracking something under the old name keeps tracking the equivalent thing under the
new name, automatically.
Compare that to the manual alternative — git remote remove origin followed by
git remote add origin <url> — which does not preserve anything:
# This sequence does NOT preserve tracking config or refs
git remote remove origin
git remote add origin https://github.com/user/repo.git
# Every branch that tracked origin/main now has a dangling upstream —
# you have to re-set it manually, branch by branch
git branch --set-upstream-to=origin/main main
git branch --set-upstream-to=origin/dev dev
Use rename when you're keeping the same remote content under a different local
name (the fork-becomes-upstream case above, or renaming a mislabeled remote). Use
remove + add only when the remote itself is genuinely gone and
being replaced by something unrelated — in which case there was no tracking config worth
preserving anyway.
Migrating a Repository to a New Host
A full git replace remote origin — sometimes typed as just "replace git remote" — for a host migration (GitHub to GitLab or Bitbucket, or self-hosted to cloud) needs more than a URL swap if you want every branch, tag, and (if used) Git LFS object to survive the move intact. Here's the runbook.
# 1. Mirror-clone the source repo — this pulls every ref, not just
# the branches you happen to have checked out locally
git clone --mirror https://github.com/acme/app.git app-mirror.git
cd app-mirror.git
# 2. Create the empty destination repo on the new host first
# (via its web UI or API — git can't create it for you)
# 3. Point the mirror's origin at the new host
git remote set-url origin https://gitlab.com/acme/app.git
# 4. Push everything — all branches and tags, in one shot
git push --mirror
# 5. If the repo uses Git LFS, push LFS objects separately —
# --mirror does not transfer them
git lfs fetch --all
git lfs push --all origin Verification, not assumption: before you update CI, teammates' remotes, or
delete anything at the old location, confirm the migration actually landed everything.
git ls-remote against both hosts gives you two plain lists of every ref and its
SHA — pull both into a
side-by-side text comparison and a
line-for-line diff will surface any branch or tag that didn't make it across, far faster than
eyeballing two long ref lists by eye:
# Old host — every ref and its SHA
git ls-remote https://github.com/acme/app.git > old-refs.txt
# New host — same, for comparison
git ls-remote https://gitlab.com/acme/app.git > new-refs.txt Paste both files' contents into Diff Checker's two panes, switch on Show Diff Only, and any ref present in one list but not the other stands out immediately — a missing tag, a branch that didn't push, or a SHA mismatch from a partial mirror push. It's a genuinely useful non-git use for a diff tool: confirming a migration by content, not by reading a push command's exit code and hoping.
Once the new host is verified complete, update every clone's remote (the next section covers doing this in bulk), update CI/CD webhooks and deploy keys on the new host, and only then archive or delete the old repository — keep it read-only for a rollback window rather than deleting immediately, since a migration you can't unwind is a migration you can't safely rush.
A second good verification pass, once teammates start cloning from the new host: diff the
[remote "origin"] block of a freshly-cloned .git/config against an
old clone's, to confirm everyone actually ended up pointed at the same new address instead of
a typo'd variant of it.
Batch-Updating Remotes Across Many Clones
A host migration or an change github remote url event (an org rename, for instance) rarely affects just one clone — it's every teammate's machine and every CI checkout. A short loop handles a directory full of repos in one pass:
# Update origin's URL across every git repo under a parent directory
for repo in ~/projects/*/; do
if [ -d "$repo/.git" ]; then
(cd "$repo" && git remote set-url origin \
"$(git remote get-url origin | sed 's/old-org/new-org/')")
fi
done
Broken down: the loop visits every subdirectory that's actually a git repo (checked via
.git's presence, so it skips plain folders), reads the current
origin URL, rewrites the old organization or hostname to the new one with
sed, and applies it with set-url. For a small, known list of repos
instead of "everything under this folder," the same idea works without the directory scan:
for repo in project-a project-b project-c; do
(cd "$repo" && git remote set-url origin \
"git@gitlab.com:acme/$repo.git")
done
Run the read-only git remote -v across the same set of directories both before
and after, and — same trick as the migration section — diff the two outputs to confirm every
repo actually changed and none got silently skipped because it wasn't a git repo, had a
different remote name, or already had the new URL and matched a no-op in your sed
pattern.
# Before and after snapshot, same set of directories
for repo in ~/projects/*/; do
[ -d "$repo/.git" ] && (cd "$repo" && echo "$repo: $(git remote get-url origin)")
done Frequently Asked Questions
What is the difference between git remote add and git remote set-url?
git remote add <name> <url> creates a brand-new remote entry
and fails with an error ("remote origin already exists") if a remote by that name is
already configured. git remote set-url <name> <newurl> changes
the URL of a remote that already exists, and fails if the remote isn't there yet. They
are not interchangeable: use add the first time you connect a repository
to a remote, and set-url every time after that when the same remote's
address changes — a host migration, an SSH-to-HTTPS switch, or a renamed GitHub
organization. Running add twice, or set-url on a name that
doesn't exist, are the two most common "command not doing anything" complaints for this
workflow.
How do I rename a Git remote?
Run git remote rename <old-name> <new-name>, for example
git remote rename origin upstream. This updates the remote's entry in
.git/config and, critically, rewrites every
branch.<name>.remote setting that pointed at the old name, so your
existing upstream tracking relationships (which local branch tracks which remote branch)
survive the rename intact. It also renames the remote-tracking refs under
refs/remotes/ to match. This is the one operation in this guide that
explicitly preserves tracking config — a manual remove-then-add sequence does not, and
you'd have to re-run git branch --set-upstream-to for every branch
afterward.
How do I remove a Git remote?
Run git remote remove <name> (the alias git remote rm
<name> does the same thing), for example git remote remove
origin. This deletes the remote's URL entries from .git/config and
deletes every remote-tracking ref under refs/remotes/<name>/ locally.
It does not touch the actual repository on the server — GitHub, GitLab, or wherever it's
hosted — and it does not touch your local branches or their commits, only the bookkeeping
that connected this local clone to that remote address. If any local branch had
branch.<name>.remote set to the removed name, that tracking link
becomes dangling until you add a new remote and re-set it.
How do I switch a Git remote from HTTPS to SSH?
Run git remote set-url origin git@github.com:user/repo.git to replace an
HTTPS URL like https://github.com/user/repo.git with its SSH equivalent.
Before switching, confirm you have an SSH key added to your GitHub, GitLab, or Bitbucket
account and that ssh -T git@github.com (or the equivalent host) returns a
successful greeting — otherwise your next fetch or push fails with a permission or
"Could not read from remote repository" error instead of the credential prompt you were
trying to avoid. Switching direction, SSH to HTTPS, uses the same command with an
https:// URL, and is common when a network blocks outbound port 22 but
allows 443.
Does changing a remote's URL affect my tracking branches or cached credentials?
No, tracking branches survive — branch.<name>.remote and
branch.<name>.merge reference the remote by name, not by URL, so
git remote set-url origin <newurl> leaves every branch's upstream
relationship intact; your next git pull or git push just goes
to the new address under the old name. Cached credentials are a separate story: if you
switch HTTPS URLs to a different host or account, a stored PAT in the OS credential
manager (macOS osxkeychain, Windows Git Credential Manager, or the
cache/store helper) can be stale, since credential helpers
usually key off the hostname, not the full URL, producing a 403 Forbidden on the first
push after the switch until you re-authenticate or clear the stale entry.