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.

Four verbs, one remote entry git remote add creates new entry git remote set-url rewrites the URL git remote rename changes the name git remote remove deletes the entry .git/config [remote "origin"] url = ... Same block, four different edits -- none of them touches the server.
add creates the entry, set-url rewrites its address, rename changes its name, and remove deletes it entirely -- all four edit the same remote block in .git/config, and none of them reaches the server.

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/main and every other cached ref under refs/remotes/origin/ stay exactly as they were. They only update on your next fetch or pull — against the new URL.
  • branch.<name>.remote and branch.<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 pull and git 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.

Only the url line moves .git/config [remote "origin"] url = old-url url = new-url branch.main.remote = origin UNCHANGED branch.main.merge = refs/heads/main UNCHANGED refs/remotes/origin/* stays cached until your next fetch or pull
set-url rewrites only the url line -- branch.*.remote, branch.*.merge, and the cached remote-tracking refs are byte-for-byte identical before and after.

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.

Two credential paths -- and where the 403 trap hits HTTPS SSH https://github.com/user/repo.git git@github.com:user/repo.git OS credential helper keyed by hostname ssh-agent holds decrypted key in memory same hostname, new account stale token -- 403 Forbidden key pair scoped to you no hostname-cache surprise Credential helpers cache by hostname -- switching accounts on the same host is the classic 403 trap.
HTTPS auth caches a token by hostname, so switching accounts on the same host can hand your push a stale credential; SSH keys are scoped to you regardless of URL.

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.

origin is your fork, upstream is the source origin your-username/project (fork) upstream original-owner/project forked from local repo clone of your fork git fetch upstream git push origin Pull from upstream to stay current, push to origin to publish your work.
Two remotes, two directions: fetch the latest from upstream, push your feature branches to origin -- your own fork.

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.

rename carries tracking forward. remove+add does not. git remote rename remove, then add origin upstream branch.main.remote = upstream AUTO-UPDATED origin removed origin re-added branch.main.remote = origin DANGLING needs set-upstream-to, per branch The tracking config isn't the remote's URL -- it's a separate link rename preserves and remove+add breaks.
Renaming a remote rewrites branch.*.remote and refs/remotes/* to match; a manual remove + add sequence leaves both dangling until you re-set them by hand.

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.

Host migration runbook 1. Mirror Clone clone --mirror pulls every ref 2. Point at New Host remote set-url new host URL 3. Push --mirror push --mirror all branches + tags 4. Verify ls-remote diff old vs new refs confirm nothing's missing 5. Cut Over CI update webhooks, deploy keys 6. Archive Old Repo keep read-only rollback window first Verify by diff before you cut over CI or archive anything old.
Mirror clone, repoint, push --mirror, then verify by diff before you touch CI or archive the old repository -- verification comes before cutover, not after.

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.