Ancient History

In OpenStack, we have a particular problem where much of the early development on the project was done using bzr and launchpad. All this history is in git, but it can be difficult to find the bzr merge proposal in launchpad which caused a given commit to be merged.

Here’s an example of how I did it yesterday.

We’re interested in commit 8aea573:

commit 8aea573bd2e44e152fb4ef1627640bab1818dede
Author: Trey Morris ...
Date:   Tue Dec 28 23:55:58 2010 -0600

    initial lock functionality commit

To trace back to the merge commit which merged this into master, I did:

$> git log --graph --topo-order --ancestry-path --merges 8aea573bd2e44e152fb4ef1627640bab1818dede..HEAD
* commit ae5dbe2b5d4871d3e26e859c03feab705c9c59ea
  Merge: 9eca4d5 76e3923
  Author: Trey Morris ...
  Date:   Fri Jan 7 00:49:30 2011 +0000
  
      This branch implements lock functionality. The lock is stored in the compute worker database. Decorators have been added to the opens
  
* commit f9c33f4ba09e02f8668bdd655b7acba15984838c
  Merge: ba245da 9eca4d5
  Author: Trey Morris ...
  Date:   Thu Jan 6 16:35:48 2011 -0600
  
      merged trunk
  
* commit f09d1ce4d38f3a8ef72566e95cde38f1dc1b8bed
  Merge: 9b9b5fe 9a84a2b
  Author: Trey Morris ...
  Date:   Wed Dec 29 15:13:24 2010 -0600
  
      fixed merge conflict with trunk

Double check that by looking at exactly what was merged in:

$> git diff 9eca4d5..ae5dbe2
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index ce64ac7..f8d5e76 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -170,6 +170,50 @@ class Controller(wsgi.Controller):
             return faults.Fault(exc.HTTPUnprocessableEntity())
         return exc.HTTPAccepted()
 
+    def lock(self, req, id):
...

That’s the one!

Now how to find the merge proposal? Simply googling for “This branch implements lock functionality” quickly lead me to the correct merge prop, but better ideas welcome 🙂

Git Workflow

Havoc’s recent post on git was interesting because it shows how frustrating git can be if you try and treat it as “just another CVS”. From that perspective, git just seems like it’s just some bizarre way for kernel hackers to torture those who just want to get work done.

I turned that corner with git when I learned about “git-rebase -i” and came to the startling realisation that git’s history is editable. Basically, this allows you to change your workflow such that you can hack away at will, commit often and then rewrite the history of your hacking session so that you have a coherent set of patches/commits at the end of it with a useful changelog.

e.g. you can go from:

A1---B1---A2---A3---C1---B2---C2---C3

to:

A1---A2---A3---B1---B2---C1---C2---C3

or even:

A'---B'---C'

Using git rebasing, I found that I could use a similar workflow to using quilt with CVS, or mercurial with its patch queue (mq) extension. The revision history becomes less about tracking the progress of your work, and more a maleable mechanism for preparing patches before submitting upstream.

Red Hat Magazine has a nice article explaining all this, and I even picked up some new tricks to try out:

  • git-merge --squash : merge a branch/tag into the current branch, but squash all the commits together as an uncommitted change to the working tree. When you go to commit the result, the changelog of all the merged commits is available in the commit message editor so you can munge them together into a useful changelog.
  • git-cherry-pick --no-commit : apply the changes from a given commit to your working tree, but do not commit it. Could be used to achieve something similar to a squashed merge, but where you selectively merge only some of the commits.
  • git-add --patch/--interactive : add some changes from the working tree to the index, but e.g. selectively add only some of the patch hunks from a given file. Allows you to make a bunch of changes to a file, but commit the changes as individual commits.