specifying ranges: we did not mean to make ".." an empty set
authorJunio C Hamano <gitster@pobox.com>
Mon, 2 May 2011 20:39:16 +0000 (13:39 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 23 Aug 2012 21:37:49 +0000 (14:37 -0700)
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>
Documentation/revisions.txt
builtin/rev-parse.c
revision.c
t/t1506-rev-parse-diagnosis.sh
t/t4202-log.sh
index dc0070bcb7ec2f4e6773066ffb745967d9df6fb3..69d996bc3899a89de49b0cbdf65f5b8ebd47d714 100644 (file)
@@ -213,6 +213,13 @@ of 'r1' and 'r2' and is defined as
 It is the set of commits that are reachable from either one of
 'r1' or 'r2' but not from both.
 
 It is the set of commits that are reachable from either one of
 'r1' or 'r2' but not from both.
 
+In these two shorthands, you can omit one end and let it default to HEAD.
+For example, 'origin..' is a shorthand for 'origin..HEAD' and asks "What
+did I do since I forked from the origin branch?"  Similarly, '..origin'
+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.
+
 Two other shorthands for naming a set that is formed by a commit
 and its parent commits exist.  The 'r1{caret}@' notation means all
 parents of 'r1'.  'r1{caret}!' includes commit 'r1' but excludes
 Two other shorthands for naming a set that is formed by a commit
 and its parent commits exist.  The 'r1{caret}@' notation means all
 parents of 'r1'.  'r1{caret}!' includes commit 'r1' but excludes
index 13495b88f5da1efc2094c0e69abfe93605ee8c03..47b4e7adb956517c22edc580892d63a99d9f219a 100644 (file)
@@ -224,6 +224,7 @@ static int try_difference(const char *arg)
        const char *next;
        const char *this;
        int symmetric;
        const char *next;
        const char *this;
        int symmetric;
+       static const char head_by_default[] = "HEAD";
 
        if (!(dotdot = strstr(arg, "..")))
                return 0;
 
        if (!(dotdot = strstr(arg, "..")))
                return 0;
@@ -235,9 +236,20 @@ static int try_difference(const char *arg)
        next += symmetric;
 
        if (!*next)
        next += symmetric;
 
        if (!*next)
-               next = "HEAD";
+               next = head_by_default;
        if (dotdot == arg)
        if (dotdot == arg)
-               this = "HEAD";
+               this = head_by_default;
+
+       if (this == head_by_default && next == head_by_default &&
+           !symmetric) {
+               /*
+                * Just ".."?  That is not a range but the
+                * pathspec for the parent directory.
+                */
+               *dotdot = '.';
+               return 0;
+       }
+
        if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
                show_rev(NORMAL, end, next);
                show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
        if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
                show_rev(NORMAL, end, next);
                show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
index 5b81a92e3ac65ab0295f25198511fc83b4bbf1c9..457868d647378e4b3249f7c3ecae6697bc1ddc04 100644 (file)
@@ -1132,15 +1132,27 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs,
                const char *this = arg;
                int symmetric = *next == '.';
                unsigned int flags_exclude = flags ^ UNINTERESTING;
                const char *this = arg;
                int symmetric = *next == '.';
                unsigned int flags_exclude = flags ^ UNINTERESTING;
+               static const char head_by_default[] = "HEAD";
                unsigned int a_flags;
 
                *dotdot = 0;
                next += symmetric;
 
                if (!*next)
                unsigned int a_flags;
 
                *dotdot = 0;
                next += symmetric;
 
                if (!*next)
-                       next = "HEAD";
+                       next = head_by_default;
                if (dotdot == arg)
                if (dotdot == arg)
-                       this = "HEAD";
+                       this = head_by_default;
+               if (this == head_by_default && next == head_by_default &&
+                   !symmetric) {
+                       /*
+                        * Just ".."?  That is not a range but the
+                        * pathspec for the parent directory.
+                        */
+                       if (!cant_be_filename) {
+                               *dotdot = '.';
+                               return -1;
+                       }
+               }
                if (!get_sha1(this, from_sha1) &&
                    !get_sha1(next, sha1)) {
                        struct commit *a, *b;
                if (!get_sha1(this, from_sha1) &&
                    !get_sha1(next, sha1)) {
                        struct commit *a, *b;
index c5cb77a0e1f34ac46dd8727341948389624e8f38..f950c101287c2f05bbd70c1faffbcf2cda1edbee 100755 (executable)
@@ -182,4 +182,18 @@ test_expect_success '<commit>:file correctly diagnosed after a pathname' '
        test_cmp expect actual
 '
 
        test_cmp expect actual
 '
 
+test_expect_success 'dotdot is not an empty set' '
+       ( H=$(git rev-parse HEAD) && echo $H && echo ^$H ) >expect &&
+
+       git rev-parse HEAD.. >actual &&
+       test_cmp expect actual &&
+
+       git rev-parse ..HEAD >actual &&
+       test_cmp expect actual &&
+
+       echo .. >expect &&
+       git rev-parse .. >actual &&
+       test_cmp expect actual
+'
+
 test_done
 test_done
index 71be59d446f773c4b567f00c469ac26225d11cb8..45058cc8cbe038edfb7bb953d9e10067eb5dc4b6 100755 (executable)
@@ -806,4 +806,11 @@ test_expect_success 'log --graph with diff and stats' '
        test_cmp expect actual.sanitized
 '
 
        test_cmp expect actual.sanitized
 '
 
+test_expect_success 'dotdot is a parent directory' '
+       mkdir -p a/b &&
+       ( echo sixth && echo fifth ) >expect &&
+       ( cd a/b && git log --format=%s .. ) >actual &&
+       test_cmp expect actual
+'
+
 test_done
 test_done