[RFC PATCH] revision: new rev%n shorthand for rev^n..rev

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I use rev^..rev daily, and I'm surely not the only one. To save typing
(or copy-pasting, if the rev is long -- like a full SHA-1 or branch name)
we can make rev% a shorthand for that.

The existing syntax rev^! seems like it should do the same, but it
doesn't really do the right thing for merge commits (it gives only the
merge itself).

As a natural generalisation, we also accept rev%n where n excludes the
nth parent of rev. It _may_ be more useful to define rev%n for an m-way
merge as:

 rev
 ^rev^1
 ^rev^[... except n]
 ^rev^m

so that you can see only the commits that arrived via the nth parent,
but this might be questionable/unintuitive in case any of the parents
that share commits (as you would get fewer commits than expected).

Signed-off-by: Vegard Nossum <vegard.nossum@xxxxxxxxxx>
---
 Documentation/revisions.txt | 14 +++++++++++++
 builtin/rev-parse.c         | 38 ++++++++++++++++++++++++++++++++++
 revision.c                  | 50 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 102 insertions(+)

diff --git Documentation/revisions.txt Documentation/revisions.txt
index 4bed5b1..ab2dc2c 100644
--- Documentation/revisions.txt
+++ Documentation/revisions.txt
@@ -281,6 +281,14 @@ is a shorthand for 'HEAD..origin' and asks "What did the origin do since
 I forked from them?"  Note that '..' would mean 'HEAD..HEAD' which is an
 empty range that is both reachable and unreachable from HEAD.
 
+Parent Exclusion Notation
+~~~~~~~~~~~~~~~~~~~~~~~~~
+The '<rev>%{<n>}', Parent Exclusion Notation::
+Shorthand for '<rev>{caret}<n>..<rev>', with '<n>' = 1 if not
+given. This is typically useful for merge commits where you
+can just pass '<commit>%' to get all the commits in the branch
+that was merged in merge commit '<commit>'.
+
 Other <rev>{caret} Parent Shorthand Notations
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Two other shorthands exist, particularly useful for merge commits,
@@ -316,6 +324,10 @@ Revision Range Summary
 	<rev2> but exclude those that are reachable from both.  When
 	either <rev1> or <rev2> is omitted, it defaults to `HEAD`.
 
+'<rev>%{<n>}', e.g. 'HEAD%, HEAD%2'::
+	Equivalent to '<rev>{caret}<n>..<rev>', with '<n>' = 1 if not
+	given.
+
 '<rev>{caret}@', e.g. 'HEAD{caret}@'::
   A suffix '{caret}' followed by an at sign is the same as listing
   all parents of '<rev>' (meaning, include anything reachable from
@@ -339,6 +351,8 @@ spelt out:
    C                            I J F C
    B..C   = ^B C                C
    B...C  = B ^F C              G H D E B C
+   B%     = B^..B
+	  = B ^B^1              E I J F B
    C^@    = C^1
 	  = F                   I J F
    B^@    = B^1 B^2 B^3
diff --git builtin/rev-parse.c builtin/rev-parse.c
index 76cf05e..f081b81 100644
--- builtin/rev-parse.c
+++ builtin/rev-parse.c
@@ -292,6 +292,42 @@ static int try_difference(const char *arg)
 	return 0;
 }
 
+static int try_branch(const char *arg)
+{
+	char *percent;
+	unsigned char sha1[20];
+	unsigned char end[20];
+
+	/*
+	 * <rev>%{<n>} is shorthand for <rev>^<n>..<rev>, with <n> = 1 if
+	 * not given. This is typically used for merge commits where you
+	 * can just pass <merge>% and it will show you all the commits in
+	 * the branch that was merged (for octopus merges, <n> is the nth
+	 * branch).
+	 */
+
+	if (!(percent = strstr(arg, "%")))
+		return 0;
+
+	*percent = '^';
+	if (!get_sha1_committish(arg, sha1)) {
+		*percent = '%';
+		return 0;
+	}
+
+	*percent = '\0';
+	if (!get_sha1_committish(arg, end)) {
+		*percent = '%';
+		return 0;
+	}
+
+	show_rev(NORMAL, end, arg);
+	*percent = '^';
+	show_rev(REVERSED, sha1, arg);
+	*percent = '%';
+	return 1;
+}
+
 static int try_parent_shorthands(const char *arg)
 {
 	char *dotdot;
@@ -839,6 +875,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 		/* Not a flag argument */
 		if (try_difference(arg))
 			continue;
+		if (try_branch(arg))
+			continue;
 		if (try_parent_shorthands(arg))
 			continue;
 		name = arg;
diff --git revision.c revision.c
index 969b3d1..e20b618 100644
--- revision.c
+++ revision.c
@@ -1519,6 +1519,56 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
 		}
 		*dotdot = '.';
 	}
+
+	/*
+	 * <rev>%{<n>} is shorthand for <rev>^<n>..<rev>, with <n> = 1 if
+	 * not given. This is typically used for merge commits where you
+	 * can just pass <merge>% and it will show you all the commits in
+	 * the branch that was merged (for octopus merges, <n> is the nth
+	 * branch).
+	 */
+	dotdot = strstr(arg, "%");
+	if (dotdot) {
+		unsigned char sha1[20];
+		unsigned char end[20];
+		struct object *a_obj, *b_obj;
+		unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
+		unsigned int a_flags;
+
+		*dotdot = '\0';
+		if (get_sha1_committish(arg, end)) {
+			if (revs->ignore_missing)
+				return 0;
+			die("Unknown revision %s", arg);
+		}
+
+		*dotdot = '^';
+		if (get_sha1_committish(arg, sha1)) {
+			if (revs->ignore_missing)
+				return 0;
+			die("Unknown revision %s", arg);
+		}
+
+		a_obj = parse_object(sha1);
+		b_obj = parse_object(end);
+		if (!a_obj || !b_obj) {
+			if (revs->ignore_missing)
+				return 0;
+			die("Invalid revision range %s", arg);
+		}
+
+		a_flags = flags_exclude;
+		a_obj->flags |= a_flags;
+		b_obj->flags |= flags;
+		*dotdot = '^';
+		add_rev_cmdline(revs, a_obj, arg, REV_CMD_LEFT, a_flags);
+		add_pending_object(revs, a_obj, arg);
+		*dotdot = '\0';
+		add_rev_cmdline(revs, b_obj, arg, REV_CMD_RIGHT, flags);
+		add_pending_object(revs, b_obj, arg);
+		*dotdot = '%';
+		return 0;
+	}
 	dotdot = strstr(arg, "^@");
 	if (dotdot && !dotdot[2]) {
 		*dotdot = 0;
-- 
2.10.0.rc0.1.g07c9292




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]