git rebase command: --continue, --abort, cancel, --interactive, squash
The topic of this article is the git rebase
command.
The git rebase
command, along with the git merge
command, is the primary way to merge two or more branches.
Let's cover the basics of using it, and some commonly used options.
1. Getting git rebase right
As I mentioned in the previous git merge
post, merging two or more branches using the git rebase
command gives you a straight commit history.
A diagram of how rebase works is shown below.
For example, let's say you want to do a rebase from the feature-a
branch to the main
branch.
Git will merge all commits from the feature-a
branch that branch from the common ancestor of the two branches into the last commit on the main
branch.
At the same time, it deletes any commits on the old feature-a
branch that were two branches.
The result is a new feature-a
branch, starting at the last branch on the main
branch.
This changes the commit history to create a single history instead of two.
The command we use to do this is
Unlike the git merge
command, we go to the source branch
and run the command against the target branch
.
In the example above it would look like this
$ git switch feature-a
$ git rebase main
Here's what it looks like
The rebase merged the develop
and main
branches, which have a common ancestor, commit 6519
.
As a result, a new develop
branch was created, branching from main
, and the old develop
branch disappeared.
If a conflict occurs during the rebase process, the following message is displayed
$ git rebase main
Result:
Auto-merging 1.md
CONFLICT (content): Merge conflict in 1.md
error: could not apply 7fc042b... 1.md edited in develop
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm ", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 7fc042b... 1.md edited in develop
If you want to finish the rebase here, resolve the conflicts and stage the files with the git add command. Then use the options to continue the rebase process.
The options to continue the rebase process are
$ git rebase --continue
If you want to view the conflicts and revert the rebase, type the following command to revert to the state before the rebase.
$ git rebase --abort
We'll look at each of these options in more detail in later sections.
2. Abort a rebase with --abort when a conflict occurs
When Git is in the middle of a rebase, it will pause if it sees a conflict.
It will then give us the option to either resolve the conflict and continue the rebase, or abort.
At this point, if we want to abort, we have the --abort
option.
If you use this option, the task tree will revert to the state it was in before the rebase was run. This can be useful if you encounter conflicts or problems that make it unreasonable to continue with the rebase.
This command is used as follows
$ git rebase --abort
Let's look at the actual execution results to see in which situations it can be used.
Looking at the log message, we can see that the 1.md
file has been modified by two branches.
In this situation, the rebase command was executed and a conflict was detected, causing the rebase to pause for a while.
When we aborted the rebase with the --abort
option, it returned to the status quo.
3. Completing the rebase with the --continue option when a conflict occurs
Next, we'll see how to use the --continue
option to resolve a conflict and complete the rebase process.
The process of resolving conflicts is the same as the git merge
process.
You will need to open the conflicting files in an editor and make your selections.
See the git merge post for more information.
Once you've fixed the files, stage them using the git add command. Then, instead of making a new commit like a merge, we continue this rebase process using the rebase command option.
Use it like this
$ git rebase --continue
Let's see the results of running it on a real crash case.
Looking at the log message, we see that the 1.md
file has been modified by both branches.
When we run rebase, the conflict is confirmed and Git pauses the rebase process.
I cleaned up the conflicting lines with the editor, staged them, and used the --continue
option.
As a result, we can see that the rebase completed successfully.
4. Undoing a rebase with git reset
If you want to undo a rebase that has already been done, you need to use the git reflog
and git reset
commands.
As we saw in the git commit post, normal commits can also be undone or deleted using the commit ID output by the git log
command.
However, if you have run a git rebase
, you have already made changes to an existing commit, so you cannot revert to the information in the current commit history.
The git reflog
command shows a log of what you did to all values stored as ref
, including branches, tags, etc.
You can think of it as a more detailed git log
for debugging.
Thanks to this, you can go back to a previous commit, even if you have run a git rebase
command.
Here is the part of the log output by the git reflog
command related to the rebase we ran in Section 3.
$ git reflog
Result:
69fd9e0 (HEAD -> develop) HEAD@{0}: rebase (finish): returning to refs/heads/develop
69fd9e0 (HEAD -> develop) HEAD@{1}: rebase (continue): 1.md edited in develop
5e110f7 (main) HEAD@{2}: rebase (start): checkout main
7fc042b HEAD@{3}: rebase (abort): returning to refs/heads/develop
From the logs, we can see that the start of the rebase we just ran was at HEAD@{2}
, and that we need to go back to HEAD@{3}
to go back before that.
Now that you know where to go back, run the git reset
command.
Use the --mixed
option to keep the disappearing changes in the working tree, or the --hard
option to go back with everything erased.
Here is the result of using the --mixed
option
You can also see that the changes from the deleted commits remain in the working tree after rebasing.
5. Modify the commit history at will with the --interactive option (feat. squash)
The --interactive
or -i
option to the git rebase
command is an option to run an interactive rebase session.
In this session we can modify commits in the current branch, squash them, reorder them, etc.
As a result, we change the commit history of the current branch.
It's important to note that unlike the git rebase
command we saw earlier, we are only manipulating the commits of one branch.
Now let's see the results in action.
To run a session, you'll need to enter the last commit ID of the commit you want to manipulate, like this
$ git rebase -i [commit-hash]
The current commit history looks like this
Here, the above command will open a window like the one below.
Here, all comments starting with #
are instructions.
The top uncommented part is the list of commits that are the subject of this rebase session, sorted by age.
pick 7fc042b 1.md edited in develop
pick 6d80db8 1.md edited in develop 2
In our example it's this part. You can replace the pick
part with any other command, save it, and do whatever you want with it.
5.1. Key commands and how to use them
pick
: Keep the current statereword
: Change only the commit messageedit
: Change details like author, date, etc.squash
:Create a new commit message, merging it with the previous commitfixup
: Use the last commit message while merging with the previous commitdrop
: Drop a commit
For example, to change the message of the 7fd0
commit and merge the 6d80
commit with the previous commit, make the following changes, save, and close the editor.
reword 7fc042b 1.md edited in develop
fixup 6d80db8 1.md edited in develop 2
When you perform an action that requires new information, such as reword
, edit
, or squash
, Git will open as many new editors as necessary to ask for the necessary information.
The screen above shows the editor window prompting for a new commit message, following the reword command.
The comment below shows that we are currently working on reword (Last command done
),
and that we will next run the fixup operation (Next command to do
).
If you look at the results, you can see that the commands we wrote above have completed the task of modifying the commit message and merging the previous commit.
As we have seen in the previous command git commit --amend, even for simple message modifications, Git uses the method of creating a new commit and replacing it. Therefore, you should use all commands in rebase carefully when collaborating.
6. Closing thoughts
The git rebase
command has the ability to change the history of commits, one of Git's unwritten rules.
As powerful as this command is, it's also powerful enough to cause unexpected chaos in a collaborative process.
For this reason, git rebase
should always be used publicly, in accordance with your team's conventions.
