June 03, 2011

merging commits into a single patch with git-rebase

It is often the scenario that you have cloned a git repository and you have made some changes, now you cannot push to the repository and need to go through a reviewer, so you basically have to submit all your commits to the code as a single patch. Here is how :

We are going to use the repository http://https://github.com/eerpini/git_demo all through the process as the remote repository. It only has a single file, README, and has just one commit with the default branch.

1) Clone the repository :

$ git clone git@github.com:eerpini/git_demo.git

and then change into the directory git_demo.

2) Add a new file :

$ touch new_file
$ echo "Entering something into the new file" >> new_file


3) Since the new file is untracked, "git status" should show something like :

# On branch master
# Untracked files:
# (use "git add ..." to include in what will be committed)
#
# new_file
nothing added to commit but untracked files present (use "git add" to track)

4) Add the file and commit :

$ git add new_file
$ git commit -a -m "Adding a new file to the repository"

Note you can now get the change as a patch by doing "git format-patch -1 HEAD". But that is not our goal.

5) Just like above lets commit some more changes(add another file for simplicity).

$ touch new_file_2
$ echo "Entering something into the second new file" >> new_file_2
$ git add new_file_2
$ git commit -a -m "Added the second file to the repository, this should be the second commit"

6) Now we have two commits and "git status" should show you this :

# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
nothing to commit (working directory clean)

7)Now if you want to submit all the changes you have made so far as a single patch, you need to first rebase the two commits into a single one. This is how you do it :

$ git rebase -i origin/master

This will show you the following text in your editor :

pick d5d924c Adding a new file to the repository
pick c0883ac Added the second file to the repository, this should be the second commit

# Rebase 879598c..c0883ac onto 879598c
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.

Edit the above text, basically changing the keywords from pick to something else. If you want only the commit message from one of them, change all the others to fixup or squash. I used squash (edit as follows) :

pick d5d924c Adding a new file to the repository
squash c0883ac Added the second file to the repository, this should be the second commit

Now you can see that both the commits have been combined into a single commit. You can check either using "git log" or "git show HEAD". You can convert these changes into a single patch now using :

$ git format-patch -1 HEAD

In my case, this ended up being :

From 11d19a54021cccdf75e5f16d46eeff89c275be39 Mon Sep 17 00:00:00 2001
From: eerpini
Date: Fri, 3 Jun 2011 01:39:45 -0700
Subject: [PATCH] Adding a new file to the repository

Added the second file to the repository, this should be the second commit
---
new_file | 1 +
new_file_2 | 1 +
2 files changed, 2 insertions(+), 0 deletions(-)
create mode 100644 new_file
create mode 100644 new_file_2

diff --git a/new_file b/new_file
new file mode 100644
index 0000000..e3c484b
--- /dev/null
+++ b/new_file
@@ -0,0 +1 @@
+Entering something into the new file
diff --git a/new_file_2 b/new_file_2
new file mode 100644
index 0000000..b4a1e8e
--- /dev/null
+++ b/new_file_2
@@ -0,0 +1 @@
+Entering something into the second new file
--
1.7.5.2