{"thread":{"id":"44143","subject":"[RFC PATCH] revision: new rev%n shorthand for rev^n..rev","startedAt":"2016-09-23T10:56:10Z","lastAt":"2016-09-23T16:43:53Z","messageCount":2,"participants":["Vegard Nossum","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"302439","messageId":"20160923105254.10235-1-vegard.nossum@oracle.com","threadId":"44143","inReplyTo":null,"subject":"[RFC PATCH] revision: new rev%n shorthand for rev^n..rev","fromName":"Vegard Nossum","fromEmail":"vegard.nossum@oracle.com","sentAt":"2016-09-23T10:52:54Z","receivedAt":"2016-09-23T10:56:10Z","isPatch":true,"sender":{"key":"vegard.nossum@oracle.com","avatar":"https://avatars.githubusercontent.com/u/24173?v=4"},"body":"I use rev^..rev daily, and I'm surely not the only one. To save typing\n(or copy-pasting, if the rev is long -- like a full SHA-1 or branch name)\nwe can make rev% a shorthand for that.\n\nThe existing syntax rev^! seems like it should do the same, but it\ndoesn't really do the right thing for merge commits (it gives only the\nmerge itself).\n\nAs a natural generalisation, we also accept rev%n where n excludes the\nnth parent of rev. It _may_ be more useful to define rev%n for an m-way\nmerge as:\n\n rev\n ^rev^1\n ^rev^[... except n]\n ^rev^m\n\nso that you can see only the commits that arrived via the nth parent,\nbut this might be questionable/unintuitive in case any of the parents\nthat share commits (as you would get fewer commits than expected).\n\nSigned-off-by: Vegard Nossum <vegard.nossum@oracle.com>\n---\n Documentation/revisions.txt | 14 +++++++++++++\n builtin/rev-parse.c         | 38 ++++++++++++++++++++++++++++++++++\n revision.c                  | 50 +++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 102 insertions(+)\n\ndiff --git Documentation/revisions.txt Documentation/revisions.txt\nindex 4bed5b1..ab2dc2c 100644\n--- Documentation/revisions.txt\n+++ Documentation/revisions.txt\n@@ -281,6 +281,14 @@ is a shorthand for 'HEAD..origin' and asks \"What did the origin do since\n I forked from them?\"  Note that '..' would mean 'HEAD..HEAD' which is an\n empty range that is both reachable and unreachable from HEAD.\n \n+Parent Exclusion Notation\n+~~~~~~~~~~~~~~~~~~~~~~~~~\n+The '<rev>%{<n>}', Parent Exclusion Notation::\n+Shorthand for '<rev>{caret}<n>..<rev>', with '<n>' = 1 if not\n+given. This is typically useful for merge commits where you\n+can just pass '<commit>%' to get all the commits in the branch\n+that was merged in merge commit '<commit>'.\n+\n Other <rev>{caret} Parent Shorthand Notations\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n Two other shorthands exist, particularly useful for merge commits,\n@@ -316,6 +324,10 @@ Revision Range Summary\n \t<rev2> but exclude those that are reachable from both.  When\n \teither <rev1> or <rev2> is omitted, it defaults to `HEAD`.\n \n+'<rev>%{<n>}', e.g. 'HEAD%, HEAD%2'::\n+\tEquivalent to '<rev>{caret}<n>..<rev>', with '<n>' = 1 if not\n+\tgiven.\n+\n '<rev>{caret}@', e.g. 'HEAD{caret}@'::\n   A suffix '{caret}' followed by an at sign is the same as listing\n   all parents of '<rev>' (meaning, include anything reachable from\n@@ -339,6 +351,8 @@ spelt out:\n    C                            I J F C\n    B..C   = ^B C                C\n    B...C  = B ^F C              G H D E B C\n+   B%     = B^..B\n+\t  = B ^B^1              E I J F B\n    C^@    = C^1\n \t  = F                   I J F\n    B^@    = B^1 B^2 B^3\ndiff --git builtin/rev-parse.c builtin/rev-parse.c\nindex 76cf05e..f081b81 100644\n--- builtin/rev-parse.c\n+++ builtin/rev-parse.c\n@@ -292,6 +292,42 @@ static int try_difference(const char *arg)\n \treturn 0;\n }\n \n+static int try_branch(const char *arg)\n+{\n+\tchar *percent;\n+\tunsigned char sha1[20];\n+\tunsigned char end[20];\n+\n+\t/*\n+\t * <rev>%{<n>} is shorthand for <rev>^<n>..<rev>, with <n> = 1 if\n+\t * not given. This is typically used for merge commits where you\n+\t * can just pass <merge>% and it will show you all the commits in\n+\t * the branch that was merged (for octopus merges, <n> is the nth\n+\t * branch).\n+\t */\n+\n+\tif (!(percent = strstr(arg, \"%\")))\n+\t\treturn 0;\n+\n+\t*percent = '^';\n+\tif (!get_sha1_committish(arg, sha1)) {\n+\t\t*percent = '%';\n+\t\treturn 0;\n+\t}\n+\n+\t*percent = '\\0';\n+\tif (!get_sha1_committish(arg, end)) {\n+\t\t*percent = '%';\n+\t\treturn 0;\n+\t}\n+\n+\tshow_rev(NORMAL, end, arg);\n+\t*percent = '^';\n+\tshow_rev(REVERSED, sha1, arg);\n+\t*percent = '%';\n+\treturn 1;\n+}\n+\n static int try_parent_shorthands(const char *arg)\n {\n \tchar *dotdot;\n@@ -839,6 +875,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)\n \t\t/* Not a flag argument */\n \t\tif (try_difference(arg))\n \t\t\tcontinue;\n+\t\tif (try_branch(arg))\n+\t\t\tcontinue;\n \t\tif (try_parent_shorthands(arg))\n \t\t\tcontinue;\n \t\tname = arg;\ndiff --git revision.c revision.c\nindex 969b3d1..e20b618 100644\n--- revision.c\n+++ revision.c\n@@ -1519,6 +1519,56 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi\n \t\t}\n \t\t*dotdot = '.';\n \t}\n+\n+\t/*\n+\t * <rev>%{<n>} is shorthand for <rev>^<n>..<rev>, with <n> = 1 if\n+\t * not given. This is typically used for merge commits where you\n+\t * can just pass <merge>% and it will show you all the commits in\n+\t * the branch that was merged (for octopus merges, <n> is the nth\n+\t * branch).\n+\t */\n+\tdotdot = strstr(arg, \"%\");\n+\tif (dotdot) {\n+\t\tunsigned char sha1[20];\n+\t\tunsigned char end[20];\n+\t\tstruct object *a_obj, *b_obj;\n+\t\tunsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);\n+\t\tunsigned int a_flags;\n+\n+\t\t*dotdot = '\\0';\n+\t\tif (get_sha1_committish(arg, end)) {\n+\t\t\tif (revs->ignore_missing)\n+\t\t\t\treturn 0;\n+\t\t\tdie(\"Unknown revision %s\", arg);\n+\t\t}\n+\n+\t\t*dotdot = '^';\n+\t\tif (get_sha1_committish(arg, sha1)) {\n+\t\t\tif (revs->ignore_missing)\n+\t\t\t\treturn 0;\n+\t\t\tdie(\"Unknown revision %s\", arg);\n+\t\t}\n+\n+\t\ta_obj = parse_object(sha1);\n+\t\tb_obj = parse_object(end);\n+\t\tif (!a_obj || !b_obj) {\n+\t\t\tif (revs->ignore_missing)\n+\t\t\t\treturn 0;\n+\t\t\tdie(\"Invalid revision range %s\", arg);\n+\t\t}\n+\n+\t\ta_flags = flags_exclude;\n+\t\ta_obj->flags |= a_flags;\n+\t\tb_obj->flags |= flags;\n+\t\t*dotdot = '^';\n+\t\tadd_rev_cmdline(revs, a_obj, arg, REV_CMD_LEFT, a_flags);\n+\t\tadd_pending_object(revs, a_obj, arg);\n+\t\t*dotdot = '\\0';\n+\t\tadd_rev_cmdline(revs, b_obj, arg, REV_CMD_RIGHT, flags);\n+\t\tadd_pending_object(revs, b_obj, arg);\n+\t\t*dotdot = '%';\n+\t\treturn 0;\n+\t}\n \tdotdot = strstr(arg, \"^@\");\n \tif (dotdot && !dotdot[2]) {\n \t\t*dotdot = 0;\n-- \n2.10.0.rc0.1.g07c9292\n\n"},{"id":"302450","messageId":"xmqqr38a1s8u.fsf@gitster.mtv.corp.google.com","threadId":"44143","inReplyTo":"20160923105254.10235-1-vegard.nossum@oracle.com","subject":"Re: [RFC PATCH] revision: new rev%n shorthand for rev^n..rev","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-09-23T16:43:45Z","receivedAt":"2016-09-23T16:43:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Vegard Nossum <vegard.nossum@oracle.com> writes:\n\n> I use rev^..rev daily, and I'm surely not the only one. To save typing\n> (or copy-pasting, if the rev is long -- like a full SHA-1 or branch name)\n> we can make rev% a shorthand for that.\n\nNo, we cannot.\n\n'%' is not reserved as a special character that is forbidden in\nreference names, and for somebody who has a branch whose name is\n'master%', such a change will suddenly make 'master%' mean something\ncompletely different, breaking existing users' repositories.\n\nThis is why existing rev^@ and rev^! both use the \"^\" as the first\ncharacter that introduces the \"magic\" semantics; \"^\" cannot be a\npart of a refname.  Also sequences that begin with \"^{\" and \"@{\" are\nreserved as escape hatches to allow us extend the revision syntax in\nthe future (\"^{\" works on history, while \"@{\" bases its working on\nthe reflog data).\n\nAs \"rev^$n\" is \"nth parent\", it may be a possibility to use \"rev^-$n\"\nas a short-hand for \"^rev^$n rev\".\n"}]}