Home Resource Centre Git Rebase | Strategies, Commands, Best Practices, Examples & More

Git Rebase | Strategies, Commands, Best Practices, Examples & More

Git rebase is a powerful feature and often-used Git tool that allows developers to modify the history of their commits. In essence, it allows you to take the changes made in one branch and apply them to another branch, resulting in a linear branch history that is easier to read and understand. However, using Git rebase can be complicated and confusing, especially for those who are new to using Git. In this article, we will explain what git rebase is, how it works, and some best practices for successful rebase use.

What Is Git Rebase? What Does It Do In Git?

In Git, rebase is a command that allows you to move the changes from one branch to another by applying the subsequent commits of the source branch onto the destination branch. In other words, Git rebase is a Git command that allows developers to change the order and structure of commits in a branch.

It works by taking a branch and applying the changes made in another branch to it. This creates a new master branch with a linear history, making it easier to understand the changes made to the code base over time. For example, the image given below illustrates how we can shift the base of a branch from one node to another. This, in practice, is done using the Git rebase command.

Git rebase is particularly useful in situations where multiple developers are working on the same codebase and commit list format.

  • It allows them to incorporate additional commits and changes made by other team members into their own code and project file while still maintaining a clean merge and easy-to-follow public history.
  • However, Git rebase must be used with caution as it rewrites the commit history of the branch.
  • Especially if the branch has already been pushed to a remote GitHub repository and other people are working with it.
  • In such cases, rebasing could cause conflicts and make it difficult to merge the tentative commit messages and changes from other branches.

How Git Rebase Works?

To understand how git rebase works, it's essential to first understand how Git tracks changes in a codebase. Git uses a system called the commit tree(git tree) to keep track of the changes made to a codebase over time. Each base commit represents a snapshot of the code at a specific point in time. The commit tree looks like a series of interconnected branches, with each branch representing a different version of the codebase.

  • When you use git rebase, you are essentially taking the changes made in one branch and applying them to another branch.
  • Branches are a fundamental and important part of version control systems. Git accomplishes this by creating a new set of commits based on the changes made in the original branch.
  • These new series of commits are then added to the target branch, resulting in a linear history. It then performs a three-way merge between the two latest branch commits.

If you want to use Git rebase to make changes in the branch history, here is how that works:

  • You typically start by checking out the master branch you want to apply changes to. This is known as the target branch.
  • You will then use the git rebase command to specify the public branch that contains the changes you want to apply, along with the commit name.
  • Git will then create a new set of commit blocks based on the changes in the original branch and add them to the target branch.

Git Rebase Vs. Git Merge

Git rebase and merge commands are both used to combine changes from one branch into another. However, they work in different ways and have different use cases.

  • Git merge creates a new commit that combines the changes from two or more branches. The new commit in the simple rebase merge operation has two parent commits, one from each of the merged branches.
  • The resulting branch history is a graph (tree structure) with multiple paths that show where the merges occurred. Merging can be performed on both public and private branches.
  • Git rebase, on the other hand, moves the current working branch to a new base commit during playback and then applies the changes from the previous branch on top of that.
  • This results in a linear history that records previous commits and fresh commits together. Here, the sequence of commits appears to have been made directly on the new base branch. The old branch is essentially discarded and replaced by the rebased branch.

Here are some of the behavioral differences between Git rebase vs. merge:

  • Git merge preserves the original branch history, while Git rebase rewrites it.
  • Git merge creates a single commit that combines the changes from two branches, while Git rebase re-applies the commits from one branch onto another.
  • Git merge is a good choice when you want to preserve the entire history editing of multiple branches, while Git rebase is useful when you want a clean, linear project history in a complex merge.

In general, you should use Git merge as the default option when you want to combine changes from multiple branches and preserve their history. Use Git rebase when you want a clean, linear history or need to update your branch with changes from another branch. However, there are legitimate reasons why a developer may want to recreate merge commits: to keep the branch structure (or "commit/ branch topology") when working on multiple, interrelated branches.

Git Rebase Configuration

Git rebase can be configured using several configuration options. These options can be set using the git config command or by editing the .gitconfig file. Here are some common configuration options for Git rebase:

  1. rebase.autostash: This option is used to automatically stash any local changes before starting a rebase operation, and then apply the changes after the rebase is complete. This can help avoid conflicts during the rebase process.
    To enable this option, run the command-
    git config --global rebase.autostach true
  2. rebase.autosquash:  This option is used to automatically squash or fixup commits during a rebase operation based on their commit message.
    To enable this option, run the command-
    git config --global rebase.autosquash true
  3. rebase.instructionFormat: This option is used to specify the format of the interactive rebase instruction sheet. The default value is pick %s, which lists each commit with the pick command. Other options include reword, edit, squash, fixup, and exec.
  4. merge.conflictstyle: This option is used to specify the style of diff output to use when resolving conflicts during a Git rebase operation. The options are merge (the default), diff3, and diff.
  5. rebase.backend: Default backend or primary backend to use for rebasing. Possible choices are to apply or merge. In the future, if the merge backend gains all remaining capabilities of the interactive backend, this setting may become unused.

To view your current Git rebase configuration, run the command- git config --list | grep rebase. To set a configuration option, run- git config <option> <value>. For example, to set the rebase.squash option to true, and run- git config --global rebase.autosquash true.

Creating Master & Feature Branches

Creating master and feature branches is a common workflow in Git. Here are the steps to create these branches:

  1. Create a new repository: If you haven't already, create a new Git repository using the git init command in your local directory.
  2. Create a master branch: The master branch is the main branch in your repository and should contain your stable, production-ready code. You can create a master branch by using the git branch command followed by the branch name, like this: git branch master
  3. Switch to the master branch: Once you've created the master branch, you need to switch to it using the git checkout command: git checkout master
  4. Create a feature branch: Feature branches are used to develop new features or make changes to existing ones. You can create a feature branch by using the git branch command followed by the branch name, like this: git branch feature_branch
  5. Switch to the feature branch: Once you've created the feature branch, switch to it using the git checkout command: git checkout feature_branch
  6. Make changes: Make your changes to the code in the feature branch. You can use the git add and git commit commands to save your changes.
  7. Switch back to the master branch: Once you've made your changes and committed them to the feature branch, switch back to the master branch using the git checkout command: git checkout master
  8. Merge the feature branch into master: Merge the changes from the feature branch into the master branch using the git merge command: git merge feature_branch
  9. Push your changes: Finally, push your changes to the remote repository using the command git push <remote_name> <branch_name>, where <remote_name> is the name of your remote repository and <branch_name> is either master or feature_branch, depending on which branch you are pushing.

Merge Strategies In Git Rebase

Git rebase provides several merge strategies that can be used to handle conflicts that arise during the rebase process. The merge strategies available in Git rebase include:

  1. recursive: This is the default merge strategy used by Git. It tries to reapply each commit on top of the new base commit in order to resolve conflicts as they arise. This strategy is suitable for most cases.
  2. resolve: This strategy tries to reapply each commit on top of the new base commit in order, but only attempts to auto-resolve conflicts. If any conflicts cannot be resolved automatically, the rebase operation will fail.
  3. ours: This strategy discards all changes from the upstream branch and applies only the changes from the current branch. This is useful when you want to completely overwrite changes in the upstream branch.
  4. theirs: This strategy discards all changes from the current branch and applies only the changes from the upstream branch. This is useful when you want to discard all local changes and use the changes from the upstream branch.

These are the 4 main strategies you can use in Git rebase. To specify a merge strategy during a Git rebase operation, use the -s <strategy> option followed by the desired merge strategy name.

  • For example, to use the resolve merge strategy, you must run the command- git rebase -s resolve <base>, where <base> is the branch or commit where you want to apply the changes.
  • The merge mechanism (git merge and git pull commands) allows the backend merge strategies to be chosen with -s option.
  • Some strategies can also take their own options, which can be passed by giving the -X<option> arguments to git merge and/ or git pull.

This is the default merge strategy when pulling or merging one branch. This strategy can only resolve two heads using a 3-way merge algorithm.

  • If there is more than one common ancestor to be used for 3-way merge, it results in a merged tree of the common ancestors. This is then used as the reference tree for the 3-way merge.
  • In addition, this reportedly results in fewer merge. It has been reported to result in fewer merge conflicts without causing mismerges by tests done on actual merge commits taken from Linux 2.6 kernel development history.
  • Additionally, this strategy can detect and handle merges involving renames. It does not make use of detected copies. The name for this algorithm is an acronym ("Ostensibly Recursive’s Twin") and came from the fact that it was written as a replacement for the previous default algorithm, recursive.

Git Rebase Examples

Let's walk through an example of using git rebase to understand the rebase properties better. In the Git examples mentions below, we will have two branches: the main branch and the feature branch.

  1. Create a new branch called "feature" and make some changes to a file:

$ git checkout -b feature
$ touch file.txt
$ echo "This is some text" >> file.txt
$ git add file.txt
$ git commit -m "Add file.txt"

  1. Switch back to the "main" branch and make some changes to the same file:

$ git checkout main
$ echo "This is some more text" >> file.txt
$ git add file.txt
$ git commit -m "Add more text to file.txt"

  1. Now, let's assume that we want to apply the changes made in the "feature" branch to the "main" branch using git rebase:

$ git checkout feature
$ git rebase main

  1. Git will now apply the changes made in the "feature" branch to the "main" branch:

First, rewind the current head branch to replay your work on top of it...
Applying: Add file.txt

  1. Git will now prompt you to resolve any conflicts that may arise during the rebase process. In this example, let's assume that there are no conflicts:

$ git rebase main
First, rewinding head to replay your work on top of it...
Applying: Add file.txt

  1. Git will now show you the new commits that were created during the rebase process:

$ git log --oneline
1234567 Add file.txt
8901234 Add more text to file.txt

As you can see, the changes made in the feature branch have been applied to the "main" branch, resulting in a linear feature branch history. This makes it easier to understand the changes that were made to the codebase over time.

Git Rebase Commands

Here are some common and interactive Git rebase commands that you may find useful:

  1. git rebase <branch>: This command applies the changes made in the specified branch to the current branch head.
  2. git rebase -i <commit>: This command starts an interactive rebase, allowing you to modify individual commits and change the order of the commits.
  3. git rebase --abort: This command aborts rebasing process.
  4. git rebase --continue: This command continues a rebase after resolving merge conflicts.
  5. git rebase --skip: This command skips the frequent commits that are causing conflicts during a rebase.
  6. git rebase master: The git rebase master command is used to rebase the current branch onto the master branch. This will move the current branch to the tip of the master branch and apply all of the changes made in the master branch onto the current branch
  7. git rebase --onto <newbase> <oldbase> <branch>: This command rewrites the history of a branch by applying the changes made in the specified branch onto a new base.
  8. git rebase --root <branch>: This command rewrites the history of a branch by applying the changes made in the branch onto the root commit of the repository.
  9. git status: It checks the rebase status of the master branch.
  10. git rebase --edit-todo: Opens the rebase todo file in an editor. This file lists all the commits that will be applied during the entire rebase and allows you to modify the order or squash multiple commits together.
  11. pull rebase command: The pull rebase command is a Git command that is used to update the current branch with the changes from the remote branch while also rebasing the current branch onto the latest changes from the remote branch. In other words, it is a shorthand for running git fetch followed by git rebase.
  12. reset command: The git reset command is a powerful Git command used to reset the state of the repository to a specific commit or previous state. The reset command is often used to undo changes or to move the HEAD pointer to a different commit using the external tool.

Also read: Git Commands | Complete Guide From Basics To Advanced (+Examples)

Splitting Commits In Git Rebase

Splitting commits is a useful technique to make your Git history clearer and more organized. Here's how you can split a commit into smaller commits:

  1. Identify the commit you want to split: Use the git log command to identify the commit hash of the commit you want to split.
  2. Create a new branch: Before making any changes, create a new branch to work on so you can keep your original commit intact. You can use the command git checkout -b <new_branch_name> to create a new branch and switch to it.
  3. Use the interactive rebase: Use the interactive rebase feature in Git to split the commit. The interactive rebase allows you to modify your commit history by editing individual commits. Use the following command to start the interactive rebase: git rebase -i <commit_hash>^
  4. Edit the commit: In the interactive rebase screen, locate the commit you want to split and replace the word "pick" with "edit" next to it.
  5. Split the commit: After saving the changes to the interactive rebase file, Git will move you to the commit you want to split. Make your changes, and then stage the changes for commit using the git add command.
  6. Commit the changes: Once you have staged your changes, commit them using the git commit command. This will create a new commit that contains the changes you made.
  7. Repeat the process: Repeat steps 5 and 6 until you have split the commit into the smaller commits you want.
  8. Complete the rebase: Once you have split the commit into smaller commits, use the git rebase --continue command to complete the rebase.
  9. Push your changes: Finally, push your changes to the remote public repository using the command git push <remote_name> <new_branch_name>.

Git Rebase Options

Here are some of the commonly used configuration options that can be used with Git rebase command:

  1. --onto: The onto option specifies a new base commit for the branch being rebased. It allows you to move the branch's commits to a new starting point in the commit history.
  2. -i or --interactive: Starts an interactive rebase, which allows you to modify the commit history by selecting which commits to apply, editing commit messages, and squashing or splitting commits.
  3. --continue: Resumes the rebase after resolving conflicts or making changes in an interactive rebase.
  4. --skip: The skip configuration option skips a commit that cannot be applied during the rebase due to conflicts or other errors.
  5. -e option: The -e option is the edit option in Git rebase opens up an editor and allows you to edit the list of commits that will be applied during the rebase. When you run git rebase -i <base>, where <base> is the branch or commit where you want to apply the changes, Git opens up an interactive rebase editor with the list of commits that will be applied. With the -e option by default, you can edit this list of commits before continuing with the rebase.
  6. --abort: Aborts the current rebase operation and returns the branch to its original state before the rebase started.
  7. ours: The ours option in Git rebase is a merge strategy that discards all changes from the upstream branch and applies only the changes from the current branch. This strategy is useful when you want to completely overwrite changes in the upstream branch. For a binary file, the entire contents are taken from our side.
  8. -p or --preserve-merges: Preserves merge conflicts during a rebase. By default, merge commits are flattened into a series of single-line commits.
  9. --quiet or -q: Suppresses output during the rebase process, making the process less verbose.
  10. --autosquash: Automatically reorders and squashes commits based on original commit messages. This is useful for cleaning up a branch before merging it into another branch.
  11. --fork-point: Specifies the point at which the branch being rebased diverged from the upstream branch. This is useful at the time of rebasing a branch that has been merged into the upstream branch, and you want to exclude the merged commits from the rebase.
  12. quit option: Abort the rebase operation but HEAD is not reset back to the original branch. The index and working tree are also left unchanged as a result. If a temporary stash entry was created using --autostash, it will be saved to the stash list.
  13. exec(-x): This option allows you to run arbitrary shell commands against a commit.
  14. --no-rerere-autoupdate: After the rerere mechanism reuses a recorded resolution on the current conflict to update the rebase instruction files in the working common ancestor tree, allow it to also update the index with the result of resolution. --no-rerere-autoupdate is a good way to double-check what rerere did and catch potential mismerges, before committing the result to the index with a separate git add.

Labelling Conflicts Markers

When there are content conflicts and spurious conflicts, the merge machinery tries to annotate each side’s conflict markers with the commits where the content came from.

  • Since the apply backend drops the original information about the rebased commits and their parents (and instead generates new fake commits based off limited information in the generated sequence of patches and patch hunk), those commits cannot be identified.
  • Instead it has to fall back to a commit summary. Also, when merge.conflictStyle is set to diff3 or zdiff3, the apply backend will use "constructed merge base" to label the content from the merge base, and thus provide no information about the merge base commit whatsoever.

The merge backend works with the full commits on both sides of history and thus has no such limitations.

Rebasing Commits Against A Branch

Rebasing is a process of moving a stable master branch to a new base commit. It is useful when you want to incorporate changes from one topic branch into another topic branch, or when you want to clean up the commit history of a branch.

To rebase commits against a branch, follow these steps:

  • Switch to the branch that you want to rebase against.

git checkout <branch>

  • Fetch the latest changes from the remote repository.

git fetch

  • Rebase the branch against the latest changes.

git rebase origin/<branch>

  • This will move the branch to the head of the remote branch <branch>, replaying your local commits on top of the latest changes.

  • Resolve any conflicts that arise during the rebase process.

    If any conflicts occur during the rebase, you will need to resolve them by editing the relevant files and adding them back to the index. Once the conflicts are resolved, continue the rebase process by running:

git rebase --continue

  • Push the changes to the remote repository.

Once the rebase is complete, push the changes to the remote repository using:

git push --force

Be careful when using the --force flag, as it can overwrite changes made by others on the remote branch.

How To Recover From Upstream Git Rebase?

Recovering from an upstream rebase can be a bit tricky, but the following steps and default strategy can help you get back on track:

  1. Check your git log: First, check your git log to see if you have any commits that were lost during the rebase. If so, note the commit hashes.
  2. Create a new branch: Create a new branch from your current state to ensure that you don't lose any work. You can use the following command: git checkout -b <new_branch_name>
  3. Merge the lost commits: If you lost any commits during the upstream rebase, merge them back into your new branch using the command git cherry-pick <commit_hash> for each commit you need to merge.
  4. Merge the upstream branch: Once you've merged any lost commit pick, merge the upstream branch into your new branch using the command git merge <upstream_branch>. This will bring in all of the changes from the upstream branch and merge them with your work.
  5. Resolve conflicts: If there are any conflicts between your work and the upstream changes, resolve them by editing the affected untracked files and then using git add <filename> to mark them as resolved.
  6. Push your changes: Finally, push your changes to the remote repository using the command git push <remote_name> <new_branch_name>.

That's it! You should now have a new branch that incorporates all of the upstream changes as well as any of the intermediate commits that were lost during the upstream rebase.

Best Practices For Using Git Rebase

While git rebase is a powerful feature, it can also be easy to misuse. Here are some best practices to keep in mind when using git rebase:

  1. Use git rebase for small changes: Git rebase works best for small, incremental changes. If you have a large number of changes that need to be applied to a branch, it may be better to use a merge instead. This will help keep the commit history clean without any fake commits and is easy to understand.
  2. Be careful when rebasing a branch that others are working on: If you are working on a branch others are also working on, it's important to be careful when using git rebase. If you rebase your remote branch and then push the changes to a shared repository, it can cause problems for other team members who are working on the same codebase. It's important to communicate with your team members before using an advanced rebase application on a shared branch.
  3. Always test your changes before pushing: Before pushing changes to a shared repository in the remote branch, it's important to test your changes in the lines of context to make sure they work as intended. This can help catch any errors, unwanted patches, or issues and commit without warning messages before they are introduced to the main codebase.
  4. Use git rebase interactive mode for more control: If you need more control over the changes that are applied during a git rebase, you can use the interactive mode. The pairs of options in interactive mode will allow you to review each commit range individually and make changes as needed in the linear process. The interactive mode is meant for this type of workflow in the context region.
  5. Keep backups of your work to avoid unintended consequences: When using git rebase, there is always a risk of losing work and exit code without any previous warning if something goes wrong.

You might also be interested in reading the following:

  1. Git Tag | Easily Create, List, Remove, Push Tags & More
  2. Git Delete Branch | Local & Remote Branches With Examples
  3. Git Hooks | Definition, Usage, Types, Workflow & More (+Examples)
  4. Git Rename Branch | How To Rename Local & Remote Branch With Ease
  5. Git Submodule: Add, Remove, Pull Changes & More (With Examples)

Shivani Goyal
Manager, Content

I am an economics graduate using my qualifications and life skills to observe & absorb what life has to offer. A strong believer in 'Don't die before you are dead' philosophy, at Unstop I am producing content that resonates and enables you to be #Unstoppable. When I don't have to be presentable for the job, I'd be elbow deep in paint/ pencil residue, immersed in a good read or socializing in the flesh.

Updated On: 16 May'24, 10:27 PM IST