2

I have a bare remote repository with source files in which I want to build only the changed files after it has been pushed to. I thought the best way to detect which files have been changed would be by putting the command changed_files=$(git diff-tree --no-commit-id --name-only -r HEAD) into a post-receive hook.

However, the variable ends up empty as I have verified by echoing it into a file. If I put HEAD^ instead of HEAD, it does show the changed files of the second to most recent commit. However, it doesn't show the most recent changes when I put HEAD but just shows nothing.

Can anyone help me? Or is there a smarter approach to my problem altogether?

I would definitely prefer a lean approach like automatically triggering a build with a push over one that would have to e. g. periodically check for changes.

Greg Bacon
  • 134,834
  • 32
  • 188
  • 245
Tobias Feil
  • 2,399
  • 3
  • 25
  • 41
  • 2
    What commit, specifically, does `HEAD` identify when the post-receive hook runs? Is it a merge commit? If so, what do you expect `git diff-tree` to do about that? Should you be using `HEAD` in the first place? What if the push went to branch `feature/X` but `HEAD` on the server refers to `master`? – torek Dec 15 '18 at 12:14
  • I only have a master branch, nothing being merged. I'm expecting it to show me the files changed in the most recent commit of what was just pushed. – Tobias Feil Dec 15 '18 at 15:15
  • OK, if it's not a merge commit, `gt diff-tree` should in fact compare `HEAD^` to `HEAD` in the simpler way. But you might want to compare the current value of a specific ref name to its previous value, rather than the current value of `HEAD` to the current value of `HEAD`'s parent. See [brian m. carlson's answer](https://stackoverflow.com/a/53795345/1256452). – torek Dec 15 '18 at 20:33
  • Note, by the way, that you can have *merge commits* without having any other *branch name*. In fact, if you're using `git pull` together with other people also doing `git push` and `git pull`, you probably are making what some call [foxtrot merges](https://stackoverflow.com/questions/35962754/git-how-can-i-prevent-foxtrot-merges-in-my-master-branch). – torek Dec 15 '18 at 20:39

2 Answers2

4

OK, I've figured it out: I was getting a

remote: fatal: ambiguous argument 'HEAD': both revision and filename

error in the push command which I had not noticed. After changing

changed_files=$(git diff-tree --no-commit-id --name-only -r HEAD)

to

changed_files=$(git diff-tree --no-commit-id --name-only -r HEAD --)

everything is working fine. Apparently, this is caused by the hook being executed in the .git directory of the remote repository, and there is a file called HEAD in that directory, which makes referring to the HEAD revision as HEAD ambiguous.

Tobias Feil
  • 2,399
  • 3
  • 25
  • 41
3

At the point the post-receive hook is executed, all the references have already been updated. Therefore, HEAD means the new head, not the old one. This may not produce the results you want, since it assumes that there is one non-merge commit and you want to diff with its parent, while you may have pushed a merge or multiple commits.

What you probably want to do is take advantage of the standard input which provides the old and new values. Something like the following will print the changed files as output from the remote side when you push:

#!/bin/sh

while read old new ref
do
    # Handle created or deleted branches.
    echo $old | grep -qsE '^0+$' && old=$(git hash-object -t tree /dev/null)
    echo $new | grep -qsE '^0+$' && new=$(git hash-object -t tree /dev/null)
    git diff-tree --no-commit-id --name-only -r "$old" "$new"
done
bk2204
  • 64,793
  • 6
  • 84
  • 100
  • This is going to be the right answer (so I've upvoted it), but note that `git diff-tree HEAD` will compare `HEAD` vs its parent-or-parents, not `HEAD` vs `HEAD`. I was guessing, apparently wrongly, that `HEAD` probably identified a merge commit, in which case a single-commit-argument diff-tree defaults to doing nothing (like the behavior of the diff section from `git log --raw`). – torek Dec 15 '18 at 20:38