Programming and IT
git rebase
git rebase moves the commits of your branch as if you had started work not from an old state but from the current one. History becomes linear, but the commits are created anew — with new hashes. That leads to the main rule: you can only rebase what nobody else is using.
In this article
What happens during a rebase#
Every commit stores a reference to its parent. A branch is a chain growing from the point where it split off. While you were working, the main branch moved on, and your chain hangs from an outdated parent.
A rebase takes your commits one by one, works out their changes and applies those changes on top of the new tip. The result looks as if you started work this morning. But these are different commits: they have a different parent, a different time of application and, as a consequence, different hashes. The old objects stay in the database until garbage collection, and you can reach them through the reference log.
From this follows a rule worth learning before the syntax. Never rewrite the history of a branch someone else has already pulled. Your teammate has the old commits, you have the new ones, the content is identical — and at the next merge Git will show every change twice. For a branch that lives only on your machine, this restriction does not apply.
Syntax and common flags#
Two more are worth knowing. --autostash stashes uncommitted edits before the
rebase and brings them back afterwards. --rebase-merges keeps the structure of
merges inside the moved range instead of flattening them.
Example one: a branch onto a fresh base#
These three lines do exactly one thing: your commits now sit on top of the state
of main that is on the server right now. A compact log is a handy way to check
the result — more on setting it up in the article on
git log:
The first git status after a rebase almost always looks alarming: it says the
branch has "diverged" from its copy on the server, with so many commits on your
side and so many on theirs. That is expected — the commits were replaced with new
ones.
Example two: cleaning up before you push#
Interactive mode opens an editor with the list of commits, oldest at the top:
The word on the left says what to do with each commit. pick keeps it as is,
reword changes the message, squash glues it to the previous one and combines
the messages, fixup does the same but throws the message away, drop deletes
it, and edit stops at that commit so you can change its contents. You can
reorder the lines: their order in the file becomes the order in history.
Change pick on the second line to fixup, save the file — and the typo fix
melts into the draft parser. Instead of three commits you have two.
Rebase or merge#
Both commands combine the work of two branches, but they answer different questions.
A merge preserves the facts: "here are two lines of work, and here is the moment they were joined." History branches, nothing is rewritten, and it is safe for any branch, including shared ones. The price is merge commits, which make history harder to read.
A rebase preserves readability: one straight line, every commit stands on its
own, and git bisect behaves predictably on such history. The price is rewritten
hashes and an obligation not to get in anyone's way.
The most common practical compromise: clean up your own branch with a rebase before pushing, and bring it into the main branch with a merge. If a conflict comes up during a rebase, you resolve it just as in a merge — the sequence is explained in how to resolve a merge conflict.
Common mistakes and how to get out of them#
The rebase was started on the wrong branch. Until the process is finished,
git rebase --abort saves you: the working copy and the branch pointer return to
their original state.
The rebase is finished and the result is wrong. The reference log brings the branch back:
Here c41ab8e is the branch tip before the rebase started. The log keeps every
position of HEAD for the last few weeks, so commits "lost" in a rebase have not
really gone anywhere. How reset behaves and why --hard is dangerous is covered
in the article on git reset.
The push is rejected. After a rebase, an ordinary git push refuses to work,
and the temptation to add --force is strong. A forced push wipes everything on
the server you have not seen — including a teammate's commits pushed five minutes
ago. The safe replacement:
This sends your changes only if the branch tip on the server is the one you saw at
your last fetch. If something new has appeared, the push is rejected and you look
at what arrived first.
The same conflict in every commit. This happens when you move a dozen commits
that all touch the same spot. Turn on git rerere
(git config --global rerere.enabled true): Git remembers your first resolution
and applies it to the repeated conflicts by itself.
Step-by-step plan
- Make sure the branch is yours aloneIf someone has pulled the branch, do not rebase it — merge instead.
- Update the basegit fetch origin so you rebase onto the current state, not yesterday's.
- Run the rebasegit rebase origin/main from your branch; on a conflict, resolve it and run git rebase --continue.
- Tidy up interactivelygit rebase -i: fixup for small fixes, reword for messages.
- Check the historygit log --oneline: order, messages, no stray commits.
- Push without overwriting othersgit push --force-with-lease; if it is rejected, find out what appeared on the server.
Start learning this in your own space
The plan goes into your repository: tick off stages, keep notes — the change history shows how far you have come.
Check yourself
1.A rebase went wrong but is not finished yet. Which command puts everything back?
2.Which push will not wipe commits that appeared on the server after your last fetch?
3.What does the word fixup do in the interactive rebase list?
Sources
-
Pro Git bookThe chapter on rebasing, including when you must not rewrite historyfree
-
git rebase referenceEvery flag and mode from the sourcefree
-
git reflog referenceThe log of HEAD positions — how to restore a branch after a bad rebasefree
Was this helpful?