One possibility would be for your post-receive
hook to:
- detect that the
post-receive
script is part of what is pushed.
(See "git post-receive hook to check files": git diff --name-only $1..$2|grep post-receive
)
- make a copy in
.git/hook/post-receive.new
Then you install a pre-receive
hook which simply check for .git/hook/post-receive.new
and rename it as .git/hook/post-receive
.
(Meaning post-receive.new disappear, and the next pre-receive
hook execution will do nothing)
That way, the hook does not update right away, but it will be updated at the next git push
to that same repo.
Note: I thought about detecting and updating the post-receive
file modification directly during the pre-receive
hook execution, but, as explained by torek in "Git pre-receive
hook to check config", this is not trivial:
A pre-receive
or update
hook is called after the new objects (commits, annotated tag objects, trees, and blobs) have been loaded into the repository, but before the references (branch names, tag names, etc) have been changed.
You would need, for each ref pushed, to diff and check for the presence and content of that file.
It is not impossible, as seen in this php script:
function get_changed_files($base, $commit) {
list($code, $stdout, $stderr) = git('diff', '--numstat', '--name-only', '--diff-filter=ACMRTUXB', '--ignore-submodules', "{$base}..{$commit}");
...
return explode("\n", $stdout);
}
function get_new_file($filename, $commit) {
list($code, $stdout, $stderr) = git('show', "{$commit}:{$filename}");
...
return $stdout;
}
...
$line = file_get_contents('php://stdin');
list($base, $commit, $ref) = explode(" ", trim($line));
if ($base == "0000000000000000000000000000000000000000") {
verbose("Initial push received. Expecting everything to be fine");
exit;
}
$modified = get_changed_files($base, $commit);
$result = true;
foreach ($modified as $fname) {
// if fname equals post-receive
$contents = get_new_file($fname, $commit);
// copy it to .git/hooks/post-receive
}
Having a two-step process is easier.