02 — History Hygiene: Interactive Rebase, Bisect & PR Discipline

At a glance

After completing this lesson you can:

Time: ~45 minutes · Prerequisites: 01 — Git Object Model

Reading Companion

Why this matters in production

When a critical production bug is reported after a Friday deploy, engineers cannot afford to manually test 80 messy commits like "fix typo", "wip 2", "try again". By maintaining atomic commits and knowing how to run automated git bisect, teams isolate the exact breaking commit and author within 90 seconds.

Deep dive

1. Merge vs Rebase

2. Automated Binary Search with git bisect

If commit 1 is good and commit 100 is broken, binary search finds the first culprit in $pprox \lceil \log_2(100) ceil = 7$ steps.

Hands on

python3 phase0-foundations/02-git-and-workflow/02-history-hygiene-and-rebasing/examples/01_demo.py
python3 phase0-foundations/02-git-and-workflow/02-history-hygiene-and-rebasing/examples/02_trap.py

AI Trap & Code Review

# BAD AI-GENERATED CODE — LINEAR SCAN INSTEAD OF BISECT
def find_breaking_commit(commits: list[str], test_fn) -> str:
    # O(N) linear scan — tests all 500 commits one by one!
    for commit in commits:
        if not test_fn(commit):
            return commit
    return ""

Exercises

  1. Rung 1 (parse_rebase_todo): Parse a git-rebase-todo script string ("<action> <sha> <message>") into a list of tuples (action, sha, message). Valid actions: pick, reword, edit, squash, fixup, drop. Ignore comments (#) and empty lines. Raise ValueError on invalid actions.
  2. Rung 2 (simulate_git_bisect — Fix the AI Bug): Implement $O(\log n)$ binary search over a chronological list of commit IDs commits (commits[0] is known GOOD, commits[-1] is known BAD). Return the first bad commit ID.
  3. Rung 3 (filter_conventional_commits): Validate commit messages against the Conventional Commits specification: f"{type}({optional_scope}): {description}". Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore.
  4. Rung 4 (squash_commit_plan): Given a rebase plan, compress consecutive squash and fixup actions into their parent pick action, returning the unified list of resulting atomic commits.
  5. Rung 5 (recover_reflog_head): Parse simulated git reflog entries ("<sha> HEAD@{<n>}: <action>: <description>") to determine what commit HEAD pointed to before a specific failed command.

Wrapping up

Can you explain?

  1. Why must you never rebase a branch that multiple developers are collaborating on?
  2. How does git bisect run ./test.sh automate finding regressions in CI?
  3. What makes a commit "atomic"?

Further Reading & References

Show reference solution notes

Solution & Engineering Notes — 02 History Hygiene & Bisect

  1. Rebasing public branches: Rebasing rewrites commit hashes. If colleague Alice branched off commit A, and Bob rebases and force-pushes A into new hash A', Alice's next pull will cause duplicate, conflicting commits.
  2. git bisect run: Runs an automated test script on each candidate commit. If the script exits 0, Git marks it good; if it exits 1..127, Git marks it bad, finding regressions in CI automatically.

Self-check

Run python3 -m unittest phase0-foundations/02-git-and-workflow/02-history-hygiene-and-rebasing/test_exercises.py -v until green, then record it here. Saved to progress/rungs.json; commit it.

Confidence:

Completion edits PROGRESS.md; commit it with the lesson.