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.

3 Responses to “Git Workflow”

  1. Havoc says:

    Maybe not parsing what you’re saying, but I don’t see how ability to do cool patch massage makes any of the problems in my post go away… shouldn’t these cool features be *in addition* to also working well in the ways I describe in my post? What’s wrong with expecting git to also be good at the stuff I’m talking about there?

    I have never understood how “oh, but it does this other cool thing” is an answer to “xyz aspect of git kind of blows” ;-)

    I do definitely enjoy the ability to do the after-the-fact commit rearrangement.

  2. markmc says:

    Clearly, the true answer to “xyz aspect of git kind of blows” is “you’re clearly just too stupid to understand how awesome git is” :-)

    I was mostly replying to the “ChangeLog workflow is wrong” bit – i.e. the git log should should suffice as a ChangeLog and you can clean up your commits to give a nice log before pushing to the upstream repo.

    Don’t get me wrong, though – figuring out how to make git slightly less insanely difficult to learn is worth doing, but encouraging a slightly different workflow should be a part of that, IMHO

    Some quick comments on the other sections:

    – “no way I’m keeping all my stuff locally” – sounds to me like you want to keep
    your own personal backup repo on a server and push regularily to that, not
    necessarily push all your work-in-progress stuff upstream. Looking at the
    “git-push” manpage and the “git-remote” manpage, I bet you could set things
    up so you could just do “git-push backup” – e.g.

    [remote "backup"]
    url = ssh://hp@gnome.org/git/foo
    refspec = *

    It’d be cool if this was easier, but the difference with git is that you probably
    don’t want to push everything upstream this way, you just want to backup to
    another git repo somewhere.

    – “Deleting a remote branch” … “surprising syntax” – notting recently found
    that syntax by accident, and we had a fun half hour restoring his branch
    again :-)

    – “–force” and “screwing things up” – I think git-push –force is mainly there
    so that you can push a “rebased” tree … i.e. because you’ve rewritten history,
    the head you’re pushing isn’t an ancestor of the remote head, so it’s not a
    fast-forward push.

    The kernel guys are constantly getting into a flap about people using rebase
    too much, e.g. on public trees that a bunch of other people have based their
    work on. I reckon they’ll figure out one of these days how to make it so that
    some metadata is retained during rebasing so that rebasing isn’t such a
    “break the world event” … and then –force wouldn’t be needed to push a
    rebased tree.

  3. In the monotone/hg/git history model, rebasing is a intrinsically a “break the world event”, as it rewrites the history while it should be immutable.

    Adding metadata will probably make the whole model a bit messy and a lot less reliable.

    Only darcs got this right. The problem with darcs was that it got everything else wrong… :(