Git, CIA and branch merging

Dear Joey, we also had this problem for dpkg, that’s why I hacked the /usr/local/bin/git-commit-notice script that we’re using on Alioth to do something like this instead:

while read oldrev newrev refname; do
    branchname=${refname#refs/heads/}
    [ "$branchname" = "master" ] && branchname=""
    for merged in $(git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --reverse --stdin $oldrev..$newrev); do
         /usr/local/bin/git-ciabot.pl $merged $branchname
    done
done

It will stop git rev-list each time that it encounters a commit that is available in any of the other branches present in the repository and thus when you merge a branch, you only see the merge commit in CIA.

You should also note that the script is smarter as it calls CIA only for branch updates, not for tag creation (and other kinds of updates) where it only leads to strange errors IIRC.

Additional Resources

Get the Debian Administrator's Handbook

After a successful liberation campaign, the Debian Administrator's Handbook is now freely available. If you appreciate my articles and what I do for Debian, check out the book and grab a copy.

Comments

  1. Anonymous says:

    And what Debian package can we find git-commit-notice in? :)

  2. Joey Hess says:

    Thanks.. Note that you have utf-8 hyphens rather than dashes, which makes it kinda hard to copy and paste. Also, git rev-list complained until I doubled the hyphens (–reverse –stdin). Aside from that, I think it works, pasted into my post-reveive as-is (no need to complicate things with the git-commit-notice script).

  3. Joey Hess says:

    Hmm, you also need to double the dashes in git rev-parse. (It accepts “-not”, but just outputs “-not”.)

    Even with that fixed, I’m still seeing floods if I merge master into a branch and push.

    Let’s see.. refname is refs/heads/branch, and ‘git rev-parse –not –branches’
    lists its ref, multiple times (once for the branch, once for master) but the grep filters it out. So rev-list doesn’t stop. I think it should only grep out one instance of $(git rev-parse $refname).

    So, currently untested:

    for merged in $(git rev-parse –not –branches | perl -e ‘$r=shift; while () { if (!$seen && /$r/) { $seen=1 } else { print } }’ $(git rev-parse $refname) | git rev-list –reverse –stdin $oldrev..$newrev); do
    /srv/git/ikiwiki.info/.git/hooks/ciabot_git.pl $merged $branchname
    done

  4. Anonymous, git-commit-notice is an Alioth-specific variation of the script that is in git’s contrib directory (/usr/share/doc/git-core/contrib/hooks/post-receive-email on a Debian system).

    Joey, I’m sorry for the dashes, but wordpress is trying to be smarter than it should… it’s replacing on the fly the double dashes and the quotes even if they are inside a <pre> or <code>.

    git rev-parse --not --branches list the current SHA1 of all branches but preceded by “^”. We remove the currently updated branch from that list and we use that list as many stop points for git rev-list thanks to its --stdin option. Why whould git rev-parse return the same commit multiple times ?

  5. I just installed a new plugin (wp-syntax) to be able to embed a code snippet properly… HTH. You get syntax highlighting as bonus.

  6. Joey Hess says:

    Buxy, git-rev-parse will list the same commit multiple times if there are multiple branches that used to diverge, but are all now merged to the same point.

    For example, if I have master, foo, and bar, and in both foo and bar, I do a ‘git merge -s theirs master’. Now, when I push foo and bar to the server, git-rev-parse will list the same sha1 3 times (with ^). But if all three are filtered out, all the stop points are gone.

  7. Joey, I don’t know git-merge-theirs (it’s not in sid’s git) but the usual merge strategies will not give the same commit if the previous commit is different so it’s a rather uncommon situation IMO.

    Anyway, maybe you can work-around this by using git rev-parse --symbolic-full-name --not --branches | grep -v $refname so that commits are not expanded to their sha1 and you will only grep out the right branch instead of all those that share the same sha1.

  8. Joey Hess says:

    Yeah, without a theirs (or ours?) merge, you have to have had a sequence of events where all merges between branches were fast-forwards to get into that state, I think .

    I think the –symbolic-full-name approach can work, though probably it should | egrep -v “^\^$refname$”

  9. Joey Hess says:

    Seems to work, but on reviewing, I think there’s still a problem. What if branches A and B are pushed at the same time? Then, when processing A, it will filter out all commits in B; when processing B it will filter all commits in A. And in the end, all commits can be filtered out, even though some are really new.

    I guess I rarely enough get into that situation that I can live with it though.

  10. It looks possible indeed (as the hook is called after the local refs have been updated apparently) but difficult to fix without completely rewriting the script to replace each stop condition with the old commit of the given refname. You’d have to parse the whole input and store the data in an array/hash to be able to refer to it. Not really worth the effort IMO. :)