Showing only posts with topic "git" [.rss for this topic]. See all posts.

vim and git grep

[Update 2010-09-20: tweaked to run the grep on the git root instead of whatever directory the current file is in.]

After many months of screwing around with git.vim and fugitive.vim, I have finally found the perfect vim + git grep combination.

This incantation allows you to press <ctrl-x> twice on a symbol and have a minibuf pop up with all the occurrences of that symbol within the project. You can then jump to any occurrence by pressing enter on the corresponding line.

Place this in e.g. ~/.vim/plugin/git-grep.vim:

let g:gitgrepprg="git\\ grep\\ -n"
let g:gitroot="`git rev-parse --show-cdup`"

function! GitGrep(args)
    let grepprg_bak=&grepprg
    exec "set grepprg=" . g:gitgrepprg
    execute "silent! grep " . a:args . " " . g:gitroot
    botright copen
    let &grepprg=grepprg_bak
    exec "redraw!"
endfunction

func GitGrepWord()
  normal! "zyiw
  call GitGrep(getreg('z'))
endf
nmap <C-x><C-x> :call GitGrepWord()<CR>

git wtf bf06ab7 released

I’ve released git-wtf version bf06ab7. The highlight of this release is colorized output. ANSI escape sequences are the future of the web.

Also, the feature / integration branch comparisons is now only displayed when -r is supplied.

Check out the git-wtf home page for an example of the fancy colorization, or just download it now.

git-wtf 58b87fe9 released

I’ve released version 58b87fe9 of git-wtf, available here: http://git-wt-commit.rubyforge.org/git-wtf

This version contains a fairly major change: branches on origin are treated as equal to local branches, and branches that are remote-only are denoted with { }. So now there are three possible symbols: ( ) for local-only, { } for remote-only, and [ ] for branches that appear on both origin and your local repo.

The motivation was dealing with the fact that Sup has very many feature branches going at once, but I work on it on several different computers and typically only have a subset of them checked out. I didn’t want anyone to be left out….

I also fixed a few minor things like removing the restriction that version branches be local branches.

Sharing Conflict Resolutions in Git

Development of Sup is done with Git. Sup follows a topic branch methodology: features and bugfixes typically start off as “topic” branches from master, and are merged into an “integration”/“version” branch next for integration testing. After n cycles of additional bugfix commits to the topic branch, and re-merges into next, the topic branches are finally merged down to master, to be included in the next release.

I really like this approach because I think it evinces the real power of Git: that merges are so foolproof that I can pick and choose, on a feature-by-feature basis, which bits of code I want at each level of integration. That’s crazy cool. And users can stick to master if they want something stable, and next if they want the latest-and-greatest features.

The biggest problem I’ve had, though, is that long-lived topic branches often conflict with each other. This happens both when merging into next and when merging into master. I don’t think there’s a way around it; isolating features in this way has all the benefits above, but it also means that when they touch the same bits of code, you’ll get a conflict.

As a lazy maintainer, the biggest question I’ve had is: is there a way to push the burden of conflict resolution to the patch submitter? Is there a way for me to say: hey, your change conflicts with Bob’s. Can you resolve the conflict and send it to me?

One option I’ve considered is to have contributors to publish not only their feature branches, but their next branch as well. Assuming they aren’t mucking about with their next branch otherwise, if it contains just the merge commit, I can merge it into mine, and it should be a fast-forward that gets me the merge commit, conflict resolution and all.

But I don’t like that idea because, in every other case, I’m merging in the feature branches directly. Why should I suddenly start merging in next just because you have a conflict?

Furthermore, Sup primarily receives email contributions via git format-patch, and I do the dirty deed of sorting them into branches and merging things around. Requiring everyone to host a git repo iff they produce a conflicting patch seems silly. (And git format-patch, unfortunately, produces nothing for merge commits, even if they have conflict resolution changes. Maybe there’s a good reason for this, or maybe not. I’m not sure.)

After some effort, and some git-talk discussion, I have a solution. And no, it doesn’t involve sharing git-rerere caches. (Which it seems that some people do!)

For the contributor: once you have resolved the conflict, do a git diff HEAD^. This will output the conflict resolution changes. Email that to the maintainer along with your patch.

For the maintainer:

$ git checkout next
$ git merge <offending branch>
[... you have a conflict, yada yada ...]
$ git checkout next .
$ git apply --index <resolution patch filename>
$ git commit

Running git merge gets you to the point where you have a conflict. Running git checkout next . sets your working directory to the state it was before you merged. And git apply applies the resolution changes.

You lose authorship of the conflict resolution, but you can use git commit --author to set it.

I think the ideal solution would be for git format-patch to produce something usable in this case. I see some traffic on the Git list that suggests this is being considered, so hopefully one day this rigmarole will not be necessary.

git-wtf dd706855 released

I’ve released a version dd706855 of git-wtf, available here: http://git-wt-commit.rubyforge.org/git-wtf

I’ve tweaked the output format so that branches that don’t exist on the remote server are displayed with ()‘s and those that do with []’s, and ~ is the new symbol for a merge that only occurs on the local side.

I think this produces a better display; lots more information per line of ourput.

I’ve also added a couple random options which you can discover by reading the source. :)

The big next step I’d like to take with this thing is to support multiple remote repos better. Currently it’s kinda specific to your origin repo.

Some git-fu

Some git-fu I’ve been finding particularly useful recently:

  1. Untangling concurrent changes into multiple commits: git add -p is the greatest thing since sliced bread. But did you know it features an ‘s’ command which allows you to split a hunk into smaller hunks? Now you can untangle pretty much anything.
  2. Splitting a previous commit into multiple commits: I’ve been finding this one useful for quite a while. Start with a git rebase -i, mark the commit(s) as edit, and once you get there, do a git reset HEAD^. All the changes in that commit will be moved out of the staging area, and you can git add/git commit to your heart’s content. Finish with a quick git rebase --continue to the throat.
  3. Fixing your email address in previous commits: I often make a new repo and forget to change my email address. (For historical, and now silly, reasons, I like to commit to different projects from different addresses, and I often screw it up.) Here’s how to do a mass change: git filter-branch --env-filter "export GIT_AUTHOR_EMAIL=your.new.email.address" commit..HEAD, where commit is the first commit to be affected. Of course, changing the email address of a commit changes its id (and the id of all subsequent commits), so be careful if you’ve published them. (Also note that using --env-filter=... won’t work. No equal sign technology.)
  4. A git log that includes a list of files modified by each commit: git log --stat, which also gives you a colorized nice histogram of additions/deletions for each file. This is a nice middle ground between git log and git log -p.
  5. Speaking of git log -p, here’s how to make it sane in the presence of moves or renames: git log -p -C -M. Otherwise it doesn’t check for moves or copies, and happily gives you the full patch. (These should be on by default.)
  6. Comparing two branches: you can use git log --pretty=oneline one..two for changes in one direction (commits that ‘two’ has that ‘one’ doesn’t); and two..one for the opposite direction. You can also use the triple-dot operator to merge those two lists into one, but typically I find it useful to separate the two. Or you can check out git-wtf, which does this for you.
  7. Preview during commit message: git commit -v will paste the diff into your editor so you can review it while composing the commit message. (It won’t be included in the final message, of course.)
  8. gitk: don’t use it. You’ll get obsessive about merge commits, rebasing, etc., and it just doesn’t matter in the end. It took me about 4 months to recover from the bad mindset that gitk put me into.

Git 1.6.0 changes

Git 1.6.0 (just released) contains now detects Ruby class, module and method definitions in diff output. Previously it was just class names. (This patch.)

Other things I’m excited about in the new Git:

  1. git-clone --mirror is a handy way to set up a bare mirror repository.
  2. git-diff --check now checks for leftover merge conflict markers.
  3. git-stash save now has a —keep-index option. This lets you stash away the local changes and bring the changes staged in the index to your working tree for examination and testing.
  4. git-stash also has a new branch subcommand to create a new branch out of stashed changes.

ditz git integration plugin, in git

I’ve fleshed out ditz plugin architecture and just added a plugin that ties it more closely to git. With this plugin enabled, you can tie issues to feature branches and automatically get a list of commits on that branch (until they’re merged into master, at which point that becomes impossible, thanks to the magic of git).

Here’s an example: Sup’s configurable colors issue.

With these changes, Ditz is now firmly in the MVC camp. The models are created from yaml objects on disk; the views are an HTML renderer (using ERB) and a screen renderer (using puts technology), and the controller is the previously-mentioned operator.rb.

If you look at the plugin code you see that it need to modify all three of these components. It adds fields to the Issue and Config objects, it adds output to the HTML and screen views, and it adds commands to the controller. The fact that it can do this in a few lines of code is pretty sweet.

git-wtf

I’ve released a fairly preliminary version of git-wtf to my collection of Git tools. This is something I’ve been working on recently to help wean myself away from excessive gitk usage. From the description:

If you’re on a feature branch, it tells you which version branches it’s merged into. If you’re on a version branch, it tells you which feature branches are merged in and which aren’t. For every branch, if it’s a tracking branch, it tells you which commits need to be pulled and which need to be pushed.

So basically if you find yourself with a ton of branches (which invariably happens if you use feature branches in Git) or you find that keeping track of branch state is generally hard, and that gitk is confusing as often as it is useful, this is the tool for you.

By default it assumes that any branches named “master”, “next” or “edge” are version branches, and all other branches are feature branches. This is configurable, of course. It also warns, for tracked branches, if both the remote branch and the local branch have new commits, i.e. git pull would create a merge commit and you should rebase instead. If you don’t care about this type of thing, this might be annoying.

The main thing addition I foresee in the near future is a warning if merging in a feature branch into a version branch would collapse two version branches. Something like: when merging a feature branch into a version branch, warn if the feature branch contains commits reachable from any version branch and not reachable from master.

The Many Styles of Git

One of Git’s defining characteristics is its extreme (some say “ridiculous”) flexibility. Even with all the fancy porcelain on top, what you’re get when you use Git is basically a general DAG builder for patches, and the ability to apply labels to points within.

It’s interesting to see how this flexibility is put to use in practice. In my many years (ok, months) of Git usage, across a variety of projects, I’ve noticed several distinct styles of Git usage.

The most salient differences between styles are:

  • How much they care about keeping the development history “beautiful”, i.e. free of unnecessary merges. Git gives you two tools for adding your commit to a branch: merge and rebase. A rebase will always preserves linearity, a merge has the potential for introducing non-linearity. Some projects are fanatic about this. Linus has been known to reject code because there were too many “test merges” (see the git-rerere man page). Other projects don’t care at all.
  • How much they make use of topic branches. Some projects do the majority of development through them. Some do all development directly onto master, branching only for long-term divergent development.
  • How new commits come into the system: patches to mailing lists, merges from remote branches performed by the maintainer, or commits directly into the central repo.

Each of these decisions results in a different style of development. The styles I’ve encountered in the wild are:

  • The just-like-SVN approach. Example project: Rubinius. Individual contributers have a commit bit, or they don’t. Everyone works from local clones. If you have a commit bit, you push directly to origin/master. Non-committers can post patches to a mailinglist or to IRC. There are some published branches, but they’re for long-running lines of development that are eventually merged in and discarded. There’s no real pickiness about merges in development history; rebasing is encouraged but not required.
  • The Gitorious / Github approach. Example project: everything on those systems. Only the maintainer can commit to the central repository. Anyone can create a remote clone, push commits, and send a formal merge request through the system to the maintainer. All code addition (except for the maintainer’s additions) are done through merges.
  • The topic-based approach. Example projects: Git itself, the Linux kernel, Sup. Patches are submitted to the mailing list. The maintainer builds topic branches for each feature/bugfix in development and merges these into different “version branches”, which correspond to different versions of the project such as stable/experimental/released version distinctions. Sub-maintainers are used when the project gets large, and their repositories are merged by the maintainer upon request.
  • The remote topic branch approach. This was an experiment I tried with Ditz, and is roughly my attempt to do topic-based Git with Gitorious. In this approach, contributors, instead of submitting patches to a mailing list, maintain feature branches themselves. When a branch is updated, a merge request is sent to the maintainer, who merges the remote branch into a version branch.

I’ve listed the styles in order from least to most overhead. The just-like-SVN style requires very little knowledge of Git; at the other end of the spectrum, the topic-based approaches require a fair amount of branch managment. For example, care has to be taken that merging a topic branch into a version branch doesn’t accidentally merge another version branch in as well. (This type of complexity spurred me to write tools like git show-merges and the soon-to-be-released git wtf.)

The advantage of the topic-based approaches, of course, is that it’s possible to maintain concurrent versions of the same program at different levels of stability, and to pick and choose which features go where.

Which style is best for you depends on what you’re trying to accomplish. Like all good tools, what you get out of Git depends on what you’re willing to put into it, and that’s a decision you’ll have to make.