I do still find myself tripping up on whether the next appropriate step is to make a commit or continue the rebase, as that is dependent on if you're in a merge conflict or just editing a commit. But even when I get that wrong, and collapse two commits together, abort saves the day.
I think I know what happens in principle (each step of a rebase does a merge of the commit from the list with the last rebased commit) but it's still often hard to understand which side of the conflict represents what, and what would be the desired outcome for that step in the history. So I tend to abort the rebase when I get a conflict and see if I can plan it differently so I can get it through without conflicts.
What I found good to know is that only actions that modify the actual changes in the history - reordering, deleting or editing commits - can cause conflicts. The other actions - reword, fixup or squash - only modify how the changes are grouped into commits and cannot cause conflicts.
So I usually do an interactive rebase in several passes: First a rebase that only reorders commits (or rarely does deletes or edits) and where I deal with the resulting conflicts, then a second pass for fixup or squash operations that can then use the reordered history from the first pass.
Rewords are both harmless and don't require any particular ordering, so can be done in any pass.
The biggest mistake most sources make when teaching about history rewriting is omitting these two lessons first:
- it’s easy to lose uncommitted data in Git and hard to lose committed data
- given this, learn how to use ‘git reflog’ to see what you’ve done and how to undo/redo it (as this post recommends but, even then, a little too late)
If you just get in the habit of constant committing and checking reflog: you’ll never lose data.
(Related lesson from my 10 years employed by GitHub: they almost never do the git gc you’d expect to remove commits so once you push a commit: it’s likely there forever and identifiable by that same hash from anywhere in the fork network. This can be bad/scary so be aware).
I guess a keyboard auto corrected i into I.
git reset HEAD^1 (NO --hard!)
git commit --patch ...
git rebase --continue
You want to add some pending changes to the previous commit? No problem: git commit --patch --amend ...
You want to move the pending changes to future commits? git stash
git rebase --continue
git stash popHere's a GIF I found with Google: https://yogwang.site/2025/cursor-vscode-gitlens-rebase-edito...
x git commit --amend --reset-author --no-edit
I use that one very frequently (after a fixup, of course) because I want the author date on amended commits to reflect the last time I edited them, not the first time I committed them.1. Everyone uses feature branches
2. Everyone cleans up their branch using interactive rebase on top of main before review
3. All merges have been freshly rebase on main
I think it works very well and keeps the history clean while preserving "each commit does one thing".
Imagine bushwhacking your way through a bunch of conflicts while rebasing on main... Now you realized that you needed to do some other work before your pr is ready for review... Now you need to rebase on main again and get to relive conflict hell again.
Maybe I'm one of the stupid incompetent people referred to in this post but I haven't figured out a way to deal with the having to solve the same merge conflicts when I rebase again :(
git reset --hard @{1}
After a rebase to "undo" it. FILE: .gitconfig
----------------
[alias]
ci = commit
fix = commit --fixup
[rebase]
autosquash = true
Now you can do quick commits as save-points, and squash all of them at the end. git tag last-good
git ci -am 'WIP'
git fix HEAD -a
git fix HEAD -a
…
git rebase -i last-good- create an empty commit with the branch name, type, ticket number etc. (with a small git script, `git issue 321 fix some things` -> `git switch -c 321-fix-some-things && git commit -m "fix(321): some things" --allow-empty`)
- `rebase -i` then shines when you have stacked commits where previous one have been merged _in a changed form (e.g. squashed)_. In such cases git often duplicates the already merged commits thinking they are part of your branch leading to endless dump conflicts, with a "marker" commit you can easily spot it in the `rebase -i` view and then also easily stripp them out by removing their lines
It isn't particularly fast, but it uses bisect to find the first commit on the target branch that conflicts with your branch, then let's you rebase to that commit. You then repeat the process until you are caught up.
The idea is that solving one conflict many times is usually quite easy, especially if you know the conflicting commit messages, but solving many conflicts in one go is not so easy. This was inspired by me rebasing a 6 month old branch which refactored a file that happened to have many edits in between.
I have a simple rule where if the rebase cannot be solved within a few commit rewrites that I will restart the branch from latest master.
If you are suffering from really difficult rebase scenarios, it's likely that the senior developers are more at fault than the junior developers. The way you organize work over the codebase has the biggest impact on how things would conflict. If nothing ever does, rebasing is a trivial operation.
- git rebase -i <commit-hash>^
- select edit
- git reset --soft HEAD~
- make some changes
- git add . && git commit -m "changes"
- git rebase --continue
- Am I using git rebase wrong?
The opposite is true, of course, for example: you move commits arround, start to resolve conflicts, and after a few steps you realize you can't, so when you
> you can bail out at any time. as aforementioned with git rebase --abort
Yep, except for all the work you've done resolving the conflict, that's "aborted"
> the old commits still exist in git’s object database, unreferenced but intact,
The best UI there is - some hidded data store you're, of course, intimately familiar with, so won't have any trouble getting data out of. Oh, wait, you didn't even know it existed? Tough luck, git gud to avoid data loss!
> botched rebase is a few minutes of you perusing through the reflog.
Unless, of course, your not that sharp to remember all the commits from their names and would also like to see the diff contents to connect to your code work. But that's just more minutes extra, not that big of a loss.
> there’s of course, the low-tech insurance policy: git branch backup-before-rebase
Finally some sensible advice, one that should be the default backup plan to save userst he trouble of wasting "few minutes" perusing the logs
git commit —fixup=<commit-id>
git rebase -i —autosquash
This is my best friendBesides interactive rebase, my other favorite command is “git log —-oneline origin/master..HEAD”
It shows me exactly where I am…
Zero rebases since Fable 5.
Sure, if they want to they can later use `npm -g i` or whatever abomination they want but when we teach them, we should use full arguments, not aliases. please, people :(
I know you can do this from within magit as well but I've never gotten into that workflow.
it's the one clear tool that i used all the time. moving to jj, it has lots of amazing tools starting with `jj edit` to go change the past safely & conveniently, with much less pain. but i miss the direct powerful data-driven experience of seeing the timeline in a text file, and picking what to do with it. sometimes.
imo every dev should work to get some competency with git rebase! it's an amazing tool. there's few other systems that give such direct control. it's top layer is, perhaps, the excel of version control?