Staying in Sync - Keeping Up With a Moving main
Here's the thing solo Git never prepares you for: while you're heads-down on your feature branch, the rest
of the team keeps merging into main. Every hour you work, your branch drifts a little further from the
"real" main. Wait too long and merging back becomes a painful tangle of conflicts.
The fix isn't to work faster - it's to sync often, in small doses. This phase shows you how to read how
far you've drifted, fold the latest main into your branch regularly, and stay calm when two people touch
the same lines.
The team-sync cheat-card
Stuck right now? Find your situation, then read the section below.
| Situation | The calm move |
|---|---|
"Is my branch behind main?" |
git fetch, then git status / git log --oneline main..origin/main (§2) |
"Pull in the latest main" |
git switch main && git pull, then git switch <branch> && git merge main (§3) |
"Conflict while merging main in" |
Resolve the files, git add, git commit - or git merge --abort to back out (§4) |
"push rejected - fetch first" |
git pull, resolve if needed, then git push (§5) |
"What does -u / tracking even mean?" |
It links your branch to its remote twin so bare push/pull work (§1) |
1. Tracking branches - the link that makes push/pull "just work"
What it actually is. When you ran git push -u origin feature/cart-totals in Phase 1, the -u
("set upstream") created a link between your local branch and its copy on GitHub, origin/feature/cart-totals.
Git calls that copy a remote-tracking branch - your local bookmark of where the remote's version is.
What it does in real life. Because of that link, git push and git pull know where to go with no
arguments, and git status can tell you how you compare to the remote:
$ git status
On branch feature/cart-totals
Your branch is ahead of 'origin/feature/cart-totals' by 2 commits.
(use "git push" to publish your local commits)
What just happened: Git compared your branch to its remote twin and reported the gap - 2 commits it hasn't seen. "Ahead" means local work to push; "behind" means remote work to pull; "diverged" means both, and you'll need to reconcile.
2. See how far you've drifted from main
Your status line compares you to your own branch's remote - not to main. To check whether main
itself has moved on without you, first download the latest, then compare:
$ git fetch
remote: Enumerating objects: 12, done.
From github.com:acme/shop
e4f5g6h..a7b8c9d main -> origin/main
$ git log --oneline main..origin/main
a7b8c9d Add promo-code field
3f1e2d0 Fix currency rounding
What just happened: git fetch quietly downloaded everyone's new commits and updated your bookmark of
origin/main - it touched none of your files (that's what makes it safe to run anytime). The log command
then asked "what's in origin/main that my main doesn't have?" - two commits.
⏭️ Fuzzy on
fetchvspullor whatorigin/mainis? They're covered in depth in Git, Explained Like You're a Human - this guide builds on that.
3. Fold the latest main into your branch
When main has moved, bring its new commits into your feature branch so you're building on current code
and you discover any clashes now, while they're small:
$ git switch main
$ git pull # get main fully up to date locally
Updating e4f5g6h..a7b8c9d
Fast-forward
$ git switch feature/cart-totals
$ git merge main # fold those new commits into your branch
Merge made by the 'ort' strategy.
promo.js | 22 ++++++++++++++++++
1 file changed, 22 insertions(+)
What just happened: You updated local main from the remote, switched back to your feature branch, and
merged main into it - your branch now has the team's recent work plus your own, so there's little left
to reconcile when it's time to merge back. ('ort' is just Git's default merge strategy name; nothing to
configure.)
💡 Key point. Do this regularly - every morning, or whenever you notice main moved. Frequent small
merges beat one giant end-of-week merge every time; divergence is what makes conflicts hurt, and syncing
often keeps divergence tiny.
4. When folding in main causes a conflict
Sometimes a teammate changed the same lines you did, and the merge in step 3 stops:
$ git merge main
Auto-merging pricing.js
CONFLICT (content): Merge conflict in pricing.js
Automatic merge failed; fix conflicts and then commit the result.
What's actually happening. Nothing is broken. Two commits changed the same lines and Git won't guess which wins - it paused and is asking you to decide. This is the good outcome of syncing often: you're resolving one small conflict now instead of twenty later.
The calm fix. Open the conflicted file, keep the version you want, delete the <<<<<<< / ======= /
>>>>>>> marker lines, then stage and commit:
$ git add pricing.js
$ git commit # completes the merge; Git pre-fills the message
What just happened: You told Git the final text, removed the markers, and committed - finishing the merge
it had paused. Your branch is now both up to date with main and conflict-free.
The escape hatch. Not ready to deal with it? git merge --abort returns everything to exactly how it
was before you started the merge. Safe anytime you're mid-conflict and want out.
⏭️ The mechanics of reading conflict markers are walked through step-by-step in Git, Explained Like You're a Human → When It Breaks. The team difference is only this: conflicts come from other people's commits, so the cure is syncing often.
5. "Updates were rejected - fetch first"
The situation. You go to push and Git refuses:
$ git push
! [rejected] feature/cart-totals -> feature/cart-totals (fetch first)
error: failed to push some refs to 'github.com:acme/shop.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.
What's actually happening. Someone (or you, from another machine) pushed commits to this branch that you don't have yet. Git refuses to overwrite work it can see you haven't seen. This is a safety feature, not a failure - and it's the wall that protects teammates from being silently clobbered.
The calm fix. Bring their commits down, then push:
$ git pull # fetch + merge their commits into yours
$ git push
What just happened: git pull merged the remote's commits into your branch (resolve a conflict here just
like §4 if one appears), so both sides agree again - and the push goes through.
⚠️ Gotcha - never reach for --force to make a rejection go away. git push --force does silence
the error, by overwriting whatever was on the remote - including a teammate's commits, permanently. On a
shared branch that's how you ruin someone's afternoon. The straight fix for a rejection is always pull,
then push. (There's a genuinely safer cousin, --force-with-lease, used when deliberately rewriting your
own branch - that belongs with the rewriting-history material in the advanced guide.)
A note on rebase
You'll hear teammates say "just rebase onto main" as an alternative to the merge in §3. Rebase rewrites
your commits - tidier history, but a sharp tool: used wrong on a shared branch it rewrites history other
people have, exactly the kind of mess that ruins days. We cover it properly, with its safety rules, in the
advanced guide (#4: Git Disaster Recovery); until then, the merge-based sync here is completely correct and
won't bite anyone.
Recap
- Tracking branches link your local branch to its remote twin, so bare
push/pullwork andstatuscan tell you ahead/behind/diverged. git fetch+git log main..origin/mainshows how farmainhas moved without you.- Fold
maininto your branch often (merge main) - small frequent syncs keep conflicts tiny. - Conflicts are an unfinished merge waiting on you - resolve and commit, or
merge --abort. - A rejected push means pull first - never
--forcea shared branch.
You can now keep your branch healthy and current while the team moves around you. The last step is getting
your finished work into main - through a pull request.
← Phase 1: The Feature-Branch Workflow · Guide overview · Phase 3: Pull Requests & Review →
Before the quiz: without looking back, say (or jot down) the core idea of this phase in your own words.
Check your understanding 2 questions
1. To keep your feature branch from drifting painfully far from main, you should...
2. A push rejected with 'fetch first' on a shared branch should be fixed by...