Remotes: Off Your Laptop
This chapter gets your work off your laptop: how remotes, fetches, pushes, and tracking branches fit together.
What a Remote Is
A remote is a named URL pointing to another copy of the repository. Usually it's on a server (GitHub, GitLab, Gitea, a colleague's machine). Git doesn't care what's on the other end; if it speaks the git protocol, it's a remote.
The conventional name for "the" remote is origin. When you git clone, git sets it up for you.
git remote -v
origin git@github.com:ada/notes.git (fetch)
origin git@github.com:ada/notes.git (push)
Two entries because git tracks the fetch URL and push URL separately (usually they're the same).
clone vs init + remote add
Two ways to start a repo that syncs with a remote.
Cloning an existing remote:
git clone git@github.com:ada/notes.git
cd notes
Git creates the directory, copies the history, sets origin, and checks out the default branch.
Adding a remote to an existing local repo:
cd notes
git init
git remote add origin git@github.com:ada/notes.git
git push -u origin main
-u sets the upstream relationship, which we'll get to.
Fetch vs Pull
git fetch origin
git fetch downloads new commits, branches, and tags from the remote. It updates remote-tracking branches (like origin/main), but it does not touch your local branches or working tree.
git log --oneline --all
* 9b2f11a (HEAD -> feature/login) add login form
* 4d2c1b1 (origin/main, main) fix typo
You can see the remote state without it affecting what you're working on. Safe to run constantly.
git pull
git pull is git fetch followed by either git merge or git rebase, integrating the remote's changes into your current branch.
With pull.rebase true (set in Chapter 1), git pull does a rebase. Without it, you get a merge, which tends to create tiny "merge branch 'main' of ..." commits that clutter history.
Tracking Branches
A tracking branch is a local branch that has an upstream relationship with a remote branch. When set up, git status tells you how far ahead or behind you are; git pull and git push know what to do with no arguments.
Set the upstream:
git push -u origin feature/login
After this, git status shows something like:
On branch feature/login
Your branch is ahead of 'origin/feature/login' by 2 commits.
Inspect relationships:
git branch -vv
* feature/login 9b2f11a [origin/feature/login: ahead 2] add login form
main 4d2c1b1 [origin/main] fix typo
git push
git push sends your local commits to the matching branch on a remote.
git push # if upstream is set
git push origin feature/login # explicit
git push -u origin feature/login # set upstream and push
git push --tags # push all tags
git push origin --delete feature/login # delete the remote branch
push --force-with-lease
If you've rebased local commits that were already pushed, a plain git push fails:
! [rejected] feature/login -> feature/login (non-fast-forward)
error: failed to push some refs to ...
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.
Git is protecting you. If you force-push, you'll overwrite the remote branch and any collaborators' commits on it get lost.
Two options:
git push --force # overwrite, no questions
git push --force-with-lease # overwrite only if the remote is where you last saw it
Always prefer --force-with-lease. If someone else pushed while you were rebasing, --force-with-lease refuses and you get a chance to check. Plain --force happily destroys their work.
Never force-push main or any shared branch. It will ruin someone's afternoon.
Multiple Remotes
You can have many remotes. Common case: your fork of someone else's repo.
git clone git@github.com:you/project.git
cd project
git remote add upstream git@github.com:original/project.git
git fetch upstream
git rebase upstream/main
git push origin main
By convention, origin is your own remote; upstream is the one you forked from.
List and manipulate remotes:
git remote -v
git remote show origin # detailed info about a remote
git remote rename origin old-origin
git remote set-url origin git@new-host:ada/notes.git
git remote remove old-origin
Inspecting the Remote
git remote show origin is the most informative single command:
git remote show origin
* remote origin
Fetch URL: git@github.com:ada/notes.git
Push URL: git@github.com:ada/notes.git
HEAD branch: main
Remote branches:
main tracked
feature/login tracked
Local branches configured for 'git pull':
main merges with remote main
feature/login merges with remote feature/login
Local refs configured for 'git push':
main pushes to main (up to date)
feature/login pushes to feature/login (fast-forwardable)
Use it when you're confused about what a remote has and what's mapped to what.
Pruning Stale Remote Branches
When someone deletes a branch on the remote, your local origin/branch-name still hangs around. Prune:
git fetch --prune
# or, permanently
git config --global fetch.prune true
git branch -a becomes noticeably tidier.
SSH vs HTTPS
Pick one for a given host and stick with it.
SSH (git@github.com:user/repo.git): authenticates with an SSH key. No password prompts once the key is set up. Standard for most developers.
HTTPS (https://github.com/user/repo.git): authenticates with a token (not a password). Sometimes easier through corporate proxies. Git can cache the token in a credential helper.
Generate and register an SSH key:
ssh-keygen -t ed25519 -C "ada@example.com"
cat ~/.ssh/id_ed25519.pub
# paste into GitHub → Settings → SSH Keys
Test:
ssh -T git@github.com
# Hi ada! You've successfully authenticated...
Common Pitfalls
Pushing without an upstream. git push fails with a hint telling you to use -u. Do what it says.
Pull with an unclean working tree. git pull with uncommitted changes sometimes works and sometimes doesn't. Commit or stash first.
Force-pushing a shared branch. Never do it without coordinating. --force-with-lease reduces the blast radius but doesn't eliminate it.
Forgetting to fetch. Your view of origin/main can be hours or days old. git fetch is cheap; run it often.
Tangled credential helpers. If you're being prompted for passwords on every operation, check git config --global credential.helper and consider osxkeychain (macOS), manager (Windows), or cache (Linux).
Next Steps
Continue to 05-history.md to read the past.