Team-Scale Collaboration

Course 2 · Ch 9 · Scenario
Multiple Users on the Same Branch — Team Scale
Diverged history, why force-push is dangerous, and using branch protection rules to prevent disasters before they happen

Course 1, Chapter 10 covered two people on the same branch as a calm, easy case. At team scale — several people, more frequent pushes, more pressure to move fast — the same situations get higher-stakes, and one extra tool (the force push) enters the picture as something genuinely dangerous if misused. This chapter covers what changes at that scale, and how branch protection rules prevent the worst outcomes structurally rather than relying on everyone remembering the rules.

Diverged History — When Pull Alone Isn't a Clean Fast-Forward

With more people pushing more often, it's common for your local branch and the remote to have genuinely different commits on both sides — not just "they're ahead," but both have commits the other doesn't.

shared their push your local commit
Both sides built on the same shared point, but neither is a superset of the other — pull needs to combine them, not just fast-forward
$ git pull
Auto-merging shared.js
Merge made by the 'ort' strategy.

This is still entirely normal and resolved the same way as Course 1, Chapter 10 — git creates a merge commit (or you hit a real conflict, resolved per Chapter 3). What changes at team scale is just how often this happens, and that's exactly why the next section matters more.

Why Force-Push Is Genuinely Dangerous

git push --force overrides git's normal protection (the "fetch first" rejection from Course 1, Chapter 10) and pushes your local branch's history regardless of what's on the remote — even if that means erasing commits the remote has that you don't.

⚠️ What it actually does
Overwrites the remote branch to exactly match your local one. Any commit on the remote that isn't also in your local history simply disappears from that branch — for everyone.
🔥 When it goes wrong
You rebased locally (Chapter 2), didn't realize a teammate had pushed new commits in the meantime, and force-pushed — silently deleting their work from the shared branch.
✅ When it's legitimately needed
After rebasing or amending a commit you're CERTAIN no one else has pulled yet — typically your own personal feature branch, never a shared branch like main.
If you must force-push, use --force-with-lease instead of --force
git push --force-with-lease adds a safety check: it refuses to force-push if the remote has changed since you last fetched — meaning it won't silently overwrite a teammate's commit you simply haven't seen yet. It's not foolproof, but it converts the most common accidental-disaster scenario into an error message instead of silent data loss. Treat plain --force as something to avoid by default.

If someone's work was already overwritten

If a force-push already happened and commits appear to be missing, the situation is usually recoverable — the person who made the lost commits likely still has them locally (their own copy was never touched), and can simply push again. Git's reflog (full coverage in Course 3, Chapter 3) can also often recover commits even after a damaging force-push, as long as no one has run aggressive garbage collection in the meantime.

Branch Protection Rules — Preventing This Structurally

Rather than relying on everyone remembering "don't force-push main," GitHub lets you enforce rules at the repository level — Settings → Branches → Add branch protection rule.

🔒 Require a pull request before merging
Disables direct pushes to the protected branch entirely — every change must go through a reviewed PR (Course 1, Ch7), no exceptions, even for admins if configured that way.
✅ Require status checks to pass
Ties directly into Chapter 8's CI workflows — a failing test run physically blocks the merge button until fixed.
👀 Require approving reviews
Specify a minimum number of approvals (commonly one or two) before a PR becomes mergeable — formalises the review culture from Chapter 7.
🚫 Block force pushes
Disables force-pushing to the protected branch entirely, regardless of who's pushing — removes the danger covered above as a possibility altogether.
📜 Require linear history
Forces every merge into the branch to be a rebase or squash, never a merge commit — keeps history reading top-to-bottom with no branching visual noise, if that's the team's preference.
Set this up on day one, not after the first incident
Branch protection costs nothing to configure and prevents an entire category of "someone accidentally force-pushed main" incidents from ever being possible — much cheaper than the alternative of recovering from one after the fact. On any repository more than one person touches, enabling at minimum "block force pushes" and "require a PR before merging" on the main branch is a sensible default from the very start.

Communication — The Non-Technical Half of This Problem

  • Announce before doing anything history-rewriting on a shared branch. A quick "about to rebase feature/checkout, please pull when I'm done" prevents most surprises entirely.
  • Pull before starting work, not just before pushing (repeated from Course 1, Chapter 10, worth repeating at team scale) — smaller, more frequent catch-ups produce smaller surprises.
  • If something looks wrong on a shared branch, ask before acting. A quick message in a team channel beats a guessed fix that makes a confusing situation worse.

Chapter 9 Quick Reference

  • Diverged history at team scale resolves the same way as Course 1's two-person scenario — just happens more often
  • git push --force can silently delete a teammate's commits from a shared branch — genuinely dangerous
  • git push --force-with-lease — safer alternative, refuses if the remote has unseen changes
  • If commits go missing: the original author likely still has them locally; git reflog can often recover them too
  • Branch protection rules: require PR before merging, require status checks, require reviews, block force pushes, require linear history
  • Set up protection early — much cheaper than recovering from the incident it would have prevented
  • Communicate before any history-rewriting operation on a branch others use
  • Next chapter (final, Scenario): a repo gone messy after a reorg — broken gitlinks and submodules without .gitmodules