Google IT Automation with Python study materials
Google IT Automation with Python study resources

The Force-Push That Ate Tuesday

Google IT Automation with Python · Put your knowledge to work in context.

All case studies

The Force-Push That Ate Tuesday

billing-retry is a shared feature branch on GitHub used by two developers on a six-person team. Developer A rebased the branch onto main, resolved conflicts, and ran git push --force origin billing-retry to update the pull request. Developer B had four local commits on the same branch that had not been pushed; B had also merged main into the branch the previous day. When B ran git pull, Git reported divergent branches. B ran git fetch origin and then git reset --hard origin/billing-retry to 'get back in sync'. The four commits disappeared from git log, and the open PR now shows only A's two commits plus rewritten history. The team has no branch protection on feature branches and no required reviews. B's reflog still exists on B's laptop; the commits were never pushed. A's force-push used plain --force, not --force-with-lease. CI had passed on the old head. The team wants to recover B's work today and prevent a repeat without banning rebases entirely.

1. Give a concrete recovery sequence for B's four commits, including what to inspect before mutating any more refs.

Read the worked answer

The commits are probably still reachable through B's reflog because reset --hard moves the branch ref but leaves the old commits in the object database and writes a reflog entry. Recovery must start by preserving that entry, not by running more destructive commands.

git reflog --date=iso
# find the entry immediately before the reset, e.g. HEAD@{1}
git branch rescue-billing HEAD@{1}
git log --oneline --graph --decorate rescue-billing -20

Inspect the graph for the four commits and the merge. Then create a new branch from the current remote head and replay only B's commits, preserving authorship:

git switch -c billing-retry-recovered origin/billing-retry
git cherry-pick <c1> <c2> <c3> <c4>
# if one of B's commits is a merge, either recreate the merge or
# cherry-pick the non-merge commits and note the merge in the PR

Push billing-retry-recovered and open a new PR against main. Do not force-push over A's rewritten branch. If the reflog entry is gone, git fsck --lost-found may still find dangling commits before garbage collection, but that is a fallback, not the first step.

2. What policy and workflow changes would prevent this specific loss without forbidding rebases entirely?

Read the worked answer

Separate the two risks: shared-branch rewrites and local-only work. A reasonable policy is:

  1. Protect shared branches from force pushes at the GitHub level. If the team wants linear history, use a short-lived personal branch plus a PR, or rebase only after announcing a freeze and tagging the old head (git tag backup/billing-retry-pre-rebase).
  2. Require --force-with-lease for any permitted force push. Plain --force overwrites even if someone else pushed in the meantime; --force-with-lease refuses when the remote moved unexpectedly.
  3. Push local commits early, even to a draft PR or a personal remote branch, so work exists outside one laptop.
  4. When the remote is rewritten, do not use git reset --hard origin/.... Fetch, then replay local work with git rebase --onto origin/billing-retry <old-base> or cherry-pick, after saving a rescue branch.
  5. Add branch protection requiring PR review and passing CI on the current head. CI passing on an old head is not evidence for the rewritten head.

The key judgement is that rebasing is fine for unpushed or single-owner branches; rewriting a branch another person is actively building on requires coordination and a recoverable backup ref.

3. How can B verify that the recovered branch contains the intended work and not a stale or partial state?

Read the worked answer

Compare the recovered branch against the old PR head and against B's rescue branch. Use git range-diff origin/billing-retry...rescue-billing to see which patches are present, missing, or modified. Run git log --oneline --author=B and git diff --stat against the old head. Re-run the test suite on the recovered branch and confirm the PR's file-level diff contains the four intended changes plus A's two commits. Finally, tag the recovered head or push it immediately so the recovery is not again only on one laptop.