Hello, Jeff.

So, this is what I currently have.  It still does the same thing but a
lot more generic in terms of both interface and implementation.

* All core logics are implemented as core helpers / features.

  * Trailer parsing and reverse-mapping in trailer_rev_xrefs_*().

  * Note refs which start with xref- (cross-reference) are recognized
    by notes core.  When notes are added, a dedicated combine_notes
    function is used to remove duplicates and curl unreachable
    commits.  When xref- notes are formatted for printing, it
    automatically follows and prints nested xrefs.

* note-cherry-picks is replaced with reverse-trailer-xrefs which can
  use other trailers, note refs and tags.  --xref-cherry-picks option
  makes it use the cherry-pick presets.

Please note that the patch is still a bit rough.  I'm polishing and
documenting.  Please let me know what you think.

Thanks.

---
 Makefile                        |    1 
 builtin.h                       |    1 
 builtin/reverse-trailer-xrefs.c |  148 ++++++++++++++++++++++++
 git.c                           |    1 
 notes.c                         |  245 +++++++++++++++++++++++++++++++++++++++-
 notes.h                         |   10 +
 object.c                        |    4 
 object.h                        |    6 
 trailer.c                       |  102 ++++++++++++++++
 trailer.h                       |   26 ++++
 10 files changed, 540 insertions(+), 4 deletions(-)

diff --git a/Makefile b/Makefile
index 1a44c811a..3c23ecf9d 100644
--- a/Makefile
+++ b/Makefile
@@ -1086,6 +1086,7 @@ BUILTIN_OBJS += builtin/multi-pack-index.o
 BUILTIN_OBJS += builtin/mv.o
 BUILTIN_OBJS += builtin/name-rev.o
 BUILTIN_OBJS += builtin/notes.o
+BUILTIN_OBJS += builtin/reverse-trailer-xrefs.o
 BUILTIN_OBJS += builtin/pack-objects.o
 BUILTIN_OBJS += builtin/pack-redundant.o
 BUILTIN_OBJS += builtin/pack-refs.o
diff --git a/builtin.h b/builtin.h
index 6538932e9..51089e258 100644
--- a/builtin.h
+++ b/builtin.h
@@ -195,6 +195,7 @@ extern int cmd_multi_pack_index(int argc, const char 
**argv, const char *prefix)
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
 extern int cmd_notes(int argc, const char **argv, const char *prefix);
+extern int cmd_reverse_trailer_xrefs(int argc, const char **argv, const char 
*prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
 extern int cmd_patch_id(int argc, const char **argv, const char *prefix);
diff --git a/builtin/reverse-trailer-xrefs.c b/builtin/reverse-trailer-xrefs.c
new file mode 100644
index 000000000..b2879be6c
--- /dev/null
+++ b/builtin/reverse-trailer-xrefs.c
@@ -0,0 +1,148 @@
+#include "builtin.h"
+#include "cache.h"
+#include "strbuf.h"
+#include "repository.h"
+#include "config.h"
+#include "commit.h"
+#include "blob.h"
+#include "notes.h"
+#include "notes-utils.h"
+#include "trailer.h"
+#include "revision.h"
+#include "list-objects.h"
+#include "object-store.h"
+#include "parse-options.h"
+
+static const char * const reverse_trailer_xrefs_usage[] = {
+       N_("git reverse_trailer_xrefs [<options>] [<commit-ish>...]"),
+       NULL
+};
+
+static const char cherry_picked_prefix[] = "(cherry picked from commit ";
+static int verbose;
+
+static void clear_trailer_xref_note(struct commit *commit, void *data)
+{
+       struct notes_tree *tree = data;
+       int status;
+
+       status = remove_note(tree, commit->object.oid.hash);
+
+       if (verbose) {
+               if (status)
+                       fprintf(stderr, "Object %s has no note\n",
+                               oid_to_hex(&commit->object.oid));
+               else
+                       fprintf(stderr, "Removing note for object %s\n",
+                               oid_to_hex(&commit->object.oid));
+       }
+}
+
+static void record_trailer_xrefs(struct commit *commit, void *data)
+{
+       trailer_rev_xrefs_record(data, commit);
+}
+
+static int note_trailer_xrefs(struct notes_tree *tree,
+                             struct commit *from_commit, struct object_array 
*to_objs,
+                             const char *tag)
+{
+       char from_hex[GIT_MAX_HEXSZ + 1];
+       struct strbuf note = STRBUF_INIT;
+       struct object_id note_oid;
+       int i, ret;
+
+       oid_to_hex_r(from_hex, &from_commit->object.oid);
+
+       for (i = 0; i < to_objs->nr; i++) {
+               const char *hex = to_objs->objects[i].name;
+
+               if (tag)
+                       strbuf_addf(&note, "%s: %s\n", tag, hex);
+               else
+                       strbuf_addf(&note, "%s\n", tag);
+               if (verbose)
+                       fprintf(stderr, "Adding note %s -> %s\n", from_hex, 
hex);
+       }
+
+       ret = write_object_file(note.buf, note.len, blob_type, &note_oid);
+       strbuf_release(&note);
+       if (ret)
+               return ret;
+
+       ret = add_note(tree, &from_commit->object.oid, &note_oid, NULL);
+       return ret;
+}
+
+int cmd_reverse_trailer_xrefs(int argc, const char **argv, const char *prefix)
+{
+       static struct notes_tree tree;
+       struct rev_info revs;
+       int i, ret;
+       struct setup_revision_opt s_r_opt = {
+               .def = "HEAD",
+               .revarg_opt = REVARG_CANNOT_BE_FILENAME
+       };
+       int cherry = 0, clear = 0;
+       const char *trailer_prefix = NULL, *notes_ref = NULL, *tag = NULL;
+       struct option options[] = {
+               OPT_BOOL(0, "xref-cherry-picks", &cherry, N_("use options for 
xref-cherry-picks notes")),
+               OPT_STRING(0, "trailer-prefix", &trailer_prefix, N_("prefix"), 
N_("process trailers starting with <prefix>")),
+               OPT_STRING(0, "ref", &notes_ref, N_("notes-ref"), N_("update 
notes in <notes-ref>")),
+               OPT_STRING(0, "tag", &tag, N_("tag"), N_("tag xref notes with 
<tag>")),
+               OPT_BOOL(0, "clear", &clear, N_("clear trailer xref notes from 
the specified commits")),
+               OPT__VERBOSE(&verbose, N_("verbose")),
+               OPT_END()
+       };
+
+       git_config(git_default_config, NULL);
+
+       init_revisions(&revs, prefix);
+       argc = setup_revisions(argc, argv, &revs, &s_r_opt);
+       argc = parse_options(argc, argv, prefix, options,
+                            reverse_trailer_xrefs_usage, 0);
+
+       /* allow inidividual options to override parts of --cherry */
+       if (cherry) {
+               if (!trailer_prefix)
+                       trailer_prefix = cherry_picked_prefix;
+               if (!notes_ref)
+                       notes_ref = NOTES_CHERRY_PICKS_REF;
+               if (!tag)
+                       tag = NOTES_CHERRY_PICKED_TO_TAG;
+       }
+
+       if (!notes_ref || (!clear && (!trailer_prefix || !tag)))
+               die(_("insufficient arguments"));
+
+       if (argc > 1)
+               die(_("unrecognized argument: %s"), argv[1]);
+
+       if (!tree.initialized)
+               init_notes(&tree, notes_ref, NULL, NOTES_INIT_WRITABLE);
+
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
+
+       if (clear) {
+               traverse_commit_list(&revs, clear_trailer_xref_note, NULL, 
&tree);
+       } else {
+               struct trailer_rev_xrefs rxrefs;
+               struct commit *from_commit;
+               struct object_array *to_objs;
+
+               trailer_rev_xrefs_init(&rxrefs, trailer_prefix);
+               traverse_commit_list(&revs, record_trailer_xrefs, NULL, 
&rxrefs);
+
+               trailer_rev_xrefs_for_each(&rxrefs, i, from_commit, to_objs) {
+                       ret = note_trailer_xrefs(&tree, from_commit, to_objs,
+                                                tag);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       commit_notes(&tree, "Notes updated by 'git reverse-trailer-xrefs'");
+
+       return 0;
+}
diff --git a/git.c b/git.c
index 2f604a41e..4948c8e01 100644
--- a/git.c
+++ b/git.c
@@ -515,6 +515,7 @@ static struct cmd_struct commands[] = {
        { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
        { "name-rev", cmd_name_rev, RUN_SETUP },
        { "notes", cmd_notes, RUN_SETUP },
+       { "reverse-trailer-xrefs", cmd_reverse_trailer_xrefs, RUN_SETUP },
        { "pack-objects", cmd_pack_objects, RUN_SETUP },
        { "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },
        { "pack-refs", cmd_pack_refs, RUN_SETUP },
diff --git a/notes.c b/notes.c
index 25cdce28b..c32064bfe 100644
--- a/notes.c
+++ b/notes.c
@@ -9,6 +9,7 @@
 #include "tree-walk.h"
 #include "string-list.h"
 #include "refs.h"
+#include "hashmap.h"
 
 /*
  * Use a non-balancing simple 16-tree structure with struct int_node as
@@ -79,6 +80,10 @@ static struct notes_tree **display_notes_trees;
 
 static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
                struct int_node *node, unsigned int n);
+static void parse_xref_note(const char *note, unsigned long size,
+                           const struct object_id *commit_oid,
+                           struct object_array *result,
+                           struct string_list *result_lines);
 
 /*
  * Search the tree until the appropriate location for the given key is found:
@@ -914,6 +919,60 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,
        return ret;
 }
 
+int combine_notes_cat_xrefs(struct object_id *cur_oid,
+                           const struct object_id *new_oid)
+{
+       char *cur_msg = NULL, *new_msg = NULL;
+       unsigned long cur_len, new_len;
+       enum object_type cur_type, new_type;
+       struct object_array xrefs = OBJECT_ARRAY_INIT;
+       struct string_list lines = STRING_LIST_INIT_DUP;
+       struct strbuf output = STRBUF_INIT;
+       int i, j, cur_nr, ret;
+
+       /* read in both note blob objects */
+       if (!is_null_oid(new_oid))
+               new_msg = read_object_file(new_oid, &new_type, &new_len);
+       if (!new_msg || !new_len || new_type != OBJ_BLOB) {
+               free(new_msg);
+               return 0;
+       }
+       if (!is_null_oid(cur_oid))
+               cur_msg = read_object_file(cur_oid, &cur_type, &cur_len);
+       if (!cur_msg || !cur_len || cur_type != OBJ_BLOB) {
+               free(cur_msg);
+               free(new_msg);
+               oidcpy(cur_oid, new_oid);
+               return 0;
+       }
+
+       /* parse xrefs and de-dup */
+       parse_xref_note(cur_msg, cur_len, NULL, &xrefs, &lines);
+       cur_nr = xrefs.nr;
+       parse_xref_note(new_msg, new_len, NULL, &xrefs, &lines);
+
+       for (i = 0; i < cur_nr; i++)
+               for (j = cur_nr; j < xrefs.nr; j++)
+                       if (!strcmp(xrefs.objects[i].name,
+                                   xrefs.objects[j].name))
+                               lines.items[j].string[0] = '\0';
+
+       /* write out the combined object */
+       for (i = 0; i < lines.nr; i++)
+               if (lines.items[i].string[0] != '\0')
+                       strbuf_addf(&output, "%s\n", lines.items[i].string);
+
+       ret = write_object_file(output.buf, output.len, blob_type, cur_oid);
+
+       strbuf_release(&output);
+       object_array_clear(&xrefs);
+       string_list_clear(&lines, 0);
+       free(cur_msg);
+       free(new_msg);
+
+       return ret;
+}
+
 static int string_list_add_one_ref(const char *refname, const struct object_id 
*oid,
                                   int flag, void *cb)
 {
@@ -996,8 +1055,12 @@ void init_notes(struct notes_tree *t, const char 
*notes_ref,
        if (!notes_ref)
                notes_ref = default_notes_ref();
 
-       if (!combine_notes)
-               combine_notes = combine_notes_concatenate;
+       if (!combine_notes) {
+               if (starts_with(notes_ref, "refs/notes/xref-"))
+                       combine_notes = combine_notes_cat_xrefs;
+               else
+                       combine_notes = combine_notes_concatenate;
+       }
 
        t->root = (struct int_node *) xcalloc(1, sizeof(struct int_node));
        t->first_non_note = NULL;
@@ -1189,6 +1252,67 @@ void free_notes(struct notes_tree *t)
        memset(t, 0, sizeof(struct notes_tree));
 }
 
+/*
+ * Parse a "[TAG:]HEX" line.  @xref is trimmed.  If @tag_p is not NULL and
+ * TAG exists, the string is split.  Returns the pointer to the OID and
+ * *@tag_p is updated to the TAG if requested.
+ */
+static char *parse_xref(char *xref, char **tag_p)
+{
+       char *p, *hex;
+
+       while (isspace(*xref))
+               xref++;
+
+       p = strchr(xref, ':');
+       if (p) {
+               if (tag_p) {
+                       *tag_p = xref;
+                       *p = '\0';
+               }
+               p++;
+               while (isspace(*p))
+                       p++;
+               hex = p;
+       } else {
+               if (tag_p)
+                       *tag_p = NULL;
+               hex = xref;
+       }
+
+       p = hex;
+       while (*p != '\0' && !isspace(*p))
+               p++;
+       *p = '\0';
+       return hex;
+}
+
+static void walk_xrefs(const char *tree_ref, struct object_id *from_oid,
+                      struct strbuf *sb, int level)
+{
+       struct object_array xrefs = OBJECT_ARRAY_INIT;
+       struct string_list lines = STRING_LIST_INIT_DUP;
+       int i;
+
+       read_xref_note(tree_ref, from_oid, &xrefs, &lines);
+
+       for (i = 0; i < xrefs.nr; i++) {
+               char *line = lines.items[i].string;
+               char *tag;
+
+               parse_xref(line, &tag);
+               strbuf_addf(sb, "    %s%s%*s%s\n",
+                           tag ?: "", tag ? ": " : "", 2 * level, "",
+                           xrefs.objects[i].name);
+               if (xrefs.objects[i].item)
+                       walk_xrefs(tree_ref, &xrefs.objects[i].item->oid, sb,
+                                  level + 1);
+       }
+
+       object_array_clear(&xrefs);
+       string_list_clear(&lines, 0);
+}
+
 /*
  * Fill the given strbuf with the notes associated with the given object.
  *
@@ -1208,6 +1332,7 @@ static void format_note(struct notes_tree *t, const 
struct object_id *object_oid
        char *msg, *msg_p;
        unsigned long linelen, msglen;
        enum object_type type;
+       int format_xrefs;
 
        if (!t)
                t = &default_notes_tree;
@@ -1250,6 +1375,8 @@ static void format_note(struct notes_tree *t, const 
struct object_id *object_oid
                }
        }
 
+       format_xrefs = !raw && starts_with(t->ref, "refs/notes/xref-");
+
        for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
                linelen = strchrnul(msg_p, '\n') - msg_p;
 
@@ -1257,6 +1384,14 @@ static void format_note(struct notes_tree *t, const 
struct object_id *object_oid
                        strbuf_addstr(sb, "    ");
                strbuf_add(sb, msg_p, linelen);
                strbuf_addch(sb, '\n');
+
+               if (format_xrefs) {
+                       struct object_id oid;
+
+                       msg_p[linelen] = '\0';
+                       if (!get_oid_hex(parse_xref(msg_p, NULL), &oid))
+                               walk_xrefs(t->ref, &oid, sb, 1);
+               }
        }
 
        free(msg);
@@ -1309,3 +1444,109 @@ void expand_loose_notes_ref(struct strbuf *sb)
                expand_notes_ref(sb);
        }
 }
+
+struct notes_tree_entry {
+       struct hashmap_entry ent;
+       struct notes_tree tree;
+};
+
+static int notes_tree_cmp(const void *hashmap_cmp_fn_data,
+                         const void *entry, const void *entry_or_key,
+                         const void *keydata)
+{
+       const struct notes_tree_entry *e1 = entry;
+       const struct notes_tree_entry *e2 = entry_or_key;
+
+       return strcmp(e1->tree.ref, e2->tree.ref);
+}
+
+static void parse_xref_note(const char *note, unsigned long size,
+                           const struct object_id *commit_oid,
+                           struct object_array *result,
+                           struct string_list *result_lines)
+{
+       struct strbuf **lines, **pline;
+
+       lines = strbuf_split_buf(note, size, '\n', 0);
+
+       for (pline = lines; *pline; pline++) {
+               struct strbuf *line = *pline;
+               const char *target_hex;
+               struct object_id target_oid;
+               struct object *target_obj;
+
+               target_hex = parse_xref(line->buf, NULL);
+               if (get_oid_hex(target_hex, &target_oid)) {
+                       if (commit_oid)
+                               warning("read invalid sha1 on %s: %s",
+                                       oid_to_hex(commit_oid), line->buf);
+                       continue;
+               }
+
+               target_obj = parse_object(the_repository, &target_oid);
+               if (!target_obj || target_obj->type != OBJ_COMMIT) {
+                       if (commit_oid)
+                               warning("read invalid commit on %s: %s",
+                                       oid_to_hex(commit_oid), line->buf);
+                       continue;
+               }
+
+               add_object_array(target_obj, target_hex, result);
+               if (result_lines) {
+                       assert(result_lines->strdup_strings);
+                       string_list_append(result_lines, line->buf);
+               }
+       }
+
+       strbuf_list_free(lines);
+}
+
+/*
+ * Read a cross-referencing note.
+ *
+ * Notes in @notes_ref contains lines of "[TAG:]HEX" pointing to other
+ * commits.  Read the target commits and add the objects to @result.  If
+ * @result_lines is non-NULL, it should point to a strdup'ing string_list.
+ * The verbatim note lines matching the target commits are appened to the
+ * list.
+ */
+void read_xref_note(const char *notes_ref, const struct object_id *commit_oid,
+                   struct object_array *result,
+                   struct string_list *result_lines)
+{
+       static struct hashmap *notes_tree_map = NULL;
+       unsigned hash = memhash(notes_ref, strlen(notes_ref));
+       struct notes_tree_entry key, *ent;
+       const struct object_id *note_oid;
+       unsigned long size;
+       enum object_type type;
+       char *note;
+
+       if (!notes_tree_map) {
+               notes_tree_map = xcalloc(1, sizeof(struct hashmap));
+               hashmap_init(notes_tree_map, notes_tree_cmp, NULL, 0);
+       }
+
+       hashmap_entry_init(&key.ent, hash);
+       key.tree.ref = (char *)notes_ref;
+       ent = hashmap_get(notes_tree_map, &key, NULL);
+       if (!ent) {
+               ent = xcalloc(1, sizeof(struct notes_tree_entry));
+               init_notes(&ent->tree, notes_ref, NULL, 0);
+               hashmap_entry_init(&ent->ent, hash);
+               hashmap_put(notes_tree_map, ent);
+       }
+
+       note_oid = get_note(&ent->tree, commit_oid);
+       if (!note_oid)
+               return;
+
+       note = read_object_file(note_oid, &type, &size);
+       if (!size) {
+               free(note);
+               return;
+       }
+
+       parse_xref_note(note, size, commit_oid, result, result_lines);
+       free(note);
+}
diff --git a/notes.h b/notes.h
index 414bc6855..fb8153334 100644
--- a/notes.h
+++ b/notes.h
@@ -2,10 +2,14 @@
 #define NOTES_H
 
 #include "string-list.h"
+#include "object.h"
 
 struct object_id;
 struct strbuf;
 
+#define NOTES_CHERRY_PICKS_REF         "refs/notes/xref-cherry-picks"
+#define NOTES_CHERRY_PICKED_TO_TAG     "Cherry-picked-to"
+
 /*
  * Function type for combining two notes annotating the same object.
  *
@@ -38,6 +42,8 @@ int combine_notes_ignore(struct object_id *cur_oid,
                         const struct object_id *new_oid);
 int combine_notes_cat_sort_uniq(struct object_id *cur_oid,
                                const struct object_id *new_oid);
+int combine_notes_cat_xrefs(struct object_id *cur_oid,
+                           const struct object_id *new_oid);
 
 /*
  * Notes tree object
@@ -317,4 +323,8 @@ void expand_notes_ref(struct strbuf *sb);
  */
 void expand_loose_notes_ref(struct strbuf *sb);
 
+void read_xref_note(const char *notes_ref, const struct object_id *commit_oid,
+                   struct object_array *result,
+                   struct string_list *result_lines);
+
 #endif
diff --git a/object.c b/object.c
index e54160550..f79652a34 100644
--- a/object.c
+++ b/object.c
@@ -404,7 +404,7 @@ void object_array_clear(struct object_array *array)
 /*
  * Return true iff array already contains an entry with name.
  */
-static int contains_name(struct object_array *array, const char *name)
+int object_array_contains_name(struct object_array *array, const char *name)
 {
        unsigned nr = array->nr, i;
        struct object_array_entry *object = array->objects;
@@ -422,7 +422,7 @@ void object_array_remove_duplicates(struct object_array 
*array)
 
        array->nr = 0;
        for (src = 0; src < nr; src++) {
-               if (!contains_name(array, objects[src].name)) {
+               if (!object_array_contains_name(array, objects[src].name)) {
                        if (src != array->nr)
                                objects[array->nr] = objects[src];
                        array->nr++;
diff --git a/object.h b/object.h
index 796792cb3..a0b3dd312 100644
--- a/object.h
+++ b/object.h
@@ -172,6 +172,12 @@ typedef int (*object_array_each_func_t)(struct 
object_array_entry *, void *);
 void object_array_filter(struct object_array *array,
                         object_array_each_func_t want, void *cb_data);
 
+/*
+ * Returns 1 if array already contains an entry with the specified name.
+ * Otherwise, 0.
+ */
+int object_array_contains_name(struct object_array *array, const char *name);
+
 /*
  * Remove from array all but the first entry with a given name.
  * Warning: this function uses an O(N^2) algorithm.
diff --git a/trailer.c b/trailer.c
index 0796f326b..3afa38d25 100644
--- a/trailer.c
+++ b/trailer.c
@@ -2,6 +2,7 @@
 #include "config.h"
 #include "string-list.h"
 #include "run-command.h"
+#include "object-store.h"
 #include "commit.h"
 #include "tempfile.h"
 #include "trailer.h"
@@ -1170,3 +1171,104 @@ void format_trailers_from_commit(struct strbuf *out, 
const char *msg,
        format_trailer_info(out, &info, opts);
        trailer_info_release(&info);
 }
+
+implement_static_commit_slab(trailer_rxrefs_slab, struct object_array *);
+
+static struct object_array *get_trailer_rxrefs(
+                       struct trailer_rev_xrefs *rxrefs, struct commit *commit)
+{
+       struct object_array **slot =
+               trailer_rxrefs_slab_peek(&rxrefs->slab, commit);
+
+       return slot ? *slot : NULL;
+}
+
+static struct object_array *get_create_trailer_rxrefs(
+                       struct trailer_rev_xrefs *rxrefs, struct commit *commit)
+{
+       struct object_array **slot =
+               trailer_rxrefs_slab_at(&rxrefs->slab, commit);
+
+       if (*slot)
+               return *slot;
+
+       add_object_array(&commit->object, oid_to_hex(&commit->object.oid),
+                        &rxrefs->from_commits);
+       *slot = xmalloc(sizeof(struct object_array));
+       **slot = (struct object_array)OBJECT_ARRAY_INIT;
+       return *slot;
+}
+
+void trailer_rev_xrefs_init(struct trailer_rev_xrefs *rxrefs, const char *tag)
+{
+       rxrefs->tag = xstrdup(tag);
+       rxrefs->tag_len = strlen(tag);
+       init_trailer_rxrefs_slab(&rxrefs->slab);
+       rxrefs->from_commits = (struct object_array)OBJECT_ARRAY_INIT;
+}
+
+void trailer_rev_xrefs_record(struct trailer_rev_xrefs *rxrefs,
+                             struct commit *commit)
+{
+       struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+       enum object_type type;
+       unsigned long size;
+       void *buffer;
+       struct trailer_info info;
+       int i;
+
+       buffer = read_object_file(&commit->object.oid, &type, &size);
+       trailer_info_get(&info, buffer, &opts);
+
+       /* when nested, the last trailer describes the latest cherry-pick */
+       for (i = info.trailer_nr - 1; i >= 0; i--) {
+               char *line = info.trailers[i];
+
+               if (starts_with(line, rxrefs->tag)) {
+                       struct object_id from_oid;
+                       struct object *from_object;
+                       struct commit *from_commit;
+                       struct object_array *to_objs;
+                       char cherry_hex[GIT_MAX_HEXSZ + 1];
+
+                       if (get_oid_hex(line + rxrefs->tag_len, &from_oid))
+                               continue;
+
+                       from_object = parse_object(the_repository, &from_oid);
+                       if (!from_object || from_object->type != OBJ_COMMIT)
+                               continue;
+
+                       from_commit = (struct commit *)from_object;
+                       to_objs = get_create_trailer_rxrefs(rxrefs, 
from_commit);
+
+                       oid_to_hex_r(cherry_hex, &commit->object.oid);
+                       add_object_array(&commit->object, cherry_hex, to_objs);
+                       break;
+               }
+       }
+
+       free(buffer);
+}
+
+void trailer_rev_xrefs_release(struct trailer_rev_xrefs *rxrefs)
+{
+       clear_trailer_rxrefs_slab(&rxrefs->slab);
+       object_array_clear(&rxrefs->from_commits);
+       free(rxrefs->tag);
+}
+
+void trailer_rev_xrefs_next(struct trailer_rev_xrefs *rxrefs, int *idx_p,
+                           struct commit **from_commit_p,
+                           struct object_array **to_objs_p)
+{
+       if (*idx_p >= rxrefs->from_commits.nr) {
+               *from_commit_p = NULL;
+               *to_objs_p = NULL;
+               return;
+       }
+
+       *from_commit_p = (struct commit *)
+               rxrefs->from_commits.objects[*idx_p].item;
+       *to_objs_p = get_trailer_rxrefs(rxrefs, *from_commit_p);
+       (*idx_p)++;
+}
diff --git a/trailer.h b/trailer.h
index b99773964..5a9704e19 100644
--- a/trailer.h
+++ b/trailer.h
@@ -2,6 +2,8 @@
 #define TRAILER_H
 
 #include "list.h"
+#include "object.h"
+#include "commit-slab.h"
 
 struct strbuf;
 
@@ -99,4 +101,28 @@ void trailer_info_release(struct trailer_info *info);
 void format_trailers_from_commit(struct strbuf *out, const char *msg,
                                 const struct process_trailer_options *opts);
 
+declare_commit_slab(trailer_rxrefs_slab, struct object_array *);
+
+struct trailer_rev_xrefs {
+       char *tag;
+       int tag_len;
+       struct trailer_rxrefs_slab slab;
+       struct object_array from_commits;
+};
+
+void trailer_rev_xrefs_init(struct trailer_rev_xrefs *rxrefs, const char *tag);
+void trailer_rev_xrefs_record(struct trailer_rev_xrefs *rxrefs,
+                             struct commit *commit);
+void trailer_rev_xrefs_release(struct trailer_rev_xrefs *rxrefs);
+
+void trailer_rev_xrefs_next(struct trailer_rev_xrefs *rxrefs,
+                           int *idx_p, struct commit **from_commit_p,
+                           struct object_array **to_objs_p);
+
+#define trailer_rev_xrefs_for_each(rxrefs, idx, from_commit, to_objs)          
\
+       for ((idx) = 0,                                                         
\
+            trailer_rev_xrefs_next(rxrefs, &(idx), &(from_commit), 
&(to_objs));\
+            (from_commit);                                                     
\
+            trailer_rev_xrefs_next(rxrefs, &(idx), &(from_commit), &(to_objs)))
+
 #endif /* TRAILER_H */

Reply via email to