Skip to main content
CodeOath
← All posts

Git95 min total · 17 parts

Git Internals and Workflows: Branching, Merging, and Rebasing

Part 17 of 17 · ~3 min

Common Mistakes Worth Remembering

Every one of these turned up somewhere in the shift-swap story, and most of them inside a single week.

  • Rebasing commits someone else had already pulled. You nearly did this on Wednesday, and the only thing that made it safe was telling Theo first. Rewriting shared commits forces a painful reconciliation on everyone downstream, because their repository still has the originals and Git cannot tell the copies are the same work.
  • Assuming HEAD means "my branch" during a rebase. It does not. HEAD is the branch you are rebasing onto, and your commit is the incoming side — the exact inverse of a merge. Resolve a rebase conflict on merge instincts and you will delete the commit you are currently replaying.
  • Reaching for git push --force instead of --force-with-lease. Plain --force would have erased Theo's review commit with no record anywhere. And --force-with-lease on its own has a real gap: a bare git fetch refreshes the lease and lets the overwrite through. Pair it with --force-if-includes.
  • Resetting a branch that is already public. git reset --hard on a pushed main undoes nothing for anybody else and sets up a force-push over a shared trunk. If it is public, git revert.
  • Forgetting -m when reverting a merge commit — and then, two weeks later, forgetting that merging the branch again will report Already up to date. and change nothing. Revert the revert.
  • Confusing git fetch with git pull. fetch downloads and touches nothing local. pull immediately merges or rebases into whatever you have checked out. One is a question; the other is a change.
  • Stashing without -u. Untracked files stay in the working tree and follow you onto every branch you visit, which produces failures that look supernatural until you notice the stray file.
  • Committing generated files or secrets because .gitignore was written after the first commit. Ignoring only applies to files Git is not already tracking, and deleting a secret in a later commit does not remove it from history — it is still in every clone, and it must be treated as compromised and rotated, not just deleted.
  • Leaving work in detached HEAD. Git warns you and prints the hash. Read the warning; it is telling you exactly what to do.
  • Letting merged branches accumulate. Deleting a merged branch deletes a label and nothing else. git branch --merged main lists what is safe to remove.
  • Treating lost work as gone for good after a reset --hard gone wrong, or a rebase that took a bad turn. git reflog almost always still has it — the one real exception is work that was never committed in the first place.
  • Resolving a conflict by taking one side wholesale without reading the other. Those markers show up precisely because each person had a legitimate reason for making a different change. On Wednesday, keeping either side alone would have shipped a bug: yours drops a timezone correctness fix, Nadia's drops the feature you spent two days on. The resolution usually has to serve both intents, not crown a winner.

That last one is the only entry on this list that Git cannot help you with, and it is the one that matters most. Everything else here is a mechanism you can learn once. Reading a conflict properly is reading code carefully, under mild time pressure, at the exact moment you most want to be finished.

If you want to see a real, if small, history with a lot of this in it, the code behind this site is public at github.com/HMankad8/screening-platform. For the data model all of this version control is ultimately protecting — joins, transactions, and the queries themselves — that is in SQL Fundamentals: Joins, NULL, Aggregate Functions, and Subqueries, and the code lab is right there if you would rather run real code than just read about it.