Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a list of incoming commits on git push for a new branch in a hook

Tags:

git

hook

I'm writing a pre-receive hook to do some validation before accepting commits on the push. It works fine with existing branches since I use following git command to get a list of incoming commits:

git rev-list $old_sha1..$new_sha1

However, above command fails if user pushing a new branch into a shared repository because old_sha1 is all zeros. So the question is how do I get a list of incoming commits for a newly created branch. Doing git rev-list $new_sha1 does not work since it gives all revisions till the beginning of times. Is there a way to specify something like this:

git rev-list $branching_sha1..$new_sha1
like image 878
KiRPiCH Avatar asked Dec 24 '10 01:12

KiRPiCH


3 Answers

Try this:

git rev-list $new_sha1 $(git for-each-ref --format '^%(refname:short)' refs/heads/)

This uses git for-each-ref to print known refs; specifies in --format that the short name be output along with a preceding caret; and says with refs/heads/ to selecting just local heads. You end up running something like this:

git rev-list $new_sha1 ^master ^foo ^bar ^baz

The caret means “exclude commits reachable from here”. So this will give you all commits that are reachable from $new_sha1, but aren’t from any of the existing refs.

To handle the new-ref and existing-ref cases at once, you can use bash’s array support:

if ! [ $old_sha1 = 0000000000000000000000000000000000000000 ] ; then
    excludes=( ^$old_sha1 )
else
    excludes=( $(git for-each-ref --format '^%(refname:short)' refs/heads/) )
fi

git rev-list $new_sha1 "${excludes[@]}"
like image 189
Aristotle Pagaltzis Avatar answered Sep 18 '22 21:09

Aristotle Pagaltzis


More simply, you can use this:

git rev-list $new_sha1 --not --all

It's quite the same principle as in Aristotle Pagaltzis' answer, but it just uses rev-list's built-in options (which I guess were added after this question was answered...).
The --all parameter is equivalent to the for-each-ref part: it adds all known refs to the command args. The --not parameter, in this particular case, is like adding the ^ prefix to all refs added by --all.
Thus this command will print commits reachable from the new SHA1 except all those reachable from the known (i.e. already pushed) refs.

like image 28
fbastien Avatar answered Sep 21 '22 21:09

fbastien


The following helped in my case. I personally think it's very simple.

OLD_REV=$2
NEW_REV=$3

if [ $OLD_REV == "0000000000000000000000000000000000000000" ]; then
    OLD_REV=$(git merge-base master $NEW_REV)
fi;

git diff --name-only $OLD_REV $NEW_REV
like image 37
Kashif Nazar Avatar answered Sep 18 '22 21:09

Kashif Nazar