Understanding Remotes
A remote in Git is simply a named reference to another copy of your repository, usually one hosted on a server like GitHub. When you clone a project, Git automatically creates a remote called origin that points back to the URL you cloned from. Remotes are what let you push your local commits somewhere else and pull down commits that other people have made, without remotes, Git would only ever know about the repository on your own machine.
This lesson explains what a remote actually stores, how Git keeps track of a remote’s branches locally through remote-tracking references, and how to add, rename, inspect, and remove remotes with confidence.
Overview / How it works
Git is a distributed version control system, every clone of a repository is a full, independent repository with its own complete history. A remote is just a bookmark, stored in your repository’s configuration, that maps a short name (like origin) to a URL (an HTTPS address or an SSH address). Nothing about the remote itself is magic, it is a line in .git/config and a set of special references Git maintains for you.
When you clone a repository, Git does three things: it copies every object (blobs, trees, commits) into your local .git directory, it records the clone URL under the remote name origin, and it creates remote-tracking branches such as origin/main for every branch that existed on the server. A remote-tracking branch is a read-only local reference, it is not a branch you commit to directly, it simply records where Git last saw that branch on the remote. It only moves when you run git fetch or git pull, never as a side effect of your own commits.
This is a key mental model: your local main branch and the remote-tracking branch origin/main are two separate pointers. main moves forward every time you commit. origin/main only moves when Git talks to the server and learns that the server's main has advanced. The gap between the two is exactly what git status means when it says your branch is "ahead" or "behind" origin.
Local branches can track a remote branch
A local branch can be configured with an upstream, a link to a specific remote-tracking branch. When a branch has an upstream set, commands like git push, git pull, and git status know which remote branch to compare against without you specifying it every time. git clone sets this up automatically for the default branch, git push -u origin <branch> sets it up for any other branch the first time you push it.
You can have more than one remote
A repository is not limited to a single remote. A very common pattern when contributing to an open-source project is to have two remotes: origin, pointing at your own fork, and upstream, pointing at the original project you forked from. You push your work to origin and open a pull request against upstream, while periodically fetching upstream to stay in sync with the project's latest changes.
Syntax
The general form of the remote-management command is:
git remote <subcommand> [options] [name] [url]
The most useful subcommands:
git remote— list the names of configured remotes (no URLs).git remote -v— list remotes with their fetch and push URLs.git remote add <name> <url>— register a new remote under a short name.git remote remove <name>(orrm) — delete a remote and its remote-tracking branches.git remote rename <old> <new>— rename a remote, updating tracking configuration automatically.git remote show <name>— show detailed information: URL, tracked branches, and push/pull status.git remote get-url <name>— print the URL for a remote.git remote set-url <name> <new-url>— change the URL a remote points to (e.g. switching from HTTPS to SSH).
Examples
Example 1: Inspecting the remote created by cloning
git clone https://github.com/octocat/hello-world.git
cd hello-world
git remote -v
Output:
origin https://github.com/octocat/hello-world.git (fetch)
origin https://github.com/octocat/hello-world.git (push)
Cloning registered a single remote named origin with the same URL for both fetching and pushing. This is the default setup for almost every repository you will work with.
Example 2: Turning a local project into a GitHub-backed repository
If you started a project locally with git init and only afterward created an empty repository on GitHub, there is no remote yet, you have to add one yourself.
git init
git add README.md
git commit -m "chore: initial commit"
git remote add origin git@github.com:yourname/todo-app.git
git branch -M main
git push -u origin main
Output:
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 226 bytes | 226.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:yourname/todo-app.git
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
git remote add registers the SSH URL under the name origin. git push -u origin main pushes the branch and, because of -u (short for --set-upstream), tells Git that your local main should track origin/main from now on, so future plain git push and git pull commands on this branch know exactly where to go.
Example 3: Adding a second remote for a forked project
Suppose you forked octo-org/widget-lib on GitHub to your own account and cloned your fork. To keep up with changes in the original project, add it as a second remote:
git remote add upstream https://github.com/octo-org/widget-lib.git
git fetch upstream
git remote -v
Output:
origin https://github.com/yourname/widget-lib.git (fetch)
origin https://github.com/yourname/widget-lib.git (push)
upstream https://github.com/octo-org/widget-lib.git (fetch)
upstream https://github.com/octo-org/widget-lib.git (push)
git fetch upstream downloads the original project's commits and creates remote-tracking branches like upstream/main, without touching your working tree or your own main branch. You can then merge or rebase your local branch onto upstream/main whenever you want to catch up, for example git merge upstream/main while on your local main.
How it works step by step
When you run git remote add origin git@github.com:yourname/todo-app.git, Git simply writes a new section into .git/config:
[remote "origin"]
url = git@github.com:yourname/todo-app.git
fetch = +refs/heads/*:refs/remotes/origin/*
That fetch line is a refspec, it tells Git "whatever branches exist under refs/heads/ on the remote, mirror them locally under refs/remotes/origin/." No network call happens yet, and no objects are transferred, the remote only becomes real data once you run git fetch, git pull, or git push.
When you later run git fetch origin, Git connects to the URL, negotiates which commit objects it is missing, downloads those objects into your local object database, and then updates the remote-tracking refs (refs/remotes/origin/main, and so on) to point at the commits the server currently has. Crucially, fetch never touches your working tree or your local branches, it only updates these bookkeeping pointers. That is why git fetch is always safe to run.
git pull does that same fetch, and then immediately runs a second command, either git merge origin/main or, with --rebase, git rebase origin/main, against your current branch. That second step is the one that can produce merge conflicts or move your working tree, fetch alone cannot.
Common Mistakes
Mistake 1: Pushing before setting an upstream and being confused by the error
git switch -c feature/login-page
# ...make commits...
git push
Output:
fatal: The current branch feature/login-page has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin feature/login-page
This is not a failure, it is Git telling you it doesn't yet know which remote branch this new local branch should map to. The fix is exactly what the message suggests, run git push -u origin feature/login-page once, and every subsequent plain git push on that branch will work without the flag.
Mistake 2: Assuming git fetch updates your working files
A common beginner mistake is running git fetch and then wondering why the code in the editor hasn't changed. Fetch only updates the hidden remote-tracking branches, it deliberately leaves your working tree and local branch alone so you can review incoming changes (for example with git log main..origin/main) before deciding whether to merge them in with git merge origin/main or git pull.
Mistake 3: Adding a remote with the wrong protocol and hitting repeated password prompts
If you add a remote with an HTTPS URL but haven't configured a credential helper or Personal Access Token, Git will prompt for authentication on every push, and GitHub no longer accepts an account password there. Check the URL with git remote get-url origin, and if you have an SSH key set up on GitHub, switch to it: git remote set-url origin git@github.com:yourname/todo-app.git. From then on, pushes authenticate through your SSH key instead of a token prompt.
Best Practices
- Keep the conventional names:
originfor your primary remote,upstreamfor the project you forked from, so any collaborator can guess the setup instantly. - Run
git remote -vbefore a push to an unfamiliar repository, especially after cloning someone else's fork, to confirm you're pushing where you think you are. - Use
git push -u origin <branch>the first time you push any new branch so future pushes and pulls don't need extra arguments. - Prefer SSH remotes for repositories you push to often, it avoids repeated token prompts once your key is set up on GitHub.
- Never commit a URL containing an embedded token or password, use
https://github.com/...with a credential helper, or an SSH URL, instead ofhttps://<token>@github.com/.... - Use
git remote show originwhen you're unsure whether your local branches are ahead, behind, or diverged from their upstream counterparts.
Practice Exercises
Exercise 1: Clone any public repository, then run git remote -v and git remote show origin. Identify the fetch URL, the push URL, and which local branch tracks which remote branch.
Exercise 2: Create a new empty repository on GitHub without cloning it. Locally, run git init in a fresh folder, make one commit, then add the GitHub repository as origin and push your main branch so it appears on GitHub. Expected end state: git remote -v shows your GitHub URL, and the commit is visible on GitHub.com.
Exercise 3: In a repository with an existing origin, practice renaming it: git remote rename origin github, confirm with git remote -v, then rename it back. Notice that your branch's upstream tracking configuration follows the rename automatically.
Summary
- A remote is a named URL, stored in your repository's config, pointing to another copy of the repository, usually on GitHub.
originis just a convention, the default name Git assigns when you clone.- Remote-tracking branches like
origin/mainare local, read-only bookmarks that record where a branch was last seen on the remote, they only move onfetch,pull, orpush. - A local branch can track a remote branch as its upstream, enabling plain
git pushandgit pullwithout extra arguments. - A repository can have multiple remotes, a common pattern is
originfor your fork andupstreamfor the original project. git fetchis always safe, it never touches your working tree, onlygit merge,git rebase, orgit pullactually integrate remote changes.- Use
git remote -v,git remote show <name>, andgit remote set-urlto inspect and manage remotes with confidence.
