[PATCHSET] git-reverse-trailer-xrefs: Reverse map cherry-picks and other cross-references
Hello, Some trailers refer to other commits. Let's call them xrefs (cross-references). For example, a cherry pick trailer points to the source commit. It is sometimes useful to build a reverse map of these xrefs - ie. source -> cherry-pick instead of cherry-pick -> source. This, e.g, can answer which releases a commit ended up in on repositories which cherry-picks fixes back from the development branch. Let's say the repository looks like the following. D'---E'' release-B / C' E' release-D / / A---B---C---D---E master where the followings cherry-picks took place. C -> C' D -> D' E -> E' -> E'' Using the reverse-mapping in this patchset, we can get the following annotated log which succinctly shows which commit ended up where. $ git log --notes=xref-cherry-picks --oneline | git name-rev --name-only --stdin 4b165af commit E Notes (xref-cherry-picks): Cherry-picked-to: release-D Cherry-picked-to: release-B 82bf1f3 commit D Notes (xref-cherry-picks): Cherry-picked-to: release-B~1 364eccf commit C Notes (xref-cherry-picks): Cherry-picked-to: release-B~2 ed3adf3 commit B ddd1bf2 commit A This patchset implements generic trailer cross-reference reverse-mapping and the necessary hooks and samples to keep cherry-pick reverse-maps up-to-date. diffstat follows. Thanks. Documentation/git-cherry-pick.txt |5 Documentation/git-fetch.txt |5 Documentation/git-notes.txt |8 Documentation/git-reverse-trailer-xrefs.txt | 145 +++ Documentation/githooks.txt | 23 ++ Makefile|1 builtin.h |1 builtin/fetch.c | 72 +++ builtin/reverse-trailer-xrefs.c | 160 + builtin/revert.c| 14 + git.c |1 notes-merge.c |9 notes-utils.c |2 notes-utils.h |3 notes.c | 260 +++- notes.h |9 t/t3321-notes-xref-cherry-picks.sh | 124 + templates/hooks--post-cherry-pick.sample|8 templates/hooks--post-fetch.sample | 30 +++ trailer.c | 105 +++ trailer.h | 38 21 files changed, 1015 insertions(+), 8 deletions(-) -- tejun
[PATCH 3/5] notes: Implement git-reverse-trailer-xrefs
From: Tejun Heo Some trailers refer to other commits. Let's call them xrefs (cross-references). For example, a cherry pick trailer points to the source commit. It is sometimes useful to build a reverse map of these xrefs - ie. source -> cherry-pick instead of cherry-pick -> source. This patch implements git-reverse-trailer-xrefs which can process a given trailer for the specified commits and generate and update the matching refs/notes/xref- notes. The command reverse-maps trailers beginning with --trailer-prefix and stores them in --ref notes with each line tagged with --tag. It also has a refs/notes/xref-cherry-picks preset for the three params. Signed-off-by: Tejun Heo --- Documentation/git-reverse-trailer-xrefs.txt | 136 + Makefile| 1 + builtin.h | 1 + builtin/reverse-trailer-xrefs.c | 160 git.c | 1 + 5 files changed, 299 insertions(+) create mode 100644 Documentation/git-reverse-trailer-xrefs.txt create mode 100644 builtin/reverse-trailer-xrefs.c diff --git a/Documentation/git-reverse-trailer-xrefs.txt b/Documentation/git-reverse-trailer-xrefs.txt new file mode 100644 index 0..20d260486 --- /dev/null +++ b/Documentation/git-reverse-trailer-xrefs.txt @@ -0,0 +1,136 @@ +git-reverse-trailer-xrefs(1) + + +NAME + +git-reverse-trailer-xrefs - Record reverse-map of trailer commit references into notes + +SYNOPSIS + +[verse] +`git reverse_trailer_xrefs` --xref-cherry-picks [--clear] [] [...] +`git reverse_trailer_xrefs` --trailer-prefix= --ref= [--tag=] [] [...] +`git reverse_trailer_xrefs` --ref= --clear [] [...] + + +DESCRIPTION +--- +Record or clear reverse-map of trailer commit references in the +specified notes ref. + +Some commit trailers reference other commits. For example, +`git-cherry-pick -x` adds the following trailer to record the source +commit. +-- +(cherry picked from commit ) +-- +The reverse mappings of such cross references can be useful. For +cherry-picks, it would allow finding all the cherry-picked commits of +a given source commit. `git-reverse-trailer-xrefs` can be used to +create and maintain such reverse mappings in notes. + +When used with `--xref-cherry-picks`, the cherry-pick trailers are +parsed from the specified commits and the reverse mappings are +recorded in the `refs/notes/xref-cherry-picks` notes of the source +commits in the following format. +-- +Cherry-picked-to: +-- + +When a note with its notes ref starting with `refs/notes/xref-` is +formatted to be displayed with the commit for, e.g., `git-show` or +`git-log`, the destination commit is followed recursively and the +matching notes are shown with increasing level of indentations. + +`--trailer-prefix`, `--notes` and `--tag` can be used to use a custom +set of trailer, notes ref and reverse mapping tag. + +OPTIONS +--- +...:: + Commit-ish object names to describe. Defaults to HEAD if omitted. + +--xref-cherry-picks:: + Use the preset to reverse map `git-cherry-pick -x` + trailers. `--trailer-prefix` is set to `(cherry-picked from + commit `, `--notes` is set to `refs/notes/xref-cherry-picks` + and `--tag` is set to `Cherry-picked-to`. This option can't be + specified with the three preset options. + +--trailer-prefix=:: + Process trailers which start with . It is matched + character-by-character and should be followed by the + referenced commit ID. When there are multiple matching + trailers, the last one is used. + +--notes=:: + The notes ref to use for the reverse mapping. While this can + be any notes ref, it is recommented to use ones starting with + `refs/notes/xref-` as they are recognized as cross-referencing + notes and handled specially when updating and showing. + +--tag=:: + Optional tag to use when generating reverse reference + notes. If specified, each note line is formatted as `: + `; otherwise, ``. + +--clear:: + Instead of creating reverse mapping notes, clear them from the + specified commits. + + +EXAMPLES + + +Assume the following history. Development is happening in "master" and +releases are branched off and fixes are cherry-picked into them. + + + D'---E'' release-B + / + C' E' release-D + / / +A---B---C---D---E master + + +The following cherry-picks took place. + + +C -> C' +D -> D' +E -> E' -> E'' + + +The reverse mappings for all commits can be created using the +following command. + + +$ git reverse-trailer-xrefs --all --xref-cherry-picks + + +With the notes added, where each commit ended up can be easily +d
[PATCH 1/5] trailer: Implement a helper to reverse-map trailer xrefs
From: Tejun Heo Some trailers refer to other commits. Let's call them xrefs (cross-references). For example, a cherry pick trailer points to the source commit. It is sometimes useful to build a reverse map of these xrefs - ie. source -> cherry-pick instead of cherry-pick -> source. This patch implements trailer helpers to enable such reverse maps. The helpers can process arbitrary trailers and once the xrefs are recorded, the reverse-mapping can be iterated per source commit using trailer_rev_xrefs_for_each(). Signed-off-by: Tejun Heo --- trailer.c | 105 ++ trailer.h | 38 2 files changed, 143 insertions(+) diff --git a/trailer.c b/trailer.c index 0796f326b..15744600b 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,107 @@ 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->dst_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 *trailer_prefix) +{ + rxrefs->trailer_prefix = xstrdup(trailer_prefix); + rxrefs->trailer_prefix_len = strlen(trailer_prefix); + init_trailer_rxrefs_slab(&rxrefs->slab); + rxrefs->dst_commits = (struct object_array)OBJECT_ARRAY_INIT; +} + +/* record the reverse mapping of @commit's xref trailer */ +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->trailer_prefix)) { + struct object_id dst_oid; + struct object *dst_object; + struct commit *dst_commit; + struct object_array *src_objs; + char cherry_hex[GIT_MAX_HEXSZ + 1]; + + if (get_oid_hex(line + rxrefs->trailer_prefix_len, + &dst_oid)) + continue; + + dst_object = parse_object(the_repository, &dst_oid); + if (!dst_object || dst_object->type != OBJ_COMMIT) + continue; + + dst_commit = (struct commit *)dst_object; + src_objs = get_create_trailer_rxrefs(rxrefs, dst_commit); + + oid_to_hex_r(cherry_hex, &commit->object.oid); + add_object_array(&commit->object, cherry_hex, src_objs); + break; + } + } + + free(buffer); +} + +void trailer_rev_xrefs_release(struct trailer_rev_xrefs *rxrefs) +{ + clear_trailer_rxrefs_slab(&rxrefs->slab); + object_array_clear(&rxrefs->dst_commits); + free(rxrefs->trailer_prefix); +} + +void trailer_rev_xrefs_next(struct trailer_rev_xrefs *rxrefs, int *idx_p, + struct commit **dst_commit_p, + struct object_array **src_objs_p) +{ + if (*idx_p >= rxrefs->dst_commits.nr) { + *dst_commit_p = NULL; + *src_objs_p = NULL; +
[PATCH 5/5] notes: Implement xref-cherry-picks hooks and tests
From: Tejun Heo Add post-cherry-pick.sample and post-fetch.sample which, when enabled, will keep refs/notes/xref-cherry-picks up-to-date as new cherry-picks are created and fetched. Also, add tests to verify xref-cherry-picks. Signed-off-by: Tejun Heo --- Documentation/git-reverse-trailer-xrefs.txt | 9 ++ t/t3321-notes-xref-cherry-picks.sh | 124 templates/hooks--post-cherry-pick.sample| 8 ++ templates/hooks--post-fetch.sample | 30 + 4 files changed, 171 insertions(+) create mode 100644 t/t3321-notes-xref-cherry-picks.sh create mode 100644 templates/hooks--post-cherry-pick.sample create mode 100644 templates/hooks--post-fetch.sample diff --git a/Documentation/git-reverse-trailer-xrefs.txt b/Documentation/git-reverse-trailer-xrefs.txt index 20d260486..651ecdce1 100644 --- a/Documentation/git-reverse-trailer-xrefs.txt +++ b/Documentation/git-reverse-trailer-xrefs.txt @@ -131,6 +131,15 @@ ddd1bf2 commit A +Keeping xref-cherry-picks up-to-date + + +Reverse-maps can be kept-up incrementally with hooks. For example, to +keep xref-cherry-picks up-to-date, `git-reverse-trailer-xrefs` should +be invoked on new cherry-picks and fetched commits. See +`hooks/post-cherry-pick.sample` and `hooks/post-fetch.sample`. + + GIT --- Part of the linkgit:git[1] suite diff --git a/t/t3321-notes-xref-cherry-picks.sh b/t/t3321-notes-xref-cherry-picks.sh new file mode 100644 index 0..96b6731c9 --- /dev/null +++ b/t/t3321-notes-xref-cherry-picks.sh @@ -0,0 +1,124 @@ +#!/bin/sh + +test_description='Verify xref-cherry-picks handling + +Assume the following git repository. + + D*---E** release-B +/ + C* E* release-D + / / + A---B---C---D---E master + +which contains the following cherry-picks. + + C -> C* + D -> D* + E -> E* -> E** + +1. Build the above repository using `git-cherry-pick -x` with the + sample post-cherry-pick hook enabled. Verify that the + xref-cherry-picks notes are populated correctly. + +2. Clear the notes and rebuild them by directly running + git-reverse-xref-trailers and verify the output. + +3. Run it again and check the output still agrees to verify duplicate + handling. + +4. Build a cloned repository using per-branch fetches with the sample + post-fetch hook enabled. Verify that the xref-cherry-picks notes + are populatec correctly. +' + +TEST_NO_CREATE_REPO=1 + +. ./test-lib.sh + +GIT_AUTHOR_EMAIL=bogus_email_address +export GIT_AUTHOR_EMAIL + +test_expect_success \ +'Build repo with cherry-picks and verify xref-cherry-picks' \ +'test_create_repo main && + cd main && + mkdir -p .git/hooks && + mv .git/hooks-disabled/post-cherry-pick.sample .git/hooks/post-cherry-pick && + + test_tick && + echo A >> testfile && + git update-index --add testfile && + git commit -am "A" && + echo B >> testfile && + git commit -am "B" && + echo C >> testfile && + git commit -am "C" && + echo D >> testfile && + git commit -am "D" && + echo E >> testfile && + git commit -am "E" && + + test_tick && + git checkout -b release-D master^ && + git cherry-pick -x master && + + test_tick && + git checkout -b release-B master^^^ && + git cherry-pick -x release-D^^ && + git cherry-pick -x release-D^ && + git cherry-pick -x release-D && + + cat > expect <<-EOF && +master E +Notes (xref-cherry-picks): +Cherry-picked-to: release-D +Cherry-picked-to: release-B + +master~1 D +Notes (xref-cherry-picks): +Cherry-picked-to: release-B~1 + +master~2 C +Notes (xref-cherry-picks): +Cherry-picked-to: release-B~2 + +master~3 B +master~4 A +EOF + + git log --pretty=oneline --notes=xref-cherry-picks master | git name-rev --name-only --stdin > actual && + test_cmp expect actual +' + +test_expect_success \ +'Clear, rebuild and verify xref-cherry-picks' \ +'git reverse-trailer-xrefs --xref-cherry-picks --all --clear && + git reverse-trailer-xrefs --xref-cherry-picks --all && + git log --pretty=oneline --notes=xref-cherry-picks master | git name-rev --name-only --stdin > actual && +test_cmp expect actual +' + +test_expect_success \ +'Build it again to verify duplicate handling' \ +'git reverse-trailer-xrefs --xref-cherry-picks --all && + git log --pretty=oneline --notes=xref-cherry-picks master | git name-rev --name-only --stdin > actual && +test_cmp expect actual +' + +test_expect_
[PATCH 2/5] notes: Implement special handlings for refs/notes/xref-
From: Tejun Heo Some trailers refer to other commits. Let's call them xrefs (cross-references). For example, a cherry pick trailer points to the source commit. It is sometimes useful to build a reverse map of these xrefs - ie. source -> cherry-pick instead of cherry-pick -> source. These reverse-maps will be recorded in special notes whose refs start with refs/notes/xref-. This patch implements the following special handling for the xref notes. * When xref notes are appended to an existing one, both parts get parsed and dead or dupliate references are dropped, so that the merged note contains only valid and unique xrefs. * When xref notes are formatted for printing, the formatter recurses into each xref and prints the nested xrefs with increasing indentation to show the comprehensive xref chains. The latter part will be documented by a future patch with the actual use case. Signed-off-by: Tejun Heo --- Documentation/git-notes.txt | 8 ++ notes-merge.c | 9 ++ notes-utils.c | 2 + notes-utils.h | 3 +- notes.c | 260 +++- notes.h | 9 ++ 6 files changed, 288 insertions(+), 3 deletions(-) diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt index df2b64dbb..872919ad4 100644 --- a/Documentation/git-notes.txt +++ b/Documentation/git-notes.txt @@ -88,6 +88,10 @@ the command can read the input given to the `post-rewrite` hook.) Append to the notes of an existing object (defaults to HEAD). Creates a new notes object if needed. + If the notes ref starts with `/refs/notes/xref-`, each + line is expected to contain `: ` and + only lines with reachable unique commit IDs are kept. + edit:: Edit the notes for a given object (defaults to HEAD). @@ -273,6 +277,10 @@ Note that if either the local or remote version contain duplicate lines prior to the merge, these will also be removed by this notes merge strategy. +"cat_xrefs" is similar to "union" but expects each line to contain +`: `. Only lines with reachable unique commit IDs are +kept. + EXAMPLES diff --git a/notes-merge.c b/notes-merge.c index bd05d50b0..3fe2389a1 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -464,6 +464,15 @@ static int merge_one_change(struct notes_merge_options *o, die("failed to concatenate notes " "(combine_notes_cat_sort_uniq)"); return 0; + case NOTES_MERGE_RESOLVE_CAT_XREFS: + if (o->verbosity >= 2) + printf("Concatenating unique and valid cross-references " + "in local and remote notes for %s\n", + oid_to_hex(&p->obj)); + if (add_note(t, &p->obj, &p->remote, combine_notes_cat_xrefs)) + die("failed to concatenate notes " + "(combine_notes_cat_xrefs)"); + return 0; } die("Unknown strategy (%i).", o->strategy); } diff --git a/notes-utils.c b/notes-utils.c index 14ea03178..db6363c39 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -70,6 +70,8 @@ int parse_notes_merge_strategy(const char *v, enum notes_merge_strategy *s) *s = NOTES_MERGE_RESOLVE_UNION; else if (!strcmp(v, "cat_sort_uniq")) *s = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ; + else if (!strcmp(v, "cat_xrefs")) + *s = NOTES_MERGE_RESOLVE_CAT_XREFS; else return -1; diff --git a/notes-utils.h b/notes-utils.h index 540830652..b604f9b51 100644 --- a/notes-utils.h +++ b/notes-utils.h @@ -28,7 +28,8 @@ enum notes_merge_strategy { NOTES_MERGE_RESOLVE_OURS, NOTES_MERGE_RESOLVE_THEIRS, NOTES_MERGE_RESOLVE_UNION, - NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ + NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ, + NOTES_MERGE_RESOLVE_CAT_XREFS, }; struct notes_rewrite_cfg { diff --git a/notes.c b/notes.c index 25cdce28b..835466787 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 @@ -996,8 +997,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; + el
[PATCH 4/5] githooks: Add post-cherry-pick and post-fetch hooks
From: Tejun Heo * 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 --- 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 `` and the second +``, where `..` 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: + + SP SP SP LF + +where `` got updated from `` to `` as a +result of fetching ``. If a branch or tag was created, +`` will be 40 `0`. If a tag was pruned, `` will +be `(delete)` and 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 ?
Re: [PATCHSET] git-reverse-trailer-xrefs: Reverse map cherry-picks and other cross-references
Hello, Junio. On Wed, Dec 12, 2018 at 04:26:57PM +0900, Junio C Hamano wrote: > It also is not immediately obvious to me what your general strategy > to maintain this reverse mapping is, when new ways and codepaths to > cause new commits with "cherry-picked-from" trailer appear. Do we > keep piling on new hooks? Or is the reverse mapping information So, what I actually wanted was "this repo now has these new commits" hook, but that didn't seem to be in line with other hooks as most were bound to specific directly user-visible commands and actions. > allowed to be a bit stale and be completed immediately before it > gets used? A totally different approach could be to record list of > commits, all commits behind which have been scanned for reverse > mapping, in the tip of the notes history, perhaps in the commit log > message (which is machine generated anyway). Then, before you need > the up-to-date-to-the-last-second reverse mapping, you could run > > git rev-list --all --not $these_tips_recorded Wouldn't it be more useful to have repo-updated-with-these-commits hook instead rather than putting more logic on note handling? > and scan the commits, just like you scan what you fetched. And when > you update the reverse mapping notes tree, the commit to record that > notes update can record the tip of the above traverseal. > > I also wonder how this topic and the topic Stefan Xenos has been > working on (cf. <20181201005240.113210-1-sxe...@google.com>) can > benefit from each other by cross pollination. Stefan's topic also > needs to answer, given a commit, what other commits were created out > of it by cherry-picking and then further amending the results, so > that when the original commit itself gets amended, the cherry-picked > ones that were created from the original can be easily identified > and get updated in the same way as necessary. Ah, I see, forward-propagating amends to cherry-picks. > The motivating workflow your topic's cover letter gives us is to > maintain the release branch that goes only forward, so in that > context, how a commit on the release branch that was created by > cherry-picking an original gets updated when the original commit > gets amended would be different (I am assuming that you cannot At least we don't do this with our trees and most trees that I've worked with don't either as that would make it really confusing for people to work together. > affored to rebase the release branch to update a deeply buried > commit that was an earlier cherry-pick), but I would imagine that > both topics share the need for a good data structure to keep track > of (1) the relationship between the original and copy of the > cherry-pick and (2) the fact that the original of such a cherry-pick > is now stale and the copy might want to get updated. As long as we can keep the reverse rference notes consistent, wouldn't amend propagation just consume them? Thanks. -- tejun
Re: [PATCHSET] git-reverse-trailer-xrefs: Reverse map cherry-picks and other cross-references
Hello, Junio. On Thu, Dec 13, 2018 at 12:01:25PM +0900, Junio C Hamano wrote: > > Wouldn't it be more useful to have repo-updated-with-these-commits > > hook instead rather than putting more logic on note handling? > > > >> and scan the commits, just like you scan what you fetched. And when > >> you update the reverse mapping notes tree, the commit to record that > >> notes update can record the tip of the above traversal. > > I do not consider what you do in notes/xref-* "more logic on note > handling" in the sense that the logic is part of "notes" API. > > The moment you decided to reserve one hierarchy in refs/notes/ and > designed what the mapping recorded there means, you designed a new > trailer-xrefs API. It is a part of that API how blobs stored in > your refs/notes/xref-cherry-picks are formatted and what they mean. > It's the same thing---it is also part of your API how the log > message for recording commits in that hierarchy is formatted and > what it means. Hmmm... I see. I still have a bit of trouble seeing why doing it that way is better tho. Wouldn't new-object-hook be simpler? They'll achieve about the same thing but one would need to keep the states in two places. > > As long as we can keep the reverse rference notes consistent, wouldn't > > amend propagation just consume them? > > Yes. Would that mean you do not need the notes/xref-* series we are > seeing here, and instead (re)use what Stefan's series, which already > needs to have access to and record the information anyway, records? Oh yeah, for sure. Didn't know Stefan was doing something similar. Will continue on the other reply. Thanks. -- tejun
Re: [PATCHSET] git-reverse-trailer-xrefs: Reverse map cherry-picks and other cross-references
Hello, Junio, Stefan. On Thu, Dec 13, 2018 at 12:09:39PM +0900, Junio C Hamano wrote: > Please do not take the above as "don't do notes/xref-; instead read > from the 'meta commits'". I do not have a preference between the > two proposed implementations. The important thing is that we won't > end up with having to maintain two separate mechanisms that want to > keep track of essentially the same class of information. FWIW I'd > be perfectly fine if the unification goes the other way, as long as > goals of both parties are met, and for that, I'd like to see you two > work together, or at least be aware of what each other is doing and > see if cross-pollination would result in a mutually better solution. So, from my POV, the only thing I want is being able to easily tell which commit got cherry picked where. I really don't care how that's achieved. Stefan, if there's anything I can do to push it forward, let me know and if you see anything useful in my patchset, please feel free to incorporate them in any way. Thanks. -- tejun
Re: [PATCHSET] git-reverse-trailer-xrefs: Reverse map cherry-picks and other cross-references
Hello, Junio. On Thu, Dec 13, 2018 at 02:47:36PM +0900, Junio C Hamano wrote: > Tejun Heo writes: > > > Hmmm... I see. I still have a bit of trouble seeing why doing it that > > way is better tho. Wouldn't new-object-hook be simpler? They'll > > achieve about the same thing but one would need to keep the states in > > two places. > > The new-object-hook thing will not have keep the states. Whenever > Git realizes that it created a new object, it must call that hook, > and at that point in time, without keeping any state, it knows which > objects are what it has just created. So "in two places" is not a Yeap, that's what I was trying to say. > problem at all. There is only one place (i.e. the place the sweeper > would record what it just did to communicate with its future > invocation). > > A new-object-hook that will always be called any time a new object > enters the picture would be a nightmare to maintain up-to-date. One But I didn't know this. I probably am too naive in thinking so but it's a bit surprising that there's no single / few chokepoints for repo updates that we can attach to. > missed codepath and your coverage will have holes. Having a back-up > mechanism to sweep for new objects will give you a better chance of > staying correct as the system evolves, I'd think. The duplicate state tracking still bothers me a bit (duplicate in the sense that the end results are the same) especially as this would mean any similar mechanism would need to track their own states too, but I really don't have the full (not even close) picture here and it can easily be me missing something. Anyways, I'll wait for Stefan to chime in. Thanks. -- tejun
Re: [PATCHSET] git-reverse-trailer-xrefs: Reverse map cherry-picks and other cross-references
Hey, guys. On Tue, Dec 18, 2018 at 08:48:35AM -0800, Stefan Xenos wrote: > I've just uploaded a new evolve proposal that includes a spec for the > "hiddenmetas" namespace, where we can store historical cherry-pick > information. Total noob question - where can I read that? Also, as long as I can have the back references and follow to the existing cherry-picks, I don't have any further requirements, so any working mechanism is great by me. Thanks. -- tejun
Re: [RFC] cherry-pick notes to find out cherry-picks from the origin
Ping, thanks. -- tejun
Re: [RFC] cherry-pick notes to find out cherry-picks from the origin
On Wed, Oct 24, 2018 at 05:24:01AM -0700, Tejun Heo wrote: > Ping, thanks. Ping again. Any comments? Wasn't this the direction you guys were suggesting? Thanks. -- tejun
Re: [RFC] cherry-pick notes to find out cherry-picks from the origin
Hello, Jeff. On Thu, Nov 15, 2018 at 09:40:44AM -0500, Jeff King wrote: > 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. > > * 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. > 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 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. > tools, and fill in their gaps in a way that lets people easily do > similar things. I'll respond to this together below. > And on that 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. > > 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. > > Combined with name-rev --stdin, it can produce outputs like the following. > > [...] > > Yeah, that looks pretty good. > > > 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. > > 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. > 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
Re: [RFC] cherry-pick notes to find out cherry-picks from the origin
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 0..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 [] [...]"), + 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(¬e, "%s: %s\n", tag, hex); + else + strbuf_addf(¬e, "%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, ¬e_oid); + strbuf_release(¬e); + if (ret) + return ret; + +
[RFC] 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/20180726153714.gx1934...@devbig577.frc2.facebook.com/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 82cddd79f962de0bb1e7cdd95d48b4865816 (branch2) Author: Tejun Heo 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 Date: Wed Jul 25 21:31:35 2018 -0700 commit 4 (cherry picked from commit 10f7ce0a0e524279f022b48460c088a108b45d54 (master~1)) Notes (cherry-picks): Cherry-picked-to: 82cddd79f962de0bb1e7cdd95d48b4865816 (branch2) commit 10f7ce0a0e524279f022b48460c088a108b45d54 (master~1) Author: Tejun Heo Date: Wed Jul 25 21:31:35 2018 -0700 commit 4 Notes (cherry-picks): Cherry-picked-to: d433e3b4d5a19b3d29e2c8349fe88ceade5f6190 (branch1) Cherry-picked-to: 82cddd79f962de0bb1e7cdd95d48b4865816 (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(-) diff --git a/Makefile b/Makefile index 5c8307b7c..fb0ff3ce9 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/builtin.h b/builtin.h index 962f0489a..d9d019abc 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_note_cherry_picks
[PATCH] name_rev: add support for --cherry-picks
>From aefa07bc66bb4a116eb84eb46d7f070f9632c990 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 26 Jul 2018 04:14:52 -0700 It's often useful to track cherry-picks of a given commit. Add --cherry-picks support to git-name-rev. When specified, name_rev also shows the commits cherry-picked from the listed target commits with indentations. $ git name-rev --cherry-picks 10f7ce0a0e524279f022 10f7ce0a0e524279f022 master~1 d433e3b4d5a19b3d29e2c8349fe88ceade5f6190 branch1 82cddd79f962de0bb1e7cdd95d48b4865816 branch2 58a8d36b2532feb0a14b4fc2a50d587e64f38324 branch3 fa8b79edc5dfff21753c2ccfc1a1828336c4c070 branch4~1 Note that branch2 is further indented because it's a nested cherry pick from d433e3b4d5a1. "git-describe --contains" is a wrapper around git-name-rev. Also add --cherry-picks support to git-describe. Signed-off-by: Tejun Heo --- Documentation/git-describe.txt | 5 ++ Documentation/git-name-rev.txt | 4 ++ builtin/describe.c | 7 +- builtin/name-rev.c | 117 +-- t/t6121-describe-cherry-picks.sh | 63 + 5 files changed, 190 insertions(+), 6 deletions(-) create mode 100755 t/t6121-describe-cherry-picks.sh diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt index e027fb8c4..13a229bd7 100644 --- a/Documentation/git-describe.txt +++ b/Documentation/git-describe.txt @@ -60,6 +60,11 @@ OPTIONS the tag that comes after the commit, and thus contains it. Automatically implies --tags. +--cherry-picks:: + Also show the commits cherry-picked from the target commits. + Cherry-picks are shown indented below their from-commmits. + Can only be used with --contains. + --abbrev=:: Instead of using the default 7 hexadecimal digits as the abbreviated object name, use digits, or as many digits diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt index 5cb0eb085..df16c4a89 100644 --- a/Documentation/git-name-rev.txt +++ b/Documentation/git-name-rev.txt @@ -61,6 +61,10 @@ OPTIONS --always:: Show uniquely abbreviated commit object as fallback. +--cherry-picks:: + Also show the commits cherry-picked from the target commits. + Cherry-picks are shown indented below their from-commmits. + EXAMPLES diff --git a/builtin/describe.c b/builtin/describe.c index 1e87f68d5..94c84004d 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -528,9 +528,10 @@ static void describe(const char *arg, int last_one) int cmd_describe(int argc, const char **argv, const char *prefix) { - int contains = 0; + int contains = 0, cherry_picks = 0; struct option options[] = { OPT_BOOL(0, "contains", &contains, N_("find the tag that comes after the commit")), + OPT_BOOL(0, "cherry-picks", &cherry_picks, N_("also include cherry-picks with --contains")), OPT_BOOL(0, "debug", &debug, N_("debug search strategy on stderr")), OPT_BOOL(0, "all",&all, N_("use any ref")), OPT_BOOL(0, "tags", &tags, N_("use any tag, even unannotated")), @@ -570,6 +571,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix) if (longformat && abbrev == 0) die(_("--long is incompatible with --abbrev=0")); + if (cherry_picks && !contains) + die(_("--cherry-picks can only be used with --contains")); if (contains) { struct string_list_item *item; @@ -579,6 +582,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix) argv_array_pushl(&args, "name-rev", "--peel-tag", "--name-only", "--no-undefined", NULL); + if (cherry_picks) + argv_array_push(&args, "--cherry-picks"); if (always) argv_array_push(&args, "--always"); if (!all) { diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 0eb440359..7b21556ad 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -7,9 +7,13 @@ #include "parse-options.h" #include "sha1-lookup.h" #include "commit-slab.h" +#include "trailer.h" +#include "object-store.h" #define CUTOFF_DATE_SLOP 86400 /* one day */ +static const char cherry_picked_prefix[] = "(cherry picked from commit "; + typedef struct rev_name { const char *tip_name; timestamp_t taggerdate; @@ -19,9 +23,12 @@ typedef struct rev_name { } rev_name; define_commit_slab(commit_rev_name, struct rev_name
[PATCH v2] name_rev: add support for --cherry-picks
>From a6a88c3da252d69547ac8b463098fc4f4c03f322 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 26 Jul 2018 04:14:52 -0700 Subject: [PATCH] name_rev: add support for --cherry-picks It's often useful to track cherry-picks of a given commit. Add --cherry-picks support to git-name-rev. When specified, name_rev also shows the commits cherry-picked from the listed target commits with indentations. $ git name-rev --cherry-picks 10f7ce0a0e524279f022 10f7ce0a0e524279f022 master~1 d433e3b4d5a19b3d29e2c8349fe88ceade5f6190 branch1 82cddd79f962de0bb1e7cdd95d48b4865816 branch2 58a8d36b2532feb0a14b4fc2a50d587e64f38324 branch3 fa8b79edc5dfff21753c2ccfc1a1828336c4c070 branch4~1 Note that branch2 is further indented because it's a nested cherry pick from d433e3b4d5a1. "git-describe --contains" is a wrapper around git-name-rev. Also add --cherry-picks support to git-describe. v2: - Remove a warning message for a malformed cherry-picked tag. There isn't much user can do about it. - Continue scanning cherry-pick tags until a working one is found instead of aborting after trying the last one. It might miss nesting but still better to show than miss the commit entirely. Signed-off-by: Tejun Heo --- Documentation/git-describe.txt | 5 ++ Documentation/git-name-rev.txt | 4 ++ builtin/describe.c | 7 +- builtin/name-rev.c | 115 +-- t/t6121-describe-cherry-picks.sh | 63 + 5 files changed, 188 insertions(+), 6 deletions(-) create mode 100755 t/t6121-describe-cherry-picks.sh diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt index e027fb8c4..13a229bd7 100644 --- a/Documentation/git-describe.txt +++ b/Documentation/git-describe.txt @@ -60,6 +60,11 @@ OPTIONS the tag that comes after the commit, and thus contains it. Automatically implies --tags. +--cherry-picks:: + Also show the commits cherry-picked from the target commits. + Cherry-picks are shown indented below their from-commmits. + Can only be used with --contains. + --abbrev=:: Instead of using the default 7 hexadecimal digits as the abbreviated object name, use digits, or as many digits diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt index 5cb0eb085..df16c4a89 100644 --- a/Documentation/git-name-rev.txt +++ b/Documentation/git-name-rev.txt @@ -61,6 +61,10 @@ OPTIONS --always:: Show uniquely abbreviated commit object as fallback. +--cherry-picks:: + Also show the commits cherry-picked from the target commits. + Cherry-picks are shown indented below their from-commmits. + EXAMPLES diff --git a/builtin/describe.c b/builtin/describe.c index 1e87f68d5..94c84004d 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -528,9 +528,10 @@ static void describe(const char *arg, int last_one) int cmd_describe(int argc, const char **argv, const char *prefix) { - int contains = 0; + int contains = 0, cherry_picks = 0; struct option options[] = { OPT_BOOL(0, "contains", &contains, N_("find the tag that comes after the commit")), + OPT_BOOL(0, "cherry-picks", &cherry_picks, N_("also include cherry-picks with --contains")), OPT_BOOL(0, "debug", &debug, N_("debug search strategy on stderr")), OPT_BOOL(0, "all",&all, N_("use any ref")), OPT_BOOL(0, "tags", &tags, N_("use any tag, even unannotated")), @@ -570,6 +571,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix) if (longformat && abbrev == 0) die(_("--long is incompatible with --abbrev=0")); + if (cherry_picks && !contains) + die(_("--cherry-picks can only be used with --contains")); if (contains) { struct string_list_item *item; @@ -579,6 +582,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix) argv_array_pushl(&args, "name-rev", "--peel-tag", "--name-only", "--no-undefined", NULL); + if (cherry_picks) + argv_array_push(&args, "--cherry-picks"); if (always) argv_array_push(&args, "--always"); if (!all) { diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 0eb440359..adffae0fe 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -7,9 +7,13 @@ #include "parse-options.h" #include "sha1-lookup.h" #include "commit-slab.h" +#include "trail
Re: [PATCH v2] name_rev: add support for --cherry-picks
Hello, Junio. On Thu, Jul 26, 2018 at 08:12:45AM -0700, Junio C Hamano wrote: > Tejun Heo writes: > > > From a6a88c3da252d69547ac8b463098fc4f4c03f322 Mon Sep 17 00:00:00 2001 > > From: Tejun Heo > > Date: Thu, 26 Jul 2018 04:14:52 -0700 > > Subject: [PATCH] name_rev: add support for --cherry-picks > > The above belongs to the mail header, not the body. Ah, right, sorry about that. > > $ git name-rev --cherry-picks 10f7ce0a0e524279f022 > > 10f7ce0a0e524279f022 master~1 > > d433e3b4d5a19b3d29e2c8349fe88ceade5f6190 branch1 > > 82cddd79f962de0bb1e7cdd95d48b4865816 branch2 > > 58a8d36b2532feb0a14b4fc2a50d587e64f38324 branch3 > > fa8b79edc5dfff21753c2ccfc1a1828336c4c070 branch4~1 > > "git name-rev X" asks "I want to know about X". And the first line > of the above tells us that 10f7ce is the first parent of the master > branch. What does the second line tell us? 10f7ce was created by > cherry picking d433e3b4 which sits at the tip of branch1? > > It appears that you are showing the reverse (d433e3, 58a8d3 and > fa8b79 sit next to each other, but it cannot be that 10f7ce was > created by cherry-picking these three). I do not mean to say that So, it means that d433e, 58a8d and fa8b7 are created by cherry picking 10f7c and 72cdd is created by cherry picking d433e. > the reverse information is not useful thing to learn about the > commit (i.e. "X got cherry-picked to all these places") but I am > having a hard time convincing myself that the feature sits well in > "describe" and "name-rev". I should have explained the use case better. Let's say I'm forking off and maintaining prod releases. We branch it off of a major upstream version and keeps developing / backporting on that branch reguarly cutting releases. A lot of commits get cherry-picked back from master tracking upstream but some are also cherry picked to sub-release branches for quick-fix releases. e.g. v4.16 oooAoo..o master \.ooA'oo.o v4.16-prod \ \.oo v4.16-prod-r2 \ .oA'' v4.16-prod-r1 Given a commit, it's useful to find out through which version that got released, which is where "git-describe --contains" helps. However, when commits are backported through cherry-picks to prod branches as in above, that commit is released through multiple versions and it's a bit painful to hunt them down. This is what --cherry-picks helps with, so if now I do "git describe --contains --cherry-picks A", it'll tell me that... Upstream, it's v4.17-rc1 Backported and released through v4.16-prod-r2 Also backported to v4.16-prod-r1 (cuz the fix happened while r1 was going through rc's) So, it extends the description of how the original commit is released (or given name) so that releases through cherry-picks are also included. Thanks. -- tejun
Re: [PATCH v2] name_rev: add support for --cherry-picks
Hello, Jeff. On Fri, Jul 27, 2018 at 4:47 AM Jeff King wrote: > - the set of names might be distinct from the set of commits you'd want >to traverse. For instance, you might want to use "name-rev --tags", >but find cherry-picks even on untagged branches (e.g., "--all"). Hmm... maybe but when I'm using --tags, I'm usually asking "when did this get released?" and --cherry-picks seems to logically extend that and fits such scenarios pretty well. ie. "Give me the release name and all the aliases for this commit". We can play with the options to support combinations of tagged/untagged but I'm not quite sure about the usefulness. After all, calling the program twice isn't all that difficult. > - instead of naming commits, you might want to see the information as >you are viewing git-log output ("by the way, this was cherry-picked >elsewhere, too") name-rev --stdin sort of does this for commit names. I thought about adding the support for --cherry-picks too but wasn't sure how to weave in the result, but if we can figure that out this should work, right? > So I kind of wonder if it would be more useful to have a command which > incrementally updates a git-notes tree to hold the mapping, and then > learn to consult it in various places. "git log --notes=reverse-cherry" > would show it, though it might be worth teaching the notes-display code > that certain types of notes contain object ids (so it would be > interesting to recursively show their notes, or format them nicely, > etc). > > I dunno. That is perhaps over-engineering. But it feels like > "reverse-map the cherry-picks" is orthogonal to the idea of name-rev. Hmm... to me, it makes logical sense because "name this commit" and "also include aliases" seem to be a natural combination both in semantics and use cases, but if there are better ways of tracking down cherry-picks, I'm all ears. Thanks. -- tejun
[ANNOUNCE] mtkdiff-20050801 (with patchkdiff, quiltkdiff, gitkdiff and modified gitk)
Hello, guys. New version of mtkdiff package is available. Changes since last release (20050514) are. * patchkdiff added. Idea is from patchview of Randy Dunlap (Hi!). patchkdiff can show multiple diff files. * quiltkdiff rewritten in perl. It's faster and doesn't push/pop quilt repository. Patches are rolled back and applied in a temporary working directory. * gitkdiff rewritten in perl. It now works with the new git diff output format and a bit faster. Also, this version can show multiple commits. For example, you can do the following. $ git-rev-list HEAD ^OLD_HEAD | gitkdiff -c -r * modified gitk-1.2. For more information... http://home-tj.org/mtkdiff Tarball is available at http://home-tj.org/mtkdiff/files/mtkdiff-20050801.tar.gz Thanks. -- tejun - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html