cherry-pick notes to find out cherry-picks from the origin
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.
Ping, thanks.
Signature · 1 line
tejun
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
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
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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 */