Stack: Git on Linux · Project: TicketFlow Status: Published — taught after Lesson 05 is corrected Prerequisite: Lesson 05 — Linux and the terminal
Objectives
By the end of this lesson you will be able to:
- Write commits that tell the intention (and use
git add -pto split them properly). - Choose between merge and rebase with judgement and resolve conflicts without panic.
- Recover from any disaster with
reflog(nothing is truly lost). - Find the commit that broke it with
git bisect. - Work with a team flow (GitHub Flow / trunk-based) with reviewable PRs.
1. The commit is the unit of intention
A commit is not "saving": it is telling one step of the project. The message rule (conventional commits, the de facto convention):
<type>: short description in imperative
optional body: the why, not the what (the diff shows the what)
refs: #123Types you will use: feat, fix, refactor, test, docs, chore, perf. Real TicketFlow examples:
feat(reservations): lock seats with select_for_update when reserving
The previous check-then-act allowed a race between CHECK and ACT.
Now the seat row is locked inside the transaction, and the unique
constraint stays as the safety net.
refs: #42fix(payments): respond 409 when the gateway retries a confirmed payment
The duplicated webhook created a second Payment for the same reservation.
With idempotency_key as unique, the retry returns the original result
instead of failing.And the tool that splits the "all together" into clean commits: git add -p (patch mode) — you decide hunk by hunk what goes in. If one commit mixes "refactor + fix + feature", none of them can be reverted alone: the disaster arrives at revert time.
2. Branches: cheap, disposable, purposeful
git switch -c feat/reservation-with-locking # create and switch (the modern checkout)
git branch -m old-name new-name # rename
git branch -d finished-feature # delete merged (-D if you force it)One change = one branch = one PR. If your branch lives more than two days without a PR, it is rotting: the merge gets complicated, the conflict grows and nobody does the review. Short branch, small PR, fast feedback.
3. Merge vs rebase: the decision with judgement
Both integrate changes; they differ in the history they leave:
git merge | git rebase | |
|---|---|---|
| History | faithful: branching and joining | linear: "as if it was always like this" |
| Commits | adds a merge commit | rewritten with new hashes |
| When | integrating into the main branch (honest history) | updating YOUR working branch (clean history) |
The team's golden rule: rebase your own branches onto main; merge (or squash) when integrating into main. And the law that is never broken: never rebase what is already shared (commits others already have) — you rewrite other people's history and the team bills you for it.
# update your branch with main's new commits (the right rebase)
git switch feat/reservation-with-locking
git fetch origin
git rebase origin/main
# if the rebase goes sideways: aborting is free and safe
git rebase --abort4. Conflicts: mechanics, not drama
A conflict says "two branches touched the same lines and Git cannot guess". The process:
git status # lists the conflicted files
# edit: choose between <<<<<<< yours / ======= / >>>>>>> theirs
git add resolved_file
git rebase --continue # or git commit if it was a mergeAvoidable conflicts: small PRs, short branches, and frequent git fetch + rebase (the fresher your branch, the less accumulated conflict). Structural conflicts (two people changing the same function): that is a team-communication signal, not a Git problem.
5. Reflog: the safety net nobody knows
git reflog records every movement of HEAD (commits, resets, rebases) for ~90 days. The "deleted" commit almost always lives here:
git reflog
# a1b2c3d HEAD@{0}: reset: moving to origin/main
# e4f5g6h HEAD@{1}: commit: feat: lock seats...
git reset --hard HEAD@{1} # back to the "lost" commitBefore any destructive reset --hard: git branch backup-before (a branch costs nothing). Reflog turns "I lost my work" into "I lost 30 seconds".
6. Bisect: binary search for bugs
"It worked last week." Git finds it by itself:
git bisect start
git bisect bad # the current commit is broken
git bisect good v1.2.0 # this tag worked
# Git checks out the middle commit; you test:
# if it fails: git bisect bad
# if it works: git bisect good
# ...in ~log2(n) steps it points at the culprit commit
git bisect resetWith 1,000 commits that is ~10 tests. And it automates: git bisect run make test runs your tests for you.
7. Team flow: GitHub Flow (the one we use in this course)
The most common one in small/medium teams:
main (always deployable)
└── feat/short-branch → PR → review → green CI → merge (squash) → deployThe rules that make it work:
- main always deployable: if it breaks, revert is the first tool (not "hotfix in place").
- Small PR (< 400 lines): real review happens there; nobody reads a 3,000-line PR.
- Mandatory CI before merge: tests, lint (Lesson 41 builds it).
- Squash on merge: the branch may hold "wip, fix typo, again"; on main there is one commit with intention (section 1).
- Delete branch after merge: history lives on main; dead branches confuse.
Key: the flow is not religion; it is the agreement on how the repository's history reflects the work. The team decides (GitHub Flow, trunk-based with feature flags, GitFlow for packaged releases) and writes it down — exactly what we did with the course ADRs.
Self-assessment (answer me in the chat)
- Why is a commit mixing refactor and fix an operational problem (revert, bisect, review)? Split it with
git add -p: which hunk would go into each commit? - Your teammate rebased a branch you had already cloned and now
pullgives you duplicated history. What happened and how is it avoided in a team? - What is the real difference between
git merge mainandgit rebase mainfrom your feature branch? Which would you pick for this project's PRs and why? - A bug appears in production; the last good tag is 300 commits back. Describe the exact
bisectflow to locate it. - Why does "main always deployable" demand revert as the first response rather than hotfix? Which squash/merge policy makes that revert viable?
Continue with the exercises. The solutions only after trying it yourself.