specifying ranges: we did not mean to make ".." an empty set

Either end of revision range operator can be omitted to default to HEAD, as in "origin.." (what did I do since I forked) or "..origin" (what did they do since I forked). But the current parser interprets ".." as an empty range "HEAD..HEAD", and worse yet, because ".." does exist on the filesystem, we get this annoying output: $ cd Documentation/howto $ git log .. ;# give me recent commits that touch Documentation/ area. fatal: ambiguous argument '..': both revision and filename Use '--' to separate filenames from revisions Surely we could say "git log ../" or even "git log -- .." to disambiguate, but we shouldn't have to. Helped-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed May 2, 2011 at 13:39 UTC 003c84f6d2b9e9c4d5bbf5262cae994bac7190cb
5 files changed +56 -4
Documentation/revisions.txt
+7
@@ -213,6 +213,13 @@ of 'r1' and 'r2' and is defined as
213 It is the set of commits that are reachable from either one of
214 'r1' or 'r2' but not from both.
215
216 +In these two shorthands, you can omit one end and let it default to HEAD.
217 +For example, 'origin..' is a shorthand for 'origin..HEAD' and asks "What
218 +did I do since I forked from the origin branch?" Similarly, '..origin'
219 +is a shorthand for 'HEAD..origin' and asks "What did the origin do since
220 +I forked from them?" Note that '..' would mean 'HEAD..HEAD' which is an
221 +empty range that is both reachable and unreachable from HEAD.
222 +
223 Two other shorthands for naming a set that is formed by a commit
224 and its parent commits exist. The 'r1{caret}@' notation means all
225 parents of 'r1'. 'r1{caret}!' includes commit 'r1' but excludes
builtin/rev-parse.c
+14 -2
@@ -224,6 +224,7 @@ static int try_difference(const char *arg)
224 const char *next;
225 const char *this;
226 int symmetric;
227 + static const char head_by_default[] = "HEAD";
228
229 if (!(dotdot = strstr(arg, "..")))
230 return 0;
@@ -235,9 +236,20 @@ static int try_difference(const char *arg)
236 next += symmetric;
237
238 if (!*next)
238 - next = "HEAD";
239 + next = head_by_default;
240 if (dotdot == arg)
240 - this = "HEAD";
241 + this = head_by_default;
242 +
243 + if (this == head_by_default && next == head_by_default &&
244 + !symmetric) {
245 + /*
246 + * Just ".."? That is not a range but the
247 + * pathspec for the parent directory.
248 + */
249 + *dotdot = '.';
250 + return 0;
251 + }
252 +
253 if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
254 show_rev(NORMAL, end, next);
255 show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
revision.c
+14 -2
@@ -1132,15 +1132,27 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs,
1132 const char *this = arg;
1133 int symmetric = *next == '.';
1134 unsigned int flags_exclude = flags ^ UNINTERESTING;
1135 + static const char head_by_default[] = "HEAD";
1136 unsigned int a_flags;
1137
1138 *dotdot = 0;
1139 next += symmetric;
1140
1141 if (!*next)
1141 - next = "HEAD";
1142 + next = head_by_default;
1143 if (dotdot == arg)
1143 - this = "HEAD";
1144 + this = head_by_default;
1145 + if (this == head_by_default && next == head_by_default &&
1146 + !symmetric) {
1147 + /*
1148 + * Just ".."? That is not a range but the
1149 + * pathspec for the parent directory.
1150 + */
1151 + if (!cant_be_filename) {
1152 + *dotdot = '.';
1153 + return -1;
1154 + }
1155 + }
1156 if (!get_sha1(this, from_sha1) &&
1157 !get_sha1(next, sha1)) {
1158 struct commit *a, *b;
t/t1506-rev-parse-diagnosis.sh
+14
@@ -182,4 +182,18 @@ test_expect_success '<commit>:file correctly diagnosed after a pathname' '
182 test_cmp expect actual
183 '
184
185 +test_expect_success 'dotdot is not an empty set' '
186 + ( H=$(git rev-parse HEAD) && echo $H && echo ^$H ) >expect &&
187 +
188 + git rev-parse HEAD.. >actual &&
189 + test_cmp expect actual &&
190 +
191 + git rev-parse ..HEAD >actual &&
192 + test_cmp expect actual &&
193 +
194 + echo .. >expect &&
195 + git rev-parse .. >actual &&
196 + test_cmp expect actual
197 +'
198 +
199 test_done
t/t4202-log.sh
+7
@@ -806,4 +806,11 @@ test_expect_success 'log --graph with diff and stats' '
806 test_cmp expect actual.sanitized
807 '
808
809 +test_expect_success 'dotdot is a parent directory' '
810 + mkdir -p a/b &&
811 + ( echo sixth && echo fifth ) >expect &&
812 + ( cd a/b && git log --format=%s .. ) >actual &&
813 + test_cmp expect actual
814 +'
815 +
816 test_done