TIL: Fixing an incorrect Git rebase
Have you ever found yourself in this situation? You’ve used git rebase
to clean up commit history or integrate changes from another branch. And now your changes appear out of sync, and somehow Git thinks you’ve deleted code from the branch you initially started from.
This happens because rebasing fundamentally alters Git history rather than preserving it.
Each commit is a snapshot of the repo at a particular point in time, and the order of these snapshots matters immensely. When you rebase, you’re essentially taking your commits and replaying them on top of a different base commit, which changes their context and can create conflicts with the existing history.
Unfortunately, this fundamental concept isn’t made clear when using Git in day-to-day operations — the commands feel simple, but they’re performing complex history manipulation under the hood.
Prevention is Better Than Cure
In my opinion, the way to avoid this is to use git merge
instead of git rebase
unless you absolutely need to rewrite history. Here’s why:
- Rebases change history - they rewrite commits, which can cause confusion when collaborating
- Merges add to history - they preserve the original timeline of your work
- Merges record context - they capture exactly what changes happened during the merge process
Once you’ve rebased incorrectly, a simple merge after the fact won’t solve the problem. The changes you made happened after those latest commits, so Git still believes you’ve deleted legitimate code.
The Solution: Diff and Apply
We’re trying to pull changes from <other-branch>
into <my-branch>
. After a bad rebase, the changes from <other-branch>
are nowhere to be found. We’ll head to the terminal to fix it:
Let’s list the files that are different:
git diff HEAD..<other-branch> --name-only
The HEAD..<other-branch>
syntax means “show me what changes will take me from HEAD (my current commit) to <other-branch>
” — essentially, what’s in the other branch that I’m missing.
We only want to merge changes from a subset of files, so let’s exclude the files we don’t want to patch. (For example, let’s exclude the file uv.lock
and all markdown files)
git diff HEAD..<other-branch> --name-only -- ':(exclude)uv.lock' ':(exclude)**/*.md'
Once we’re happy with the list of files, let’s make a patch:
git diff HEAD..<other-branch> -- ':(exclude)uv.lock' ':(exclude)**/*.md' > patch.diff
Open the file patch.diff
in your editor — we can see all the changes that would occur if we apply the patch. There might be some unrelated changes, so let’s edit the patch and remove the changes we don’t want and save it.
Now we can apply the changes:
git apply patch.diff
There may be some conflicts, edit the files and resolve the conflicts one by one. Once we’re finished, we can remove the patch.diff
file.
Now git add
your changes and git commit
as you normally would.
All good now!