Git from First PrinciplesPost 8 of 16 · 16 of 16 written

8. Remotes and collaboration

Git · 8 of 163 min readOct 2026

A remote is simply another copy of the database, with a name and a URL. Collaboration is copying objects between databases and then asking each side to move its refs.

What a remote is

git remote -v
# origin  git@github.com:you/project.git (fetch)
# origin  git@github.com:you/project.git (push)

origin is only the default name that clone gives. The definition lives in .git/config:

[remote "origin"]
    url = git@github.com:you/project.git
    fetch = +refs/heads/*:refs/remotes/origin/*

That last line, a refspec, is the whole design in one line. It says: copy their branches (refs/heads/*) into my refs/remotes/origin/*. The + allows those copies to move even when it is not a fast-forward.

Remote-tracking branches

origin/main is your local, read-only memory of where main was on the server the last time you talked to it. It is not live. It moves only when you fetch or push.

Most remote confusion disappears once you hold this: origin/main is a bookmark that fetch updates. git branch -vv shows each local branch, its upstream, and how far ahead or behind it is.

clone

git clone <url> is a bundle of steps: create an empty repository, add the remote origin, fetch everything, create a local main tracking origin/main, and check it out.

fetch: download, change nothing of yours

git fetch downloads objects you lack and updates your remote-tracking refs. It never touches your branches, index or files, so it is always safe. Add --prune to drop bookmarks for branches deleted on the server.

git fetch
git log main..origin/main     # what arrived that you don't have
git log origin/main..main     # what you have that you haven't pushed

pull: fetch, then integrate

git pull is fetch followed by a merge or a rebase of the upstream into your current branch. That hidden integration step is why pull surprises people. Recent Git refuses to pull diverged branches until you have chosen a strategy, so choose one deliberately:

git config --global pull.ff only       # only fast-forward; never surprise merges
# or
git config --global pull.rebase true   # replay your local commits on top

Many experienced users simply run git fetch, look at the log, and then merge or rebase by hand.

push: upload, then ask the server to move a ref

git push uploads your new objects and asks the server to move its branch to your commit. The server accepts only a fast-forward: your commit must already contain theirs.

If someone pushed first, you are rejected. The fix is to fetch, integrate their work, and push again. Never force-push to make a rejection go away on a shared branch.

  • git push -u origin feature pushes and sets the upstream, so later a bare git push works.
  • git push origin --delete feature deletes the branch on the server.
  • git push --force overwrites the server’s branch unconditionally and can delete other people’s commits.
  • git push --force-with-lease overwrites only if the server’s branch is still where your origin/feature says it was, meaning nobody pushed since you last fetched.
  • Add --force-if-includes as well. It closes a gap when an editor fetches in the background and silently updates your bookmark.

Upstreams

A local branch can have an upstream, usually origin/<same name>. That link is what powers “ahead 2, behind 1” in git status, a bare git pull or git push, and the @{u} shorthand.

Authentication

  • SSH keys: ssh-keygen -t ed25519 -C "you@example.com", add the public key to your host, and use git@github.com: URLs. Test with ssh -T git@github.com.
  • HTTPS: use a personal access token through a credential helper, or sign in with the GitHub CLI (gh auth login). GitHub no longer accepts account passwords for Git operations.

Forks and pull requests

These are host features, built from the pieces above.

  • A fork is a server-side clone under your account. By convention origin is your fork and upstream is the original. Stay current with git fetch upstream then git rebase upstream/main.
  • A pull request asks the host to merge one branch into another, and wraps that request in discussion, review and CI checks. The host then merges with a merge commit, a squash, or a rebase.
  • GitHub exposes each pull request as a ref, so you can test one locally: git fetch origin pull/123/head:pr-123, or gh pr checkout 123.

Check yourself

  • What changes in your repository when you run git fetch, and what never does?
  • Your push is rejected. Explain why in terms of fast-forwards, and give the right fix.
  • Why is --force-with-lease safer than --force?