Git


In an earlier post I presented a number of rake tasks that were designed to ease git development. They helped mostly with managing branches, pulling from a remote branch (git-svn or plain ol’ git), and with pushing to remote branches. After using them for a while and getting others to use them, I discovered a problem…

They actually make things harder. Consider the user who doesn’t know git that well, is coming from Subversion, and wants something to help ease the transition. A set of rake tasks to make git feel a bit more like Subversion sounds like a good thing, right? Well, yes and no. Yes in that they should allow you to use the more familiar concepts, but no in that, as written, they didn’t educate you about what they were actually doing under the hood. So you never learned about git-stash, the difference between git-fetch and git-pull, and more of that sort of thing. This is bad because you will become dependent on the tools, which is a problem because…

Things go wrong. When a merge conflict happens in a rebase and most of the output is hidden from you, you can’t fix it because (a) you have very little information about the problem and (b) you don’t even know what a rebase is! Granted, good tools can and do help with this sort of thing by providing help when things go wrong, but my tasks just didn’t do it enough. After a while I got to thinking about the tasks as suitable for someone who didn’t know git, and that was my mistake, because…

These tools are meant for people who don’t need them. Only once I realized this did I start over, drop the sake-git project, stop using the thor-git project, and went back to pure git. After using that for a while, I noticed some of the same patterns that lead me to write the original tasks in the first place, so I decided to write (based on Rein H’s hack and ship) scripts for people who know what they’re doing, but want to do it faster. They’re written in bash, they’re fast, they tell you what’s going on, they don’t hide anything, and they’re a gist with an installer, but nothing else.

Enjoy.

Update: I’m ditching Sake for Thor. These tasks have been ported to thor and are available on github.

I’ve started using Git as my SCM of choice for Subversion projects over the last several months and have found that, while I don’t want to use Subversion anymore, there are some things it makes easier than git. For example, let’s say you’re working on something and you want to pull in the changes from other people on your team. With svn it’s simply:

$ svn up

With git things are different, since it only merges changesets and not locally changed files. This was a pain before git-stash came along, since I’d have to back out a change, update, and then reapply it. Even with git-stash things are a bit more painful. Here’s the equivalent to the above for a git-svn project:

$ git stash
$ git svn rebase
$ git stash apply

Oh, and that’s only if you’re on the master branch. If you’re on another one (and you should be), then here’s what it looks like if you want to keep master up to date too:

$ git stash
$ git checkout master
$ git svn rebase
$ git checkout mybranch
$ git rebase master
$ git stash apply

Whew! Note that this mostly applies to git-svn projects. For regular git projects a git-pull will do nicely.

I got sick of this, and I noticed that the Rubinius project uses a Rakefile to handle a fair number of the git commands, including updating and pushing. Here’s a Sake script that gives you two tasks: git:update and git:push which automatically check whether the project is a git-svn project and do the right thing. Install it like so:

$ sake -i http://pastie.caboo.se/147964.txt

And now we’re back to a one-liner:

$ sake git:update

Update: I just added git:open and git:close which you should think of as opening and closing issues. They just create and delete branches and can be used like this:

$ sake git:open
* Name your branch: ofx
* Switching to master
Switched to branch "master"
Switched to a new branch "ofx"
$ sake git:close
* Switching to master
* Deleting branch ofx

And don’t worry, git:close is safe and won’t destroy your work if you haven’t merged it yet:

$ sake git:close
* Switching to master
* Deleting branch ofx
* Branch ofx isn't a strict subset of master, quitting

Update: I gave this its own repo on github, so go forth, and git.

Git is split up into a whole bunch of executables like git-commit and git-status, but you can also run them by doing git commit and git status. Cool, right? But it doesn’t work with your own scripts, so you can’t just put git-wip in your PATH and have it work. To fill this need I cooked up a Zsh function that does what git should be doing itself:

    git() {
      for p in $(echo $PATH | tr ':' '\n'); do
        if [[ -x "$p/git-$1" ]]; then
          eval "$p/git-$1 $argv[2,-1]" && return
        fi
      done

      eval "/usr/bin/env git $argv"
    }

Update: The above function has the annoying side-effect that if the git command fails or any valid reasons (such as canceling a commit by not providing any message), it’ll try to run it again. For that reason, and since Coda showed me how to shell out with git aliases, I recommend not using this function.