Git for Engineers — Level by Level›04 · Remotes and pull requests

Lesson 04 of 8 · Level 2 — Working in a team

Remotes and pull requests

Work with shared repositories: remotes and tracking branches, the difference between fetch and pull, pushing safely, forks, and the pull-request workflow with reviews, status checks and protected branches that keeps the main branch always releasable.

Beginner → Practitioner
Key wordsgit remoteorigingit fetchgit pullgit pushupstreamforkpull requestmerge requestcode reviewprotected branchstatus checksSSH keys

Remotes and tracking branches

A remote is a named URL of another copy of the repository, usually origin. After fetching, Git keeps remote-tracking branches such as origin/main, read-only snapshots of where the remote's branches were last time you looked.

$ git remote -v
origin  git@github.com:acme/shop.git (fetch)
origin  git@github.com:acme/shop.git (push)
$ git fetch origin
$ git log --oneline main..origin/main      # commits on the remote you don't have yet
$ git status -sb
## main...origin/main [behind 3]
  • git fetch updates origin/* only. Always safe.
  • git pull = fetch + integrate into your current branch (merge, or rebase with pull.rebase true).
  • git push sends your commits; it's rejected if the remote has commits you don't have, so fetch, integrate, then push again.

The remote is the team's shared noticeboard. Fetching is walking over to read it; pulling is reading it and copying the new notes into your own notebook; pushing is pinning your notes up, which only works if you've read the latest notes first.

Authenticate with SSH keys (or a token)

$ ssh-keygen -t ed25519 -C "asha@example.com"
$ cat ~/.ssh/id_ed25519.pub              # add this public key to your Git server account
$ ssh -T git@github.com
Hi asha! You've successfully authenticated...

For HTTPS, use a personal access token or a credential manager, never your account password. For CI, use deploy keys or short-lived tokens scoped to one repository.

Forks

When you can't push to a repository (open source, or a platform team's repo), fork it: your own server-side copy. Push branches to your fork and open a pull request to the original. Keep your fork current:

$ git remote add upstream git@github.com:platform/charts.git
$ git fetch upstream
$ git rebase upstream/main

The pull-request workflow

  1. Branch from an up-to-date main.
  2. Commit small, focused changes; push the branch.
  3. Open a pull request (called a merge request on GitLab) with a description: what, why, how it was tested, risk and rollback.
  4. CI runs status checks: build, tests, lint, security scans, terraform plan.
  5. Review: at least one approval, from code owners where they're defined.
  6. Merge (with the repository's chosen method), delete the branch, and let the pipeline deploy.

A pull-request description template in the repository (.github/pull_request_template.md) makes step 3 automatic.

Protected branches

Set on the Git server for main (and release branches):

Rule Why
Require a pull request before merging No direct pushes to main
Require N approvals, including code owners The right people review the right files
Require passing status checks Broken code can't merge
Require branches to be up to date before merging Tests ran against what will actually land
Block force pushes and deletion History can't be rewritten or lost
Require signed commits (optional) Commits are verifiably from their authors (lesson 07)

Many servers also offer a merge queue, which tests each pull request on top of the others waiting to merge, so a busy main stays green without everyone constantly rebasing.

Try it: a full pull-request cycle

  1. Create a repository on your Git server (GitHub, GitLab, Gitea) and push a small project.
  2. Protect main: pull requests required, 1 approval, a status check (even a simple lint workflow).
  3. Create a branch, change something, push, and open a pull request with a proper description.
  4. Try git push origin main directly and read the rejection.
  5. Merge through the pull request, then git fetch --prune and delete your local branch.

Going deeper: review that scales

  • Keep pull requests under a few hundred lines; split refactors from behaviour changes.
  • Use CODEOWNERS so reviews route automatically (lesson 07).
  • Automate the boring parts (formatting, linting, plan output, security scans) so humans review design and intent.
  • Measure review turnaround; slow reviews push people towards bigger, riskier pull requests.

Recap

  • origin/main is your last-known view of the remote; fetch is safe, pull integrates, push needs you to be up to date.
  • Authenticate with SSH keys or tokens, never passwords.
  • Pull requests with description, checks and reviews; forks when you can't push.
  • Protect main: PRs, approvals, code owners, checks, no force pushes.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.