From: Tejun Heo <hte...@fb.com>

* post-cherry-pick: Called after a cherry-pick and given parameters so
  that it can tell which are the new cherry-picks.

* post-fetch: Called after a fetch.  Each updated ref and sha1 are fed
  on stdin.

These two hooks will be used to keep refs/notes/xref-cherry-picks
up-to-date.

Signed-off-by: Tejun Heo <hte...@fb.com>
---
 Documentation/git-cherry-pick.txt |  5 +++
 Documentation/git-fetch.txt       |  5 +++
 Documentation/githooks.txt        | 23 ++++++++++
 builtin/fetch.c                   | 72 ++++++++++++++++++++++++++++---
 builtin/revert.c                  | 14 ++++++
 5 files changed, 114 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-cherry-pick.txt 
b/Documentation/git-cherry-pick.txt
index d35d771fc..527cb9fea 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -224,6 +224,11 @@ the working tree.
 spending extra time to avoid mistakes based on incorrectly matching
 context lines.
 
+HOOKS
+-----
+This command can run `post-cherry-pick` hook. See linkgit:githooks[5]
+for more information.
+
 SEE ALSO
 --------
 linkgit:git-revert[1]
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index e31993559..a04c6079a 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -290,6 +290,11 @@ fetched, making it impossible to check out that submodule 
later without
 having to do a fetch again. This is expected to be fixed in a future Git
 version.
 
+HOOKS
+-----
+This command can run `post-fetch` hook. See linkgit:githooks[5]
+for more information.
+
 SEE ALSO
 --------
 linkgit:git-pull[1]
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 959044347..24c122343 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -149,6 +149,14 @@ invoked after a commit is made.
 This hook is meant primarily for notification, and cannot affect
 the outcome of `git commit`.
 
+post-cherry-pick
+~~~~~~~~~~~~~~~~
+
+This hook is invoked by linkgit:git-cherry-pick[1]. This hook is
+called with two parameters. The first is `<old sha1>` and the second
+`<new sha1>`, where `<old sha1>..<new sha1>` describes all new
+cherry-picked commits.
+
 pre-rebase
 ~~~~~~~~~~
 
@@ -191,6 +199,21 @@ save and restore any form of metadata associated with the 
working tree
 (e.g.: permissions/ownership, ACLS, etc).  See contrib/hooks/setgitperms.perl
 for an example of how to do this.
 
+post-fetch
+~~~~~~~~~~
+This hook is called by linkgit:git-fetch[1] and can be used to process
+newly fetched commits and tags.
+
+Information about what was fetched is provided on the hook's standard
+input with lines of the form:
+
+  <local ref> SP <old sha1> SP <remote ref> SP <new sha1> LF
+
+where `<local ref>` got updated from `<old sha1>` to `<new sha1>` as a
+result of fetching `<remote ref>`. If a branch or tag was created,
+`<old_sha1>` will be 40 `0`. If a tag was pruned, `<remote_ref>` will
+be `(delete)` and <new sha1> will be 40 `0`.
+
 pre-push
 ~~~~~~~~
 
diff --git a/builtin/fetch.c b/builtin/fetch.c
index e0140327a..eac792a33 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -66,6 +66,7 @@ static struct refspec refmap = REFSPEC_INIT_FETCH;
 static struct list_objects_filter_options filter_options;
 static struct string_list server_options = STRING_LIST_INIT_DUP;
 static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
+static struct strbuf post_fetch_sb = STRBUF_INIT;
 
 static int git_fetch_config(const char *k, const char *v, void *cb)
 {
@@ -510,10 +511,24 @@ static struct ref *get_ref_map(struct remote *remote,
        return ref_map;
 }
 
+static void record_post_fetch(const char *name,
+                             const struct object_id *old_oid,
+                             const char *remote,
+                             const struct object_id *new_oid)
+{
+       char old_hex[GIT_MAX_HEXSZ + 1], new_hex[GIT_MAX_HEXSZ + 1];
+
+       oid_to_hex_r(old_hex, old_oid);
+       oid_to_hex_r(new_hex, new_oid);
+       strbuf_addf(&post_fetch_sb, "%s %s %s %s\n",
+                   name, old_hex, remote ?: "(delete)", new_hex);
+}
+
 #define STORE_REF_ERROR_OTHER 1
 #define STORE_REF_ERROR_DF_CONFLICT 2
 
 static int s_update_ref(const char *action,
+                       const char *remote,
                        struct ref *ref,
                        int check_old)
 {
@@ -546,6 +561,7 @@ static int s_update_ref(const char *action,
        ref_transaction_free(transaction);
        strbuf_release(&err);
        free(msg);
+       record_post_fetch(ref->name, &ref->old_oid, remote, &ref->new_oid);
        return 0;
 fail:
        ref_transaction_free(transaction);
@@ -726,7 +742,7 @@ static int update_local_ref(struct ref *ref,
            starts_with(ref->name, "refs/tags/")) {
                if (force || ref->force) {
                        int r;
-                       r = s_update_ref("updating tag", ref, 0);
+                       r = s_update_ref("updating tag", remote, ref, 0);
                        format_display(display, r ? '!' : 't', _("[tag 
update]"),
                                       r ? _("unable to update local ref") : 
NULL,
                                       remote, pretty_ref, summary_width);
@@ -766,7 +782,7 @@ static int update_local_ref(struct ref *ref,
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
                        check_for_new_submodule_commits(&ref->new_oid);
-               r = s_update_ref(msg, ref, 0);
+               r = s_update_ref(msg, remote, ref, 0);
                format_display(display, r ? '!' : '*', what,
                               r ? _("unable to update local ref") : NULL,
                               remote, pretty_ref, summary_width);
@@ -782,7 +798,7 @@ static int update_local_ref(struct ref *ref,
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
                        check_for_new_submodule_commits(&ref->new_oid);
-               r = s_update_ref("fast-forward", ref, 1);
+               r = s_update_ref("fast-forward", remote, ref, 1);
                format_display(display, r ? '!' : ' ', quickref.buf,
                               r ? _("unable to update local ref") : NULL,
                               remote, pretty_ref, summary_width);
@@ -797,7 +813,7 @@ static int update_local_ref(struct ref *ref,
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
                        check_for_new_submodule_commits(&ref->new_oid);
-               r = s_update_ref("forced-update", ref, 1);
+               r = s_update_ref("forced-update", remote, ref, 1);
                format_display(display, r ? '!' : '+', quickref.buf,
                               r ? _("unable to update local ref") : _("forced 
update"),
                               remote, pretty_ref, summary_width);
@@ -1071,8 +1087,11 @@ static int prune_refs(struct refspec *rs, struct ref 
*ref_map,
        if (!dry_run) {
                struct string_list refnames = STRING_LIST_INIT_NODUP;
 
-               for (ref = stale_refs; ref; ref = ref->next)
+               for (ref = stale_refs; ref; ref = ref->next) {
                        string_list_append(&refnames, ref->name);
+                       record_post_fetch(ref->name, &ref->old_oid,
+                                         NULL, &null_oid);
+               }
 
                result = delete_refs("fetch: prune", &refnames, 0);
                string_list_clear(&refnames, 0);
@@ -1561,6 +1580,47 @@ static int fetch_one(struct remote *remote, int argc, 
const char **argv, int pru
        return exit_code;
 }
 
+static int run_post_fetch_hook(void)
+{
+       int ret = 0, x;
+       struct child_process proc = CHILD_PROCESS_INIT;
+       const char *argv[2];
+
+       if (!(argv[0] = find_hook("post-fetch")))
+               return 0;
+       argv[1] = NULL;
+
+       proc.argv = argv;
+       proc.in = -1;
+
+       if (start_command(&proc)) {
+               finish_command(&proc);
+               return -1;
+       }
+
+       sigchain_push(SIGPIPE, SIG_IGN);
+
+       if (write_in_full(proc.in, post_fetch_sb.buf, post_fetch_sb.len) < 0) {
+               /* We do not mind if a hook does not read all refs. */
+               if (errno != EPIPE)
+                       ret = -1;
+       }
+
+       strbuf_release(&post_fetch_sb);
+
+       x = close(proc.in);
+       if (!ret)
+               ret = x;
+
+       sigchain_pop(SIGPIPE);
+
+       x = finish_command(&proc);
+       if (!ret)
+               ret = x;
+
+       return ret;
+}
+
 int cmd_fetch(int argc, const char **argv, const char *prefix)
 {
        int i;
@@ -1669,6 +1729,8 @@ int cmd_fetch(int argc, const char **argv, const char 
*prefix)
 
        close_all_packs(the_repository->objects);
 
+       run_post_fetch_hook();
+
        argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
        if (verbosity < 0)
                argv_array_push(&argv_gc_auto, "--quiet");
diff --git a/builtin/revert.c b/builtin/revert.c
index c93393c89..0b7e578cc 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -8,6 +8,8 @@
 #include "dir.h"
 #include "sequencer.h"
 #include "branch.h"
+#include "refs.h"
+#include "run-command.h"
 
 /*
  * This implements the builtins revert and cherry-pick.
@@ -223,12 +225,24 @@ int cmd_revert(int argc, const char **argv, const char 
*prefix)
 int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 {
        struct replay_opts opts = REPLAY_OPTS_INIT;
+       struct object_id old_oid, new_oid;
+       char old_hex[GIT_MAX_HEXSZ + 1], new_hex[GIT_MAX_HEXSZ + 1];
        int res;
 
+       if (read_ref("HEAD", &old_oid))
+               die(_("failed to read HEAD, cherry-pick failed"));
+
        opts.action = REPLAY_PICK;
        sequencer_init_config(&opts);
        res = run_sequencer(argc, argv, &opts);
        if (res < 0)
                die(_("cherry-pick failed"));
+
+       if (read_ref("HEAD", &new_oid))
+               die(_("failed to read HEAD after cherry-pick"));
+
+       oid_to_hex_r(old_hex, &old_oid);
+       oid_to_hex_r(new_hex, &new_oid);
+       run_hook_le(0, "post-cherry-pick", old_hex, new_hex, NULL);
        return res;
 }
-- 
2.17.1

Reply via email to