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.