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

2016-09-23 Thread Junio C Hamano
Vegard Nossum  writes:

> 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.

No, we cannot.

'%' is not reserved as a special character that is forbidden in
reference names, and for somebody who has a branch whose name is
'master%', such a change will suddenly make 'master%' mean something
completely different, breaking existing users' repositories.

This is why existing rev^@ and rev^! both use the "^" as the first
character that introduces the "magic" semantics; "^" cannot be a
part of a refname.  Also sequences that begin with "^{" and "@{" are
reserved as escape hatches to allow us extend the revision syntax in
the future ("^{" works on history, while "@{" bases its working on
the reflog data).

As "rev^$n" is "nth parent", it may be a possibility to use "rev^-$n"
as a short-hand for "^rev^$n rev".


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

2016-09-23 Thread Vegard Nossum
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 
---
 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 '%{}', Parent Exclusion Notation::
+Shorthand for '{caret}..', with '' = 1 if not
+given. This is typically useful for merge commits where you
+can just pass '%' to get all the commits in the branch
+that was merged in merge commit ''.
+
 Other {caret} Parent Shorthand Notations
 ~
 Two other shorthands exist, particularly useful for merge commits,
@@ -316,6 +324,10 @@ Revision Range Summary
 but exclude those that are reachable from both.  When
either  or  is omitted, it defaults to `HEAD`.
 
+'%{}', e.g. 'HEAD%, HEAD%2'::
+   Equivalent to '{caret}..', with '' = 1 if not
+   given.
+
 '{caret}@', e.g. 'HEAD{caret}@'::
   A suffix '{caret}' followed by an at sign is the same as listing
   all parents of '' (meaning, include anything reachable from
@@ -339,6 +351,8 @@ spelt out:
CI J F C
B..C   = ^B CC
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];
+
+   /*
+* %{} is shorthand for ^.., with  = 1 if
+* not given. This is typically used for merge commits where you
+* can just pass % and it will show you all the commits in
+* the branch that was merged (for octopus merges,  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 = '.';
}
+
+   /*
+* %{} is shorthand for ^.., with  = 1 if
+* not given. This is typically used for merge commits where you
+* can just pass % and it will show you all the commits in
+* the branch that was merged (for octopus merges,  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 ^ (