Saturday, 28 August 2010

Collapsing git repository history with rebase

Assuming you have a couple of git repository history that you want to wrap up together (after a conflict or some changes needed to be revert but you don't want to expose it), you might find git rebase quite handy. To do this, first thing we need is to run this :

$ git log
commit 83af3c6f8aaaf91dad91cc34c02092f520ce3327
Author: John Doe 
Date:   Fri Aug 25 17:55:21 2010 +0700

condition for js links passed as nokogiri object

commit d717a68c0f8199d27b50e08d74b2acc22b9a35d8
Author: John Doe 
Date:   Wed Aug 25 17:32:46 2010 +0700

latest revision, replaced indexed css for nokogiri search

commit 9c83b904880b33bd1a112b9bf43ec71155859f27
Author: Peter Piper 
Date:   Tue Aug 24 17:47:56 2010 -0400

move common asp javascript helpers into a module

commit 5ec142c344508d0c9f3b89b501646d2bb703ffb4
Author: John Doe 
Date:   Tue Aug 24 18:37:44 2010 +0700

deleting data_point variable declaration
We can see that there are 2 history listed on aug 25, while what we want is to let only 1 history on john doe's action right after peter piper's. So what we need to do is to run git rebase, like so :

$ git rebase -i HEAD~2

This line would mean, we want to collapse the last 2 commits from the log above and write down just one history afterwards. After we hit enter on that line above, we would find git nano such this :

$ git rebase -i HEAD~1
pick yyyyyy   latest revision, replaced indexed css for nokogiri search
pick xxxxxx   condition for js links passed as nokogiri object

# generate a pick list of all commits starting with 2bcdef
# Rebasing zzzzzz onto yyyyyyy 
# 
# Commands: 
#  pick = use commit 
#  edit = use commit, but stop for amending 
#  squash = use commit, but meld into previous commit 
# 
# If you remove a line here THAT COMMIT WILL BE LOST.
We need to let the first one to stick with pick (this list is ASC, unlike the log which would give you the latest changes first), and change the second line (or more if you have more to come) from pick to squash, this would mean the first act is the one with 'pick' and the ones with squash is the latter that we would want to combine. Follow with save and quit and we will get another editor as a reply :
# This is a combination of 2 commits.
# The first commit's message is:

condition for js links passed as nokogiri object

# This is the 2nd commit message:

latest revision, replaced indexed css for nokogiri search

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i nor -o; assuming --only paths...
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
# modified:   README
# modified:   lib/importer/asp_javascript_helper.rb
You might want to create a new commit message than describe the two, and then another save and quit and you'll get this message :

Created commit xxxxx: latest revision, replaced indexed css & js link condition for nokogiri
2 files changed, 27 insertions(+), 30 deletions(-)
create mode 100644 yyyyyy
Successfully rebased and updated refs/heads/master.

If somewhere between the two committed files happend to be a conflict, you will have a message stating that you need to resolve the conflict, this will happen after squash just before editting commit message. When this supposed to happen, you need the resolve the conflict(s) manually and then run :

$ git add filename_path
$ git rebase --continue

And all will be good again (^.^). Last thing to wrap this whole things up :

$ git push -f origin branchname

A little tip, the rebase will take place on an unknown temporary branch until you push it, if you're like me (completely paranoid type of person, won't believe on anyone or anything until you see the results with your own two eyes) you might want to run these below every now and then to make sure everything works the way & at the place you want it to be :

$ git reflog --oneline
$ git status
$ git branch

If you find it doesn't behave/do what you think it would do, you can abort the whole thing (as long you haven't push them).

$ git rebase --abort

As a closure, this is what you'll end up with :

$ git log
commit 83af3c6f8aaaf91dad91cc34c02092f5xxxxxxxxx
Author: John Doe 
Date:   Fri Aug 25 18:55:21 2010 +0700

latest revision, replaced indexed css & js link condition for nokogiri

commit 9c83b904880b33bd1a112b9bf43ec71155859f27
Author: Peter Piper 
Date:   Tue Aug 24 17:47:56 2010 -0400

move common asp javascript helpers into a module

commit 5ec142c344508d0c9f3b89b501646d2bb703ffb4
Author: John Doe 
Date:   Tue Aug 24 18:37:44 2010 +0700

deleting data_point variable declaration

Have a Git day (^.^)v

No comments:

Post a Comment