I broke our main branch twice in the same month, once from a bad merge conflict resolution I rushed through late on a Friday, and once from a rebase that silently dropped a commit nobody noticed missing until a feature that had definitely worked in review turned out to be entirely absent in production. Both incidents came from genuine misunderstandings about what these two commands actually do, not carelessness alone, and fixing my understanding properly took longer than either incident itself.
Resolving a conflict in a shared configuration file, I accepted "theirs" for the whole file during a rushed resolution instead of carefully merging both sets of changes, silently discarding a colleague's unrelated change to a database timeout setting that had been sitting in that same file. That timeout setting reverting to its old, too-short value caused a spike of failed requests in production two days later that took real time to trace back to a merge conflict resolution rather than new code.
After that incident, I stopped using blanket "accept theirs" or "accept mine" resolutions for any file with more than a trivial one-line conflict, and now manually review every conflicting hunk individually, which is slower but has caught at least two other silent-overwrite situations since that I'd have otherwise missed the same way.
git checkout --conflict=diff3 -- path/to/file
# diff3 style shows the common ancestor too, not just both sides
# which made it much easier to see what each side actually changed
I'd assumed rebase was simply "replaying my commits on top of the latest main," which is technically true but I hadn't internalized what happens when a rebase encounters a conflict on a specific commit partway through a longer branch, I resolved that one conflict but didn't realize the rebase had paused, and I ran git rebase --skip instead of --continue, thinking skip meant "move past this minor issue," when it actually meant "discard this entire commit." An entire feature commit vanished from the branch history without any error message telling me something had been dropped.
# what I should have understood clearly beforehand
git rebase --continue # apply the resolved commit and move to the next
git rebase --skip # discard the current commit entirely
git rebase --abort # cancel the whole rebase, back to original state
I now run git log --oneline before and after any rebase and manually diff the commit list, specifically to catch a silently dropped commit before pushing, a habit that takes maybe fifteen seconds and would have caught the exact incident that cost us a missing feature in production for half a day before anyone noticed.
For any branch that's already been shared with or reviewed by another person, I merge rather than rebase, since rewriting shared history creates a genuinely confusing situation for anyone who already has the original commits checked out locally, their local branch and the rewritten remote branch diverge in a way that's painful to reconcile. Rebase is now something I only do on my own private, unshared feature branches before opening a pull request, never after.
For cleaning up my own messy, unshared commit history before review, five commits of "wip" and "fix typo" squashed into one coherent commit, rebase produces a genuinely more readable history for reviewers than a merge commit tangled with every intermediate false start would. I use interactive rebase specifically for this, and it's made code review noticeably faster for the people reviewing my work.
git rebase -i HEAD~5
# squash messy WIP commits into a clean, reviewable history
# before opening the pull request, never after
Both incidents came from treating these commands as things I understood well enough to move quickly through, when I actually understood them well enough to be dangerous, not well enough to be careful. The specific rules I landed on, manual conflict review instead of shortcuts, rebase only on unshared branches, and always diffing commit lists before and after a rebase, all came directly from a specific failure rather than from reading documentation in advance, which is honestly how most of my genuinely reliable habits around Git have been built.