Skip to contentSkip to conversation

cherry-pick notes to find out cherry-picks from the origin

RFC In review Tejun Heo 2 participants 6 messages Oct 17, 2018 – Dec 6, 2018 Kbuild

lore
Share & tools
lore

Tejun Heo

author
#0 lore raw

RFC 1/1 cherry-pick notes to find out cherry-picks from the origin

Hello, Junio, Jeff.

A while ago, I proposed changes to name-rev and describe so that they can identify the commits cherry-picked from the one which is being shown.

https://public-inbox.org/git/[email protected]/T/

While the use-cases - e.g. tracking down which release / stable branches a given commit ended up in - weren't controversial, it was suggested that it'd make more sense to use notes to link cherry-picks instead of building the feature into name-rev.

The patch appended to this message implements most of it (sans tests and documentation). It's composed of the following two parts.

* A new built-in command note-cherry-picks, which walks the specified commits and if they're marked with the cherry-pick trailer, adds the backlink to the origin commit using Cherry-picked-to tag in a cherry-picks note.

* When formatting a cherry-picks note for display, nested cherry-picks are followed from each Cherry-picked-to tag and printed out with matching indentations.

Combined with name-rev --stdin, it can produce outputs like the following.

    commit 82cddd79f962de0bb1e7cdd95d48b48633335816 (branch2)
    Author: Tejun Heo <[email protected]>
    Date:   Wed Jul 25 21:31:35 2018 -0700

	commit 4

    (cherry picked from commit 10f7ce0a0e524279f022b48460c088a108b45d54 (master~1))
    (cherry picked from commit d433e3b4d5a19b3d29e2c8349fe88ceade5f6190 (branch1))

    commit d433e3b4d5a19b3d29e2c8349fe88ceade5f6190 (branch1)
    Author: Tejun Heo <[email protected]>
    Date:   Wed Jul 25 21:31:35 2018 -0700

	commit 4

	(cherry picked from commit 10f7ce0a0e524279f022b48460c088a108b45d54 (master~1))

    Notes (cherry-picks):
	Cherry-picked-to: 82cddd79f962de0bb1e7cdd95d48b48633335816 (branch2)

    commit 10f7ce0a0e524279f022b48460c088a108b45d54 (master~1)
    Author: Tejun Heo <[email protected]>
    Date:   Wed Jul 25 21:31:35 2018 -0700

	commit 4

    Notes (cherry-picks):
	Cherry-picked-to: d433e3b4d5a19b3d29e2c8349fe88ceade5f6190 (branch1)
	Cherry-picked-to:   82cddd79f962de0bb1e7cdd95d48b48633335816 (branch2)
	Cherry-picked-to: 2dd08fe869986c26bc1152a0bcec8c2fa48c50f7 (branch5)
	Cherry-picked-to: fa8b79edc5dfff21753c2ccfc1a1828336c4c070 (branch4~5)
	Cherry-picked-to:   a1fb6024e3bda5549de1d15d8fa37e8c3a7eecbe (branch4~2)
	Cherry-picked-to:     58964342321a65e316ff47db33f7063743bc0de8 (branch4)
	Cherry-picked-to:   45e0d5f31c869dcc89b9737853e64489e2ad80b0 (branch4~1)
	Cherry-picked-to: 58a8d36b2532feb0a14b4fc2a50d587e64f38324 (branch3)

Locally, the notes can be kept up-to-date with a trivial post-commit hook which invokes note-cherry-picks on the new commit; however, I'm having a bit of trouble figuring out a way to keep it up-to-date when multiple trees are involved. AFAICS, there are two options.

1. Ensuring that the notes are always generated on local commits and whenever new commits are received through fetch/pulls.

2. Ensuring that the notes are always generated on local commits and transported with push/pulls.

3. A hybrid approach - also generate notes on the receiving end and ensure that fetch/pulls receives the notes together (ie. similar to --tags option to git-fetch).

#1 seems simpler and more robust to me. Unfortunately, I can't see a way to implement any of the three options with the existing hooks. For #1, there's no post-fetch hook. For #2 and #3, there doesn't seem to be a fool-proof way to make sure that the notes are transported together. Any suggestions would be greatly appreciated.

Please let me know what you think.

Thanks.

---
 Makefile                    |    1 
 builtin.h                   |    1 
 builtin/note-cherry-picks.c |  197 ++++++++++++++++++++++++++++++++++++++++++++
 builtin/notes.c             |   17 ++-
 git.c                       |    1 
 notes.c                     |   95 +++++++++++++++++++++
 notes.h                     |    7 +
 object.c                    |    4 
 object.h                    |    6 +
 9 files changed, 320 insertions(+), 9 deletions(-)
Makefile +1 -0
index 5c8307b7c..fb0ff3ce9 100644
@@ -1073,6 +1073,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/note-cherry-picks.o
 BUILTIN_OBJS += builtin/pack-objects.o
 BUILTIN_OBJS += builtin/pack-redundant.o
 BUILTIN_OBJS += builtin/pack-refs.o
builtin.h +1 -0
index 962f0489a..d9d019abc 100644
@@ -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_note_cherry_picks(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);
builtin/note-cherry-picks.c new file +197 -0

197 lines not on this page · raw message

builtin/notes.c +10 -7
index c05cd004a..6b623b25c 100644
@@ -26,7 +26,7 @@
 
 static const char * const git_notes_usage[] = {
 	N_("git notes [--ref <notes-ref>] [list [<object>]]"),
-	N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+	N_("git notes [--ref <notes-ref>] add [-f] [-q] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
 	N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"),
 	N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
 	N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"),
@@ -394,7 +394,7 @@ static int append_edit(int argc, const char **argv, const char *prefix);
 
 static int add(int argc, const char **argv, const char *prefix)
 {
-	int force = 0, allow_empty = 0;
+	int force = 0, quiet = 0, allow_empty = 0;
 	const char *object_ref;
 	struct notes_tree *t;
 	struct object_id object, new_note;
@@ -416,6 +416,7 @@ static int add(int argc, const char **argv, const char *prefix)
 		OPT_BOOL(0, "allow-empty", &allow_empty,
 			N_("allow storing empty note")),
 		OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
+		OPT__QUIET(&quiet, N_("suppress informational messages")),
 		OPT_END()
 	};
 
@@ -455,8 +456,9 @@ static int add(int argc, const char **argv, const char *prefix)
 			argv[0] = "edit";
 			return append_edit(argc, argv, prefix);
 		}
-		fprintf(stderr, _("Overwriting existing notes for object %s\n"),
-			oid_to_hex(&object));
+		if (!quiet)
+			fprintf(stderr, _("Overwriting existing notes for object %s\n"),
+				oid_to_hex(&object));
 	}
 
 	prepare_note_data(&object, &d, note);
@@ -466,8 +468,9 @@ static int add(int argc, const char **argv, const char *prefix)
 			BUG("combine_notes_overwrite failed");
 		commit_notes(t, "Notes added by 'git notes add'");
 	} else {
-		fprintf(stderr, _("Removing note for object %s\n"),
-			oid_to_hex(&object));
+		if (!quiet)
+			fprintf(stderr, _("Removing note for object %s\n"),
+				oid_to_hex(&object));
 		remove_note(t, object.hash);
 		commit_notes(t, "Notes removed by 'git notes add'");
 	}
@@ -898,7 +901,7 @@ static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag
 static int remove_cmd(int argc, const char **argv, const char *prefix)
 {
 	unsigned flag = 0;
-	int from_stdin = 0;
+	int from_stdin = 0, quiet = 0;
 	struct option options[] = {
 		OPT_BIT(0, "ignore-missing", &flag,
 			N_("attempt to remove non-existent note is not an error"),
git.c +1 -0
index a6f4b44af..aedafff02 100644
@@ -512,6 +512,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 },
+	{ "note-cherry-picks", cmd_note_cherry_picks, 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 },
notes.c +95 -0

122 lines not on this page · raw message

notes.h +7 -0
index 414bc6855..b58899c74 100644
@@ -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/cherry-picks"
+#define NOTES_CHERRY_PICKED_TO		"Cherry-picked-to: "
+
 /*
  * Function type for combining two notes annotating the same object.
  *
@@ -317,4 +321,7 @@ void expand_notes_ref(struct strbuf *sb);
  */
 void expand_loose_notes_ref(struct strbuf *sb);
 
+void read_cherry_picks_note(const struct object_id *commit_oid,
+			    struct object_array *result);
+
 #endif
object.c +2 -2
index e54160550..f79652a34 100644
@@ -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++;
object.h +6 -0
index 0feb90ae6..ee14ce595 100644
@@ -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.
author
#1 lore raw

Ping, thanks.

Signature · 1 line
tejun
author
#2 lore raw
Quoted 1 line · Tejun Heo

Ping, thanks.

Ping again. Any comments? Wasn't this the direction you guys were suggesting?

Thanks.

Signature · 1 line
tejun
#3 lore raw
In reply to Tejun Heo
Quoted 10 lines · Tejun Heo

A while ago, I proposed changes to name-rev and describe so that they can identify the commits cherry-picked from the one which is being shown.

https://public-inbox.org/git/[email protected]/T/

While the use-cases - e.g. tracking down which release / stable branches a given commit ended up in - weren't controversial, it was suggested that it'd make more sense to use notes to link cherry-picks instead of building the feature into name-rev.

Sorry for the slow reply. This was on my to-look-at pile, but for some reason I accidentally put in my done pile.

Quoted 7 lines

The patch appended to this message implements most of it (sans tests and documentation). It's composed of the following two parts.

* A new built-in command note-cherry-picks, which walks the specified commits and if they're marked with the cherry-pick trailer, adds the backlink to the origin commit using Cherry-picked-to tag in a cherry-picks note.

That makes sense. I think this could also be an option to cherry-pick, to instruct it to create the note when the cherry-pick is made.

But you may still want a command to backfill older cherry-picks, or those done by other people who do not care themselves about maintaining the notes tree.

It _feels_ like this is something that should be do-able by plugging a few commands together, rather than writing a new C program. I.e., something like:

git rev-list --format='%(trailers)' HEAD |
perl -lne '

	/^commit ([0-9]+)/ and $commit = $1;
	/^\(cherry picked from commit ([0-9]+)/
		and print "$commit $1";

' |
while read from to; do
# One process per note isn't very efficient. Ideally there would # be an "append --stdin" mode. Double points if it understands # how to avoid adding existing lines.
git notes append -m "Cherry-picked-to: $to" $from
done

which is roughly what your program is doing. Not that I'm entirely opposed to doing something in C (we've been moving away from shell scripts anyway). But mostly I am wondering if we can leverage existing tools, and fill in their gaps in a way that lets people easily do similar things.

And on that note...

Quoted 3 lines

* When formatting a cherry-picks note for display, nested cherry-picks are followed from each Cherry-picked-to tag and printed out with matching indentations.

That makes sense to me, but does this have to be strictly related to cherry-picks? I.e., in the more generic form, could we have a way of marking a note as "transitive" for display, and the notes-display code would automatically recognize and walk hashes?

That would serve your purpose, but would also allow similar things to easily be done in the future.

Quoted 2 lines

Combined with name-rev --stdin, it can produce outputs like the following. [...]

Yeah, that looks pretty good.

Quoted 15 lines

Locally, the notes can be kept up-to-date with a trivial post-commit hook which invokes note-cherry-picks on the new commit; however, I'm having a bit of trouble figuring out a way to keep it up-to-date when multiple trees are involved. AFAICS, there are two options.

1. Ensuring that the notes are always generated on local commits and whenever new commits are received through fetch/pulls.

2. Ensuring that the notes are always generated on local commits and transported with push/pulls.

3. A hybrid approach - also generate notes on the receiving end and ensure that fetch/pulls receives the notes together (ie. similar to --tags option to git-fetch).

Quoted 5 lines

#1 seems simpler and more robust to me. Unfortunately, I can't see a way to implement any of the three options with the existing hooks. For #1, there's no post-fetch hook. For #2 and #3, there doesn't seem to be a fool-proof way to make sure that the notes are transported together. Any suggestions would be greatly appreciated.

Yeah, I think (1) is the simplest: it becomes a purely local thing that you've generated these annotations. Unfortunately, no, I don't think there's anything like a post-fetch hook. This might be a good reason to have one. One can always do "git fetch && update-notes" of course, but having fetch feed the script the set of updated ref tips would be very helpful (so you know you can traverse from $old..$new looking for cherry-picks).

For (2) and (3), you can push/pull a notes tree, but setting up the refspecs and config is fairly manual. Using the cherry-pick option I suggested above would most locally-made picks, but you'd probably still need to backfill sometimes anyway.

I don't think you'd need to be (nor am I sure you _could_ be) as fancy as fetching notes and their respective commits together. The notes are bound together in a single tree, so you cannot say "I don't have commit 1234abcd, I don't need the note for it". You get the whole tree. And that is not such a bad thing. The big reason _not_ to fetch tags that point to commits we don't have is that the tag fetch implies reachability, which may mean sucking in a big chunk of history. Whereas you can happily have a note pointing to a commit you don't have (though I suppose you should avoid running "git notes prune" in that case).

Quoted 11 lines
---
 Makefile                    |    1 
 builtin.h                   |    1 
 builtin/note-cherry-picks.c |  197 ++++++++++++++++++++++++++++++++++++++++++++
 builtin/notes.c             |   17 ++-
 git.c                       |    1 
 notes.c                     |   95 +++++++++++++++++++++
 notes.h                     |    7 +
 object.c                    |    4 
 object.h                    |    6 +
 9 files changed, 320 insertions(+), 9 deletions(-)

I only looked briefly over your implementation, but didn't see anything obviously wrong. I do think it would be nice to make it more generic, as much as possible. I think the most generic form is really:

traverse-and-show-trailers | invert-trailers | add-notes

In theory I should be able to do the same inversion step on any trailer which mentions another commit.

If it is going to stay in C and be cherry-pick-specific, one obvious improvement would be to use the notes API directly, rather than spawning subprocesses. That should be much more efficient if you have a lot of notes to write.

-Peff

author
#4 lore raw

Hello, Jeff.

Quoted 2 lines · Jeff King

Sorry for the slow reply. This was on my to-look-at pile, but for some reason I accidentally put in my done pile.

No worries and sorry about my late reply too. Things were a bit hectic.

Quoted 11 lines
Quoted 4 lines

* A new built-in command note-cherry-picks, which walks the specified commits and if they're marked with the cherry-pick trailer, adds the backlink to the origin commit using Cherry-picked-to tag in a cherry-picks note.

That makes sense. I think this could also be an option to cherry-pick, to instruct it to create the note when the cherry-pick is made.

But you may still want a command to backfill older cherry-picks, or those done by other people who do not care themselves about maintaining the notes tree.

So, I wanted to do both with the same command. git-cherry-pick knows which commits are new, so it can just pass those commits to note-cherry-picks. When backfilling the whole tree or newly pulled commits, the appropriate command can invoke note-cherry-picks with the new commits which should be super cheap.

Quoted 17 lines

It _feels_ like this is something that should be do-able by plugging a few commands together, rather than writing a new C program. I.e., something like:

git rev-list --format='%(trailers)' HEAD |
perl -lne '

	/^commit ([0-9]+)/ and $commit = $1;
	/^\(cherry picked from commit ([0-9]+)/
		and print "$commit $1";

' |
while read from to; do
# One process per note isn't very efficient. Ideally there would # be an "append --stdin" mode. Double points if it understands # how to avoid adding existing lines.
git notes append -m "Cherry-picked-to: $to" $from
done

Quoted 3 lines

which is roughly what your program is doing. Not that I'm entirely opposed to doing something in C (we've been moving away from shell scripts anyway). But mostly I am wondering if we can leverage existing

But the above wouldn't clean up stale commits, which could happen with e.g. abandoned releases, and would be prone to creating duplicates. We sure can add all those to shell / perl scripts but it's difficult for me to see the upsides of doing it that way.

Quoted 2 lines

tools, and fill in their gaps in a way that lets people easily do similar things.

I'll respond to this together below.

Quoted 6 lines

And on that note...

Quoted 3 lines

* When formatting a cherry-picks note for display, nested cherry-picks are followed from each Cherry-picked-to tag and printed out with matching indentations.

Quoted 7 lines

That makes sense to me, but does this have to be strictly related to cherry-picks? I.e., in the more generic form, could we have a way of marking a note as "transitive" for display, and the notes-display code would automatically recognize and walk hashes?

That would serve your purpose, but would also allow similar things to easily be done in the future.

Below.

Quoted 26 lines
Quoted 2 lines

Combined with name-rev --stdin, it can produce outputs like the following. [...]

Yeah, that looks pretty good.

Quoted 15 lines

Locally, the notes can be kept up-to-date with a trivial post-commit hook which invokes note-cherry-picks on the new commit; however, I'm having a bit of trouble figuring out a way to keep it up-to-date when multiple trees are involved. AFAICS, there are two options.

1. Ensuring that the notes are always generated on local commits and whenever new commits are received through fetch/pulls.

2. Ensuring that the notes are always generated on local commits and transported with push/pulls.

3. A hybrid approach - also generate notes on the receiving end and ensure that fetch/pulls receives the notes together (ie. similar to --tags option to git-fetch).

Quoted 5 lines

#1 seems simpler and more robust to me. Unfortunately, I can't see a way to implement any of the three options with the existing hooks. For #1, there's no post-fetch hook. For #2 and #3, there doesn't seem to be a fool-proof way to make sure that the notes are transported together. Any suggestions would be greatly appreciated.

Quoted 7 lines

Yeah, I think (1) is the simplest: it becomes a purely local thing that you've generated these annotations. Unfortunately, no, I don't think there's anything like a post-fetch hook. This might be a good reason to have one. One can always do "git fetch && update-notes" of course, but having fetch feed the script the set of updated ref tips would be very helpful (so you know you can traverse from $old..$new looking for cherry-picks).

This sounds great to me.

Quoted 8 lines

I only looked briefly over your implementation, but didn't see anything obviously wrong. I do think it would be nice to make it more generic, as much as possible. I think the most generic form is really:

traverse-and-show-trailers | invert-trailers | add-notes

In theory I should be able to do the same inversion step on any trailer which mentions another commit.

Hmm... yeah, I can definitely separate out the first part into a separate function, move dedup of notes to add-notes, and then glue them together.

I'm a bit hesitant to expose the first part as a separate command without knowing what other use cases would actually look like. I'll make it so that it's easy to do so when a new use case arises.

Quoted 4 lines

If it is going to stay in C and be cherry-pick-specific, one obvious improvement would be to use the notes API directly, rather than spawning subprocesses. That should be much more efficient if you have a lot of notes to write.

I'll try to make the components more generic and then glue them together in C and make it use the APIs directly.

Thanks.

Signature · 1 line
tejun
author
#5 lore raw

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(-)
Makefile +1 -0
index 1a44c811a..3c23ecf9d 100644
@@ -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
builtin.h +1 -0
index 6538932e9..51089e258 100644
@@ -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);
builtin/reverse-trailer-xrefs.c new file +148 -0

148 lines not on this page · raw message

git.c +1 -0
index 2f604a41e..4948c8e01 100644
@@ -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 },
notes.c +243 -2

296 lines not on this page · raw message

notes.h +10 -0
index 414bc6855..fb8153334 100644
@@ -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
object.c +2 -2
index e54160550..f79652a34 100644
@@ -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++;
object.h +6 -0
index 796792cb3..a0b3dd312 100644
@@ -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.
trailer.c +102 -0

111 lines not on this page · raw message

trailer.h +26 -0
index b99773964..5a9704e19 100644
@@ -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 */