From: Tejun Heo <tj@kernel.org>
To: git@vger.kernel.org
Cc: kernel-team@fb.com
Subject: [PATCH v2] name_rev: add support for --cherry-picks
Date: Thu, 26 Jul 2018 07:39:30 -0700 [thread overview]
Message-ID: <20180726143930.GW1934745@devbig577.frc2.facebook.com> (raw)
In-Reply-To: <20180726121346.GT1934745@devbig577.frc2.facebook.com>
From a6a88c3da252d69547ac8b463098fc4f4c03f322 Mon Sep 17 00:00:00 2001
From: Tejun Heo <tj@kernel.org>
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
82cddd79f962de0bb1e7cdd95d48b48633335816 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 <tj@kernel.org>
---
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=<n>::
Instead of using the default 7 hexadecimal digits as the
abbreviated object name, use <n> 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 "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 *);
+define_commit_slab(commit_cherry_picks, struct object_array *);
static timestamp_t cutoff = TIME_MAX;
static struct commit_rev_name rev_names;
+static struct commit_cherry_picks cherry_picks;
+static int do_cherry_picks = 0;
/* How many generations are maximally preferred over _one_ merge traversal? */
#define MERGE_TRAVERSAL_WEIGHT 65535
@@ -38,6 +45,26 @@ static void set_commit_rev_name(struct commit *commit, struct rev_name *name)
*commit_rev_name_at(&rev_names, commit) = name;
}
+static struct object_array *get_commit_cherry_picks(struct commit *commit)
+{
+ struct object_array **slot =
+ commit_cherry_picks_peek(&cherry_picks, commit);
+
+ return slot ? *slot : NULL;
+}
+
+static struct object_array *get_create_commit_cherry_picks(struct commit *commit)
+{
+ struct object_array **slot =
+ commit_cherry_picks_at(&cherry_picks, commit);
+
+ if (!*slot) {
+ *slot = xmalloc(sizeof(struct object_array));
+ **slot = (struct object_array)OBJECT_ARRAY_INIT;
+ }
+ return *slot;
+}
+
static int is_better_name(struct rev_name *name,
const char *tip_name,
timestamp_t taggerdate,
@@ -76,6 +103,45 @@ static int is_better_name(struct rev_name *name,
return 0;
}
+static void record_cherry_pick(struct commit *commit)
+{
+ 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);
+
+ /* when nested, the last trailer describes the latest cherry-pick */
+ for (i = info.trailer_nr - 1; i >= 0; i--) {
+ const int prefix_len = sizeof(cherry_picked_prefix) - 1;
+ char *line = info.trailers[i];
+
+ if (!strncmp(line, cherry_picked_prefix, prefix_len)) {
+ struct object_id from_oid;
+ struct object *from_object;
+ struct commit *from_commit;
+ struct object_array *from_cps;
+
+ if (get_oid_hex(line + prefix_len, &from_oid))
+ continue;
+
+ from_object = parse_object(&from_oid);
+ if (!from_object || from_object->type != OBJ_COMMIT)
+ continue;
+
+ from_commit = (struct commit *)from_object;
+ from_cps = get_create_commit_cherry_picks(from_commit);
+ add_object_array(&commit->object, NULL, from_cps);
+ break;
+ }
+ }
+
+ free(buffer);
+}
+
static void name_rev(struct commit *commit,
const char *tip_name, timestamp_t taggerdate,
int generation, int distance, int from_tag,
@@ -91,6 +157,10 @@ static void name_rev(struct commit *commit,
if (commit->date < cutoff)
return;
+ /* if a cherry pick we see for the first time, remember it */
+ if (do_cherry_picks && !name)
+ record_cherry_pick(commit);
+
if (deref) {
tip_name = to_free = xstrfmt("%s^0", tip_name);
@@ -402,6 +472,32 @@ static void name_rev_line(char *p, struct name_ref_data *data)
strbuf_release(&buf);
}
+static void show_cherry_picks(struct object *obj, int always,
+ int allow_undefined, int name_only, int level)
+{
+ struct object_array *cps;
+ int i;
+
+ if (obj->type != OBJ_COMMIT)
+ return;
+
+ cps = get_commit_cherry_picks((struct commit *)obj);
+ if (!cps)
+ return;
+
+ for (i = 0; i < cps->nr; i++) {
+ struct object *cherry_pick = cps->objects[i].item;
+ int j;
+
+ for (j = 0; j < level; j++)
+ fputs(" ", stdout);
+
+ show_name(cherry_pick, NULL, always, allow_undefined, name_only);
+ show_cherry_picks(cherry_pick, always, allow_undefined,
+ name_only, level + 1);
+ }
+}
+
int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = OBJECT_ARRAY_INIT;
@@ -420,6 +516,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
OPT_BOOL(0, "always", &always,
N_("show abbreviated commit object as fallback")),
+ OPT_BOOL(0, "cherry-picks", &do_cherry_picks, N_("include cherry-picked commits")),
{
/* A Hidden OPT_BOOL */
OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL,
@@ -430,6 +527,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
};
init_commit_rev_name(&rev_names);
+ init_commit_cherry_picks(&cherry_picks);
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
if (all + transform_stdin + !!argc > 1) {
@@ -464,10 +562,9 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
continue;
}
- if (commit) {
+ if (commit)
if (cutoff > commit->date)
cutoff = commit->date;
- }
if (peel_tag) {
if (!commit) {
@@ -506,9 +603,17 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
}
} else {
int i;
- for (i = 0; i < revs.nr; i++)
- show_name(revs.objects[i].item, revs.objects[i].name,
- always, allow_undefined, data.name_only);
+ for (i = 0; i < revs.nr; i++) {
+ struct object *obj = revs.objects[i].item;
+ const char *name = revs.objects[i].name;
+
+ show_name(obj, name, always, allow_undefined,
+ data.name_only);
+
+ if (do_cherry_picks)
+ show_cherry_picks(obj, always, allow_undefined,
+ data.name_only, 1);
+ }
}
UNLEAK(revs);
diff --git a/t/t6121-describe-cherry-picks.sh b/t/t6121-describe-cherry-picks.sh
new file mode 100755
index 000000000..838e0acc0
--- /dev/null
+++ b/t/t6121-describe-cherry-picks.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+test_description='git describe should show cherry-picks correctly
+
+ C
+ o----o----x
+ |\
+ | .--o
+ |\ C1
+ | .--o
+ \ C2
+ .--o
+ C3
+
+C1 and C3 are cherry-picks from C, and C2 from C1. Verify git desribe
+handles c and its cherry-picks correctly.
+'
+. ./test-lib.sh
+
+GIT_AUTHOR_EMAIL=bogus_email_address
+export GIT_AUTHOR_EMAIL
+
+test_expect_success \
+ 'prepare repository with topic branches with cherry-picks' \
+ 'test_tick &&
+ echo First > A &&
+ git update-index --add A &&
+ git commit -m "Add A." &&
+
+ test_tick &&
+ git checkout -b T1 master &&
+ git checkout -b T2 master &&
+ git checkout -b T3 master &&
+ git checkout master &&
+
+ test_tick &&
+ echo Second > B &&
+ git update-index --add B &&
+ git commit -m "Add B." &&
+
+ test_tick &&
+ git checkout -f T1 &&
+ rm -f B &&
+ git cherry-pick -x master &&
+
+ test_tick &&
+ git checkout -f T2 &&
+ rm -f B &&
+ git cherry-pick -x T1 &&
+
+ test_tick &&
+ git checkout -f T3 &&
+ rm -f B &&
+ git cherry-pick -x master
+'
+
+test_expect_success 'Verify describing cherry-picks' '
+ git describe --contains --all --cherry-picks master >actual &&
+ echo -e "master\n T1\n T2\n T3" >expect &&
+ test_cmp expect actual
+'
+
+test_done
--
2.17.1
next prev parent reply other threads:[~2018-07-26 14:39 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-07-26 12:13 [PATCH] name_rev: add support for --cherry-picks Tejun Heo
2018-07-26 14:39 ` Tejun Heo [this message]
2018-07-26 15:12 ` [PATCH v2] " Junio C Hamano
2018-07-26 15:37 ` Tejun Heo
2018-07-26 19:01 ` Junio C Hamano
2018-07-27 8:40 ` Jeff King
2018-07-27 14:47 ` Tejun Heo
2018-07-27 15:55 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180726143930.GW1934745@devbig577.frc2.facebook.com \
--to=tj@kernel.org \
--cc=git@vger.kernel.org \
--cc=kernel-team@fb.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).