Remotes, Push/Pull & the GitHub Flow¶
Everything so far happens on your own machine. A remote is how your local history connects to a shared copy — usually on GitHub — and the GitHub flow is the team-level pattern built on top of push/pull that turns individual commits into reviewed, shippable changes.
TL;DR — in 30 seconds:
- A remote (conventionally named
origin) is just a URL Git remembers, pointing at another copy of the repository —git push/git pullmove commits to and from it. - The GitHub flow: branch off
main→ commit your change → open a pull request → get it reviewed → merge intomain.mainis always deployable. - A pull request (PR) isn't a Git concept at all — it's GitHub's review wrapper around a branch, giving the team a place to comment, request changes, and gate the merge.
1. Remotes: a named pointer to another copy¶
A remote is a bookmark for another repository's URL, so you don't have to type it out every time. When
you clone a repository, Git automatically names the source origin.
| Command | What it does |
|---|---|
git remote -v |
list configured remotes and their URLs |
git fetch origin |
download the remote's latest commits/branches, without touching your working files |
git pull origin main |
fetch plus merge (or rebase, if configured) the result into your current branch |
git push origin feature |
upload your local feature branch's commits to the remote |
fetch and pull are often confused: fetch only downloads — your local branches don't move, so it's
always safe to run. pull downloads and integrates the result into your current branch immediately —
that's the step that can produce a merge commit or a conflict.
flowchart LR
Local["your local repo"] -->|"git push"| Remote["origin (GitHub)"]
Remote -->|"git fetch — download only"| Local
Remote -->|"git pull — download + merge"| Local
2. The GitHub flow¶
The GitHub flow is the standard pattern for shipping a change on a team: keep main always in a
working, deployable state, and do every change on a short-lived branch that goes through review before it
joins main.
flowchart LR
A["Branch off main<br/>(feature/topic)"] --> B["Commit your change(s)"]
B --> C["Push the branch<br/>and open a PR"]
C --> D["Review<br/>(comments, requested changes)"]
D -->|changes requested| B
D -->|approved| E["Merge into main"]
- Branch off
mainwith a name that says what it's for (feature/topic,fix/topic). - Commit your change(s) on that branch — small, focused commits with clear messages, same discipline as any other Git work.
- Push the branch and open a pull request — this is the point where the change becomes visible and reviewable to the rest of the team, even before it's finished (a draft PR signals "not ready yet, but here's the direction").
- Review — teammates read the diff, comment, and either request changes (back to step 2, same branch) or approve.
- Merge into
mainonce approved — the branch's commits joinmain, typically followed by deleting the now-merged branch.
main staying deployable at every point is the whole reason for the flow: nobody is ever blocked by an
in-progress change sitting on main, because in-progress work only ever lives on a branch.
3. Pull requests & reviews¶
A pull request is not a Git object — Git itself has no idea what a PR is. It's a feature GitHub (and
similar platforms) layers on top of a pushed branch: a page that shows the branch's diff against main,
collects comments and review approvals, and provides the button that actually performs the merge.
- Review comments can be general (on the PR as a whole) or inline (attached to a specific line of the diff) — inline comments are how a reviewer points at the exact spot a change is needed.
- "Request changes" vs "approve" — a review state, not just a comment; some repositories are configured to require an approval before the merge button is even available.
- Pushing new commits to the same branch updates the same, already-open PR automatically — there's no need to open a new one; the diff and review thread just grow.
4. .gitignore — telling Git what to never track¶
Not everything in a project directory belongs in version control — build output, dependency folders,
editor config, secrets. A .gitignore file lists patterns Git should never track or offer to add, so
git status doesn't drown in noise and nobody accidentally commits something that shouldn't be shared.
# example .gitignore
node_modules/
*.log
.env
dist/
.gitignore only stops Git from tracking files it doesn't already know about — if a file is already
committed, adding it to .gitignore afterward doesn't remove it from history; it has to be explicitly
untracked (git rm --cached <file>) first.
Common gotchas
- Using
git pullout of habit and being surprised by a merge commit or conflict. Fix:git fetchfirst to see what changed, then decide whether to merge or rebase it in —pullcollapses that decision into one step and picks for you. - Committing a secret or credential, then just adding it to
.gitignore. Fix:.gitignorehas no effect on history that already exists — a committed secret is still in the repo's history and must be treated as compromised (rotate it), not just hidden going forward. - Opening a PR only when the work is "finished." Fix: a draft PR opened early gets the team visibility and early feedback before you've invested in the wrong direction.
- Force-pushing to a shared branch to "clean up" commits after a review has started. Fix: once teammates are reviewing or have pulled a branch, treat it like a shared/public branch — see the merge-vs-rebase rule on the previous page.
Check yourself — what's the actual difference between git fetch and git pull?
fetch only downloads the remote's latest commits/branches — your local branches don't move, so it's
always safe. pull does a fetch and then immediately merges (or rebases) the result into your
current branch — that's the step that can produce a merge commit or a conflict.
Check yourself — you added a build-output folder to .gitignore, but it still shows up in git status and diffs. Why?
It was already tracked (committed) before the .gitignore rule existed. .gitignore only prevents Git
from tracking new files matching the pattern — an already-tracked file has to be explicitly removed
from tracking with git rm --cached <path> (it stays on disk, Git just stops watching it).
5. How this connects¶
- This course's previous page,
02-branching-and-merging.md— the GitHub flow's "keep your branch current withmain" step is exactly where you apply the merge-vs-rebase decision from that page, before a PR is reviewed. - This course's next page,
04-undoing-changes-safely.md— once a change is pushed and reviewed, the safe way to undo something that already landed onmain(a revert, not a reset) matters a lot more than it does on a private, unpushed branch. - CI/CD (module 15, CI/CD) — most CI pipelines trigger on exactly the events this page describes: a push to a branch, or a PR being opened/updated — the GitHub flow is why CI runs when it runs.
- Module 00 (Setup & ways of working) — its "first PR" rite is steps 1–2 of the GitHub flow described here; this page is what happens next, once real teammates are reviewing.
You can defend this when you can explain the difference between fetch and pull without checking
docs, and can walk through all five steps of the GitHub flow unprompted.