disallow updating the named ref. Otherwise it should exit with
zero.
-The same hook is also called with an empty string as refname and
-no other arguments just before git-receive-pack exits. This can
-be used to implement repository wide cleanup task if needed.
-The exit code from this hook invocation is ignored; the only
-thing left for git-receive-pack to do at that point is to exit
-itself anyway.
-
Using this hook, it is easy to generate mails on updates to
the local repository. This example script sends a mail with
the commits pushed to the repository:
#!/bin/sh
- case "$#,$1" in
- 1,) # help packed repository pulled via dumb protocol.
- git-update-server-info
- ;;
- *) # mail out commit update information.
- if expr "$2" : '0*$' >/dev/null
- then
- echo "Created now ref."
+ # mail out commit update information.
+ if expr "$2" : '0*$' >/dev/null
+ then
+ echo "Created a new ref, with the following commits:"
git-rev-list --pretty "$2"
- else
- echo "New commits"
+ else
+ echo "New commits:"
git-rev-list --pretty "$3" "^$2"
- fi |
- mail -s "Changes to ref $1" commit-list@mydomain
- esac
+ fi |
+ mail -s "Changes to ref $1" commit-list@mydomain
exit 0
+Another hook $GIT_DIR/hooks/post-update, if exists and
+executable, is called with the list of refs that have been
+updated. This can be used to implement repository wide cleanup
+task if needed. The exit code from this hook invocation is
+ignored; the only thing left for git-receive-pack to do at that
+point is to exit itself anyway. This hook can be used, for
+example, to run "git-update-server-info" if the repository is
+packed and is served via a dumb transport.
+
+ #!/bin/sh
+ exec git-update-server-info
+
OPTIONS
-------
<directory>::
struct command {
struct command *next;
+ unsigned char updated;
unsigned char old_sha1[20];
unsigned char new_sha1[20];
char ref_name[0];
}
}
-static void update(const char *name, unsigned char *old_sha1, unsigned char *new_sha1)
+static int update(const char *name,
+ unsigned char *old_sha1, unsigned char *new_sha1)
{
char new_hex[60], *old_hex, *lock_name;
int newfd, namelen, written;
strcpy(new_hex, sha1_to_hex(new_sha1));
old_hex = sha1_to_hex(old_sha1);
if (!has_sha1_file(new_sha1))
- die("unpack should have generated %s, but I can't find it!", new_hex);
+ return error("unpack should have generated %s, "
+ "but I can't find it!", new_hex);
newfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);
if (newfd < 0)
- die("unable to create %s (%s)", lock_name, strerror(errno));
+ return error("unable to create %s (%s)",
+ lock_name, strerror(errno));
/* Write the ref with an ending '\n' */
new_hex[40] = '\n';
close(newfd);
if (written != 41) {
unlink(lock_name);
- die("unable to write %s", lock_name);
+ return error("unable to write %s", lock_name);
}
if (verify_old_ref(name, old_hex) < 0) {
unlink(lock_name);
- die("%s changed during push", name);
+ return error("%s changed during push", name);
}
if (run_update_hook(name, old_hex, new_hex)) {
unlink(lock_name);
- fprintf(stderr, "hook declined to update %s\n", name);
+ return error("hook declined to update %s\n", name);
}
else if (rename(lock_name, name) < 0) {
unlink(lock_name);
- die("unable to replace %s", name);
+ return error("unable to replace %s", name);
}
- else
+ else {
fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
+ return 0;
+ }
}
+static char update_post_hook[] = "hooks/post-update";
+
+static void run_update_post_hook(struct command *cmd)
+{
+ struct command *cmd_p;
+ int argc;
+ char **argv;
+
+ if (access(update_post_hook, X_OK) < 0)
+ return;
+ for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
+ if (!cmd_p->updated)
+ continue;
+ argc++;
+ }
+ argv = xmalloc(sizeof(*argv) * (1 + argc));
+ argv[0] = update_post_hook;
+
+ for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
+ if (!cmd_p->updated)
+ continue;
+ argv[argc] = xmalloc(strlen(cmd_p->ref_name) + 1);
+ strcpy(argv[argc], cmd_p->ref_name);
+ argc++;
+ }
+ argv[argc] = NULL;
+ run_command_v(argc, argv);
+}
/*
* This gets called after(if) we've successfully
struct command *cmd = commands;
while (cmd) {
- update(cmd->ref_name, cmd->old_sha1, cmd->new_sha1);
+ cmd->updated = !update(cmd->ref_name,
+ cmd->old_sha1, cmd->new_sha1);
cmd = cmd->next;
}
- run_update_hook("", NULL, NULL);
+ run_update_post_hook(commands);
}
static void read_head_info(void)
#include "run-command.h"
#include <sys/wait.h>
-static int run_external_command(int argc, const char **argv)
+int run_command_v(int argc, char **argv)
{
pid_t pid = fork();
return -ERR_RUN_COMMAND_FORK;
if (!pid) {
execvp(argv[0], (char *const*) argv);
- return -ERR_RUN_COMMAND_EXEC;
+ die("exec %s failed.", argv[0]);
}
for (;;) {
int status, code;
int run_command(const char *cmd, ...)
{
int argc;
- const char *argv[MAX_RUN_COMMAND_ARGS];
+ char *argv[MAX_RUN_COMMAND_ARGS];
const char *arg;
va_list param;
- fprintf(stderr, "run-command %s (%d)\n", cmd, ERR_RUN_COMMAND_EXEC);
-
va_start(param, cmd);
- argv[0] = cmd;
+ argv[0] = (char*) cmd;
argc = 1;
while (argc < MAX_RUN_COMMAND_ARGS) {
arg = argv[argc++] = va_arg(param, char *);
va_end(param);
if (MAX_RUN_COMMAND_ARGS <= argc)
return error("too many args to run %s", cmd);
- return run_external_command(argc, argv);
+ return run_command_v(argc, argv);
}