GitEye and Interactive Rebase

Introduction

As I mentioned in my earlier blog, TeamForge for Gerrit, a Gerrit patch set must be associated with one and only one commit.  In this blog I will talk about how you can commit your work periodically as you work on your change request and later use the interactive rebase feature in GitEye to squash your commits into one commit, and to compose a commit message suitable for Gerrit.

The Scenario

I have cloned a TeamForge Git repository and configured it for Gerrit as described in my earlier blog.  Also following the steps described in the earlier blog I have created and checked out a local branch for a TeamForge artifact.  As I worked on the artifact, I periodically committed my changes to the local repository.  This not only gave me a sense of accomplishment, but it also gave me good checkpoints in the event that I made a mess and needed to roll back.  The following screenshot shows the repository history after these commits.

history_1.png

As you can see, I have made five commits since cloning the repository, starting with Create database and ending with Create the UI.

The Problem

Now I am ready to push my work to Gerrit to request a code review.  However, Gerrit insists that my change must consist of just one commit!

The Solution

Fortunately, Git has a tool called interactive rebase that can do exactly what I need to do, which is to squash all of my commits into a single commit.  Furthermore, GitEye includes a slick graphical interface for this tool.

Interactive Rebase

To start interactive rebase from the History view, right-click on the commit preceding the oldest commit you want to rewrite.  File the previous sentence in your memory bank, as not knowing where to start an interactive rebase is a frequent cause of frustration for beginners.

rebase_menu.png

Planning Rebase

When you start interactive rebase an editor will be opened that lets you define a rebase plan.

rebase_editor.png

To understand what you are doing as you define your rebase plan, it helps to know what is going to happen when you finish your plan and click Start.

  1. GitEye will rewind HEAD to the commit preceding the first one in the edit list.
  2. GitEye will process your plan, starting from the top row and working towards the bottom.

But what does process your plan mean?  It means that for each row it will perform the action that you have selected.  The available actions are described below:

  • pick             Include commit.  Pick entries can be moved up or down to reorder commits.
  • skip             Do not include commit.  It will be removed from history.
  • edit              Amend commit.  You will be asked for input when an edit entry is encountered.
  • reword        Include commit, but edit commit message.  You will be asked for input.
  • squash        Squash commit with its predecessor commit, including their commit messages.
  • fixup            Squash commit with its predecessor commit, discarding squashed commit’s   commit message.

So, for example, if you were to click Start without making any changes to the default plan, the net effect would be zero.  HEAD would be rewound to the commit that you selected from the History view, and then the PICK actions would cause the commits to be reapplied in the same order that they were originally made, leaving you right back where you started.

With this in mind, let’s plan a strategy for preparing our commits to be pushed to Gerrit.

Reword (Fix Commit Message)

As I committed my changes, I didn’t worry too much about my commit messages.  Git repositories are local, so for the time being I knew that the messages were strictly for my own eyes and use.  I knew that when I was ready to push my changes I could use interactive rebase to adjust my commit message for Gerrit and the world at large.  To do this, I will select the first commit in my plan’s edit list and I will click on the Reword button.  The action column will be updated to reflect the change.

reword.png

At this point, I haven’t done anything to actually change my commit message but, when I eventually click Start, the REWORD entry will cause the rebase process to pause to allow me to edit the commit message for that commit.

Fixup (Squash Commits and Discard Commit Messages)

Since I will be providing a Gerrit-appropriate commit message for the first commit, all I need to do now is squash the remaining commits into that one.  I will use the Fixup option because my intent is to discard all the commit messages except for the first.  I select all the commits except for the first and I click Fixup.  Again, the action column is updated to reflect the change.

fixup.png

Executing Rebase

Now that I have defined my rebase plan, it is time to execute it.  To do this, I click on the Start button.  Because I chose REWORD for the first entry, I will immediately be prompted to edit the commit message.

message.png

In this example, there is only one rebase plan entry that requires input, but you can tell which entry you are being prompted for because it is bold in the edit list, as you can see above.

In the above screenshot, I have formatted the first row of the commit message so as to cause an association to be created between my artifact and my commit (see my blog, Linking TeamForge Commits to Artifacts for a detailed explanation).  Because I included a Change-Id in my original commit, I can leave the last line in the commit message as it is.  To create a Gerrit change request, this Change-Id row must be included in the commit message when pushing to Gerrit (see my blog, TeamForge for Gerrit for a detailed explanation).

Once my commit message is the way I want it, I click OK.

Success!

Because none of my other rebase plan entries require user intervention, the next thing that I will see is notification that the rebase was successful.

success.png

Notice that the label decoration of the repository now indicates that there is just one outgoing commit whereas, if you refer back to the first screenshot, you will see that there were originally five.  We can further confirm that our interactive rebase was successful by looking again at the History view.

history_2.png

Push Changes

Now that I have a single commit that includes all my work as well as a Gerrit Change-Id in the commit message, I can go ahead and push my commit.

push_results.png

Change Request Created

By pushing my commit to Gerrit, I have created a new Gerrit change.

change_request.png

Another Example (Squash vs. Fixup)

Let’s look at a similar example, only this time I will use Squash rather than Fixup when defining my interactive rebase plan.

In this example I am working on another TeamForge artifact which represents a request that copyright information be added to the header of all the files that I created in the previous example.  In the screenshot below, you can see that I have made four commits since creating the local branch for my artifact.

history.png

Once again, I will open the History view, select the commit preceding the oldest commit that I want to rewrite, and then select Rebase Interactive from the context menu.  Again I will use REWORD for the first entry, but this time I will use SQUASH for the remaining entries.

rebase_plan.png

When I click Start, the rebase will pause on the last squash in the block of commits that are being squashed together.

commit_message.png

At this point, the commit message is a bit of a mess.  However, the text includes all four of the message from my commits, along with comments indicating which message belongs to which commit.  All I have to do now is use cut and paste to arrange them how I want.  Also, you’ll notice that I included a Change-Id in each of my four commits.  I’ll be sure to delete all but one of these.  I’ll also remove all the comments.  My fixed commit message looks like this:

commit_message_2.png

When I click OK, the interactive rebase completes successfully and I can see in the History view that, once again, my commits have been squashed into a single commit that is ready to be pushed to Gerrit.

history.png

Conclusion and Some Words of Caution

Interactive rebase is a powerful tool and I have only given you a small taste.  It can be used to fix problems but it can also be used to create problems!  Keep in mind that if you are working on an interactive rebase and it gets out of control, you can always click on Abort to stop the rebase and roll back to the starting point.  In this blog I have used interactive rebase to prepare commits for Gerrit because that is a situation in which the need often arises, but interactive rebase can be useful as a general Git tool, and is in no way limited to Gerrit.  You should not, however, use interactive rebase to rewrite commits that you have already pushed to a real (non-review) branch of a remote repository.  Your colleagues will hate you for it, and you will end up agreeing with them.

Steve Elsemore

Steve Elsemore is a Sr. Software Engineer at CollabNet. He works on Subclipse, GitEye and the CollabNet Desktop - Eclipse Edition.

Tagged with: , , ,
Posted in Agile, CloudForge, Git, TeamForge

Leave a Reply

Your email address will not be published. Required fields are marked *

*

CAPTCHA Image

*

connect with CollabNet
   Contact Us
Subscribe

Have new blog posts sent directly to your email.

looking for something
conversations

CloudForge: Join #CollabNet for the TeamForge® 8.1 release webinar and learn about its new powerful enterprise #Git features http://t.co/IHfnkoEfGr
Date: 1 September 2015 | 5:00 pm

CloudForge: Join this #CollabNet #webinar and learn how to reduce server loads with #Git replication and improve Git performance http://t.co/pB1DEsWFPh
Date: 31 August 2015 | 6:00 pm

CloudForge: Seamlessly integrate #Git upstream and downstream to tools such as #Jira and #Jenkins on this #CollabNet #webinar http://t.co/pB1DEsWFPh
Date: 28 August 2015 | 5:30 pm