4. Rewriting Git History

Git's main job is to make sure you never lose a committed change. But, it's also designed to give you total control over your development workflow. This includes letting you define exactly what your project history looks like; however, it also creates the potential to lose commits. Git provides its history-rewriting commands under the disclaimer that using them may result in lost content.

This tutorial discusses some of the most common reasons for overwriting committed snapshots and shows you how to avoid the pitfalls of doing so.

Git Tutorial: Rewriting History

The git rebase -i Command

Running git rebase with the -i flag begins an interactive rebasing session. Instead of blindly moving all of the commits to the new base, interactive rebasing gives you the opportunity to alter individual commits in the process. This lets you clean up history by removing, splitting, and altering an existing series of commits. It’s like git commit --amend on steroids.

Usage

git rebase -i <base>

Rebase the current branch onto <base>, but use an interactive rebasing session. This opens an editor where you can enter commands (described below) for each commit to be rebased. These commands determine how individual commits will be transferred to the new base. You can also reorder the commit listing to change the order of the commits themselves.

Discussion

Interactive rebasing gives you complete control over what your project history looks like. This affords a lot of freedom to developers, as it lets them commit a “messy” history while they’re focused on writing code, then go back and clean it up after the fact.

Most developers like to use an interactive rebase to polish a feature branch before merging it into the main code base. This gives them the opportunity to squash insignificant commits, delete obsolete ones, and make sure everything else is in order before committing to the “official” project history. To everybody else, it will look like the entire feature was developed in a single series of well-planned commits.

Examples

The example found below is an interactive adaptation of the one from the non-interactive git rebase page.

# Start a new feature
git checkout -b new-feature master
# Edit files
git commit -a -m "Start developing a feature"
# Edit more files
git commit -a -m "Fix something from the previous commit"

# Add a commit directly to master
git checkout master
# Edit files
git commit -a -m "Fix security hole"

# Begin an interactive rebasing session
git checkout new-feature
git rebase -i master

The last command will open an editor populated with the two commits from new-feature, along with some instructions:

pick 32618c4 Start developing a feature
pick 62eed47 Fix something from the previous commit

You can change the pick commands before each commit to determine how it gets moved during the rebase. In our case, let’s just combine the two commits with a squash command:

pick 32618c4 Start developing a feature
squash 62eed47 Fix something from the previous commit

Save and close the editor to begin the rebase. This will open another editor asking for the commit message for the combined snapshot. After defining the commit message, the rebase is complete and you should be able to see the squashed commit in your git log output. This entire process can be visualized as follows:

Git Tutorial: git rebase -i example

Note that the squashed commit has a different ID than either of the original commits, which tells us that it is indeed a brand new commit.

Finally, you can do a fast-forward merge to integrate the polished feature branch into the main code base:

git checkout master
git merge new-feature

The real power of interactive rebasing can be seen in the history of the resulting master branch—the extra 62eed47 commit is nowhere to be found. To everybody else, it looks like you’re a brilliant developer who implemented the new-feature with the perfect amount of commits the first time around. This is how interactive rebasing can keep a project’s history clean and meaningful.

Sign up for more Git articles & resources:

Our latest Git blog posts

Nicola Paolucci

Git and project dependencies

Consider the following questions: How do you handle project dependencies with git? Our project is made up of multiple inter-dependent repositories. Currently we manage those with svn:externals. What ...

Read more at the Git blog