Squashing commits using an interactive rebase

When I work on a local branch, I prefer to commit in small increments with a few comments on what I did in the commits; however, as these commits do not build or pass any test requirements, I cannot submit them for review and verification one by one. I have to merge them in my branch, and still, cherry picking my fix would require me to cherry-pick twice the number of commits, which is not very handy.

What we can do is rebase and squash the commits into a single commit or at least a smaller number of commits.

Getting ready

To get started with this example, we need a new branch, namely rebaseExample3, that tracks origin/stable-3.1. Create the branch with the following command:

$ git checkout -b rebaseExample3 --track origin/stable-3.1
Branch rebaseExample3 set up to track remote branch stable-3.1 from origin.
Switched to a new branch 'rebaseExample3'

How to do it...

To really showcase this feature of Git, we will start by being six commits ahead of the origin/stable-3.1 branch. This is to simulate the fact that we have just created six commits on top of the rebaseExample3 branch; to do this, perform the following steps:

  1. Find a commit that is between origin/stable-3.1 and origin/stable-3.2 and list the commits in reverse order. If you don't list them in reverse order, you can scroll down to the bottom of the output and find the commit we will use, as shown in the following snippet:
    $ git log origin/stable-3.1..origin/stable-3.2 --oneline --reverse
    8a51c44 Do not close ArchiveOutputStream on error
    3467e86 Revert "Close unfinished archive entries on error"
    f045a68 Added the git-describe implementation
    0be59ab Merge "Added the git-describe implementation"
    fdc80f7 Merge branch 'stable-3.1'
    7995d87 Prepare 3.2.0-SNAPSHOT builds
    5218f7b Propagate IOException where possible when getting refs.
    
  2. Reset the rebaseExample3 branch to the 5218f7b commit; this will simulate that we have six commits on top of the origin/stable-3.1 branch. This can be tested by running the status of Git as follows:
    $ git reset --hard 5218f7b
    HEAD is now at 5218f7b Propagate IOException where possible when getting refs.
    $ git status
    On branch rebaseExample3
    Your branch is ahead of 'origin/stable-3.1' by 6 commits.
      (use "git push" to publish your local commits)
    
    nothing to commit, working directory clean
    
  3. Now we have these six commits on top of the origin/stable-3.1 branch, and we want to squash these commits into two different commits instead of six commits. This can be done by simply running git rebase --interactive. Notice that we are not specifying which branch we want to rebase to since we have already set up a tracking branch when we created the branch using --track. To continue, let's execute the rebase command as follows:
    $ git rebase --interactive
    pick 8a51c44 Do not close ArchiveOutputStream on error
    pick f045a68 Added the git-describe implementation
    pick 7995d87 Prepare 3.2.0-SNAPSHOT builds
    pick 5218f7b Propagate IOException where possible when getting refs.
    
  4. The editor will open, and you will see four commits and not six as you would expect. This is because the rebase in general refuses to take merge commits as part of the rebase scenario. You can use the --preserve-merges flag. As per the Help section of Git, this is not recommended.

    Note

    According to the Help section in Git --preserve-merges instead of ignoring merges, tries to recreate them.

    The --preserve-merges flag uses the --interactive machinery internally, but combining it with the --interactive option explicitly is generally not a good idea unless you know what you are doing (see the BUGS in the following snippet).

  5. Edit the file so it looks as follows:
    pick 8a51c44 Do not close ArchiveOutputStream on error
    squash f045a68 Added the git-describe implementation
    pick 7995d87 Prepare 3.2.0-SNAPSHOT builds
    squash 5218f7b Propagate IOException where possible when getting refs.
    
  6. Remember that commits are listed in reverse order as compared to the Git log. So, while squashing, we squash up into the commits we have marked with the pick. When you close the editor, Git will start the rebase from top to bottom. First, apply 8a51c44 and then squash f045a68 into the commit 8a51c44. This will open the commit message editor that contains both the commit messages. You can edit the commit messages, but for now, let us just close the editor to finish with the rebase and the squashing of these two commits. The editor will open one more time to complete the squashing of 5218f7b into 7995d87. Use gitk to verify the result.

    The following screenshot is as expected; now, we only have two commits on top of the origin/stable3-1 branch:

    How to do it...
  7. If you check the commit message of the HEAD commit, you will see that it has the information of two commits, as shown in the following command. This is because we decided not to change the commit message when we made the change:
    $ git log -1
    commit 9c96a651ff881c7d7c5a3974fa7a19a9c264d0a0
    Author: Matthias Sohn <[email protected]>
    Date:   Thu Oct 3 17:40:22 2013 +0200
    
        Prepare 3.2.0-SNAPSHOT builds
    
        Change-Id: Iac6cf7a5bb6146ee3fe38abe8020fc3fc4217584
        Signed-off-by: Matthias Sohn <[email protected]>
    
        Propagate IOException where possible when getting refs.
    
        Currently, Repository.getAllRefs() and Repository.getTags() silently
        ignores an IOException and instead returns an empty map. Repository
        is a public API and as such cannot be changed until the next major
        revision change. Where possible, update the internal jgit APIs to
        use the RefDatabase directly, since it propagates the error.
    
        Change-Id: I4e4537d8bd0fa772f388262684c5c4ca1929dc4c
    

There's more…

Now we have squashed two commits, but we could have used other keywords when editing the rebase's to-do list.

We will try the fixup functionality, which works like the squash functionality, by performing the following steps; the exception is that Git will select the commit message of the commits with the pick keyword:

  1. Start by resetting back to our starting point.
    $ git reset --hard 5218f7b
    HEAD is now at 5218f7b Propagate IOException where possible when getting refs.
    $ git status
    On branch rebaseExample3
    Your branch is ahead of 'origin/stable-3.1' by 6 commits.
      (use "git push" to publish your local commits)
    
    nothing to commit, working directory clean
    
  2. As you can see, we are back at the starting point, that is, we're six commits ahead of the origin/stable-3.1 branch. Now, we can try the fixup functionality. Start the interactive rebase and change the file according to the following output. Notice that you can use f instead of fixup.
    $ git rebase --interactive
    pick 8a51c44 Do not close ArchiveOutputStream on error
    f f045a68 Added the git-describe implementation
    pick 7995d87 Prepare 3.2.0-SNAPSHOT builds
    f 5218f7b Propagate IOException where possible when getting refs.
    
  3. Once you close the editor, you will see rebase's progress through Git. As predicted, the commit message editor will not open; Git will just rebase the changes into two commits on top of the orgin/stable-3.1 branch.
    $ git rebase --interactive
    [detached HEAD 70b4eb7] Do not close ArchiveOutputStream on error
     Author: Jonathan Nieder <[email protected]>
     6 files changed, 537 insertions(+), 2 deletions(-)
     create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeComma
    ndTest.java
     create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.ja
    va
    [detached HEAD c5bc5cc] Prepare 3.2.0-SNAPSHOT builds
     Author: Matthias Sohn <[email protected]>
     67 files changed, 422 insertions(+), 372 deletions(-)
     rewrite org.eclipse.jgit.http.server/META-INF/MANIFEST.MF (61%)
     rewrite org.eclipse.jgit.java7.test/META-INF/MANIFEST.MF (66%)
     rewrite org.eclipse.jgit.junit/META-INF/MANIFEST.MF (73%)
     rewrite org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF (61%)
     rewrite org.eclipse.jgit.pgm/META-INF/MANIFEST.MF (63%)
     rewrite org.eclipse.jgit.test/META-INF/MANIFEST.MF (77%)
     rewrite org.eclipse.jgit.ui/META-INF/MANIFEST.MF (67%)
     rewrite org.eclipse.jgit/META-INF/MANIFEST.MF (64%)
    Successfully rebased and updated refs/heads/rebaseExample3.
    
  4. Another difference is that the commit message from the two commits we marked with fixup has disappeared. So, if you compare this with the previous example, it is very clear what the difference is; it is shown in the following command:
    commit c5bc5cc9e0956575cc3c30c3be4aecab19980e4d
    Author: Matthias Sohn <[email protected]>
    Date:   Thu Oct 3 17:40:22 2013 +0200
    
        Prepare 3.2.0-SNAPSHOT builds
    
        Change-Id: Iac6cf7a5bb6146ee3fe38abe8020fc3fc4217584
        Signed-off-by: Matthias Sohn [email protected]
    
  5. Finally, we can also confirm that we still have the same source code, but with different commits. This can be done by comparing this commit with the commit we created via 9c96a65, using the following command:
    $ git diff  9c96a65
    

As predicted, there is no output from git diff, so we still have the same source code.

This check can also be performed on the previous example.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset