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.

Updated
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#

git rebase <base>              # move the current branch onto <base>
git rebase main feature        # switch to feature and move it onto main
git rebase -i HEAD~5           # interactive: edit the last five commits
git rebase --onto main old new # move only the range old..new
git rebase --continue          # carry on after resolving a conflict
git rebase --skip              # drop a commit that has nothing to apply
git rebase --abort             # put everything back as it was

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#

$ git switch feature-export
$ git fetch origin
$ git rebase origin/main
Successfully rebased and updated refs/heads/feature-export.

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:

$ git log --oneline -3
8f2c91a (HEAD -> feature-export) Export to CSV
4b77de0 Add column headers
a10d33e (origin/main) Update dependencies

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:

$ git rebase -i HEAD~3
pick 1c0de11 Draft parser
pick 7ab4409 Fix typo
pick 3f19c02 Test for empty file

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:

$ git reflog
8f2c91a HEAD@{0}: rebase (finish): returning to refs/heads/feature-export
4b77de0 HEAD@{1}: rebase (pick): Export to CSV
c41ab8e HEAD@{2}: checkout: moving from main to feature-export
$ git reset --hard c41ab8e

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:

git push --force-with-lease

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

  1. Make sure the branch is yours aloneIf someone has pulled the branch, do not rebase it — merge instead.
  2. Update the basegit fetch origin so you rebase onto the current state, not yesterday's.
  3. Run the rebasegit rebase origin/main from your branch; on a conflict, resolve it and run git rebase --continue.
  4. Tidy up interactivelygit rebase -i: fixup for small fixes, reword for messages.
  5. Check the historygit log --oneline: order, messages, no stray commits.
  6. 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.

Start the plan

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

Was this helpful?

More articles

Programming and IT git merge Merging brings work from one branch into another. git merge takes the branch you name and joins its history with the current one — sometimes by just moving a pointer, sometimes by creating a separate commit, and sometimes by stopping and asking for help. Programming and IT git pull git pull brings other people's commits from a remote repository and immediately merges them into your current branch. It is two operations in a row, and almost every problem with pull comes from the second one — how it joins the two histories. Programming and IT How to undo a commit The question "how do I undo a commit" actually hides four different ones: fix the last commit, take it back and redo it, erase several, or cancel an old commit in the middle of history. The answer depends above all on one thing — whether anyone besides you has seen that commit. Programming and IT How to resolve a merge conflict A conflict is not a breakage, it is a question. Git merges changes automatically as long as they are in different parts of a file; when two branches edit the same lines, only a person can choose the right version. Your job is to read both versions, build working code out of them and tell Git the question is settled. Programming and IT git commit A commit is a saved state of your project with an author, a timestamp and a message. git commit takes what you selected beforehand with git add and writes it into history as one indivisible piece. Programming and IT Git commands Git has well over a hundred commands, but on an ordinary day you use about twenty. Here they are in the order you need them — from your first copy of a repository to undoing a bad step — with one line of explanation each.

More solutions