Merge branch 'jc/dotdot-is-parent-directory'
authorJunio C Hamano <gitster@pobox.com>
Fri, 7 Sep 2012 18:09:18 +0000 (11:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 7 Sep 2012 18:09:18 +0000 (11:09 -0700)
"git log .." errored out saying it is both rev range and a path when
there is no disambiguating "--" is on the command line. Update the
command line parser to interpret ".." as a path in such a case.

* jc/dotdot-is-parent-directory:
specifying ranges: we did not mean to make ".." an empty set

1  2 
builtin/rev-parse.c
revision.c
t/t4202-log.sh
diff --combined builtin/rev-parse.c
index bb3a5161bb548a676ea0410e8b5b116b6c2e98d6,47b4e7adb956517c22edc580892d63a99d9f219a..f267a1d3b57c1f1ab44c83a197f46051f4f13060
@@@ -195,12 -195,6 +195,12 @@@ static int anti_reference(const char *r
        return 0;
  }
  
 +static int show_abbrev(const unsigned char *sha1, void *cb_data)
 +{
 +      show_rev(NORMAL, sha1, NULL);
 +      return 0;
 +}
 +
  static void show_datestring(const char *flag, const char *datestr)
  {
        static char buffer[100];
@@@ -230,6 -224,7 +230,7 @@@ static int try_difference(const char *a
        const char *next;
        const char *this;
        int symmetric;
+       static const char head_by_default[] = "HEAD";
  
        if (!(dotdot = strstr(arg, "..")))
                return 0;
        next += symmetric;
  
        if (!*next)
-               next = "HEAD";
+               next = head_by_default;
        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)) {
 +      if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) {
                show_rev(NORMAL, end, next);
                show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
                if (symmetric) {
@@@ -284,7 -290,7 +296,7 @@@ static int try_parent_shorthands(const 
                return 0;
  
        *dotdot = 0;
 -      if (get_sha1(arg, sha1))
 +      if (get_sha1_committish(arg, sha1))
                return 0;
  
        if (!parents_only)
@@@ -324,15 -330,15 +336,15 @@@ static int cmd_parseopt(int argc, cons
  {
        static int keep_dashdash = 0, stop_at_non_option = 0;
        static char const * const parseopt_usage[] = {
 -              "git rev-parse --parseopt [options] -- [<args>...]",
 +              N_("git rev-parse --parseopt [options] -- [<args>...]"),
                NULL
        };
        static struct option parseopt_opts[] = {
                OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
 -                                      "keep the `--` passed as an arg"),
 +                                      N_("keep the `--` passed as an arg")),
                OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option,
 -                                      "stop parsing after the "
 -                                      "first non-option argument"),
 +                                      N_("stop parsing after the "
 +                                         "first non-option argument")),
                OPT_END(),
        };
  
@@@ -449,11 -455,11 +461,11 @@@ static void die_no_single_rev(int quiet
  }
  
  static const char builtin_rev_parse_usage[] =
 -"git rev-parse --parseopt [options] -- [<args>...]\n"
 -"   or: git rev-parse --sq-quote [<arg>...]\n"
 -"   or: git rev-parse [options] [<arg>...]\n"
 -"\n"
 -"Run \"git rev-parse --parseopt -h\" for more information on the first usage.";
 +N_("git rev-parse --parseopt [options] -- [<args>...]\n"
 +   "   or: git rev-parse --sq-quote [<arg>...]\n"
 +   "   or: git rev-parse [options] [<arg>...]\n"
 +   "\n"
 +   "Run \"git rev-parse --parseopt -h\" for more information on the first usage.");
  
  int cmd_rev_parse(int argc, const char **argv, const char *prefix)
  {
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
 +                      if (!prefixcmp(arg, "--disambiguate=")) {
 +                              for_each_abbrev(arg + 15, show_abbrev, NULL);
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--bisect")) {
                                for_each_ref_in("refs/bisect/bad", show_reference, NULL);
                                for_each_ref_in("refs/bisect/good", anti_reference, NULL);
diff --combined revision.c
index 442a9452334704d90e4457fdeb11014fb886c58a,457868d647378e4b3249f7c3ecae6697bc1ddc04..cbcae1086b346e2b956db7ef9b2729385eaa2ec9
@@@ -345,7 -345,6 +345,7 @@@ static int tree_difference = REV_TREE_S
  static void file_add_remove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
 +                  int sha1_valid,
                    const char *fullpath, unsigned dirty_submodule)
  {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
@@@ -359,7 -358,6 +359,7 @@@ static void file_change(struct diff_opt
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
 +               int old_sha1_valid, int new_sha1_valid,
                 const char *fullpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
@@@ -1002,7 -1000,7 +1002,7 @@@ static int add_parents_only(struct rev_
                flags ^= UNINTERESTING;
                arg++;
        }
 -      if (get_sha1(arg, sha1))
 +      if (get_sha1_committish(arg, sha1))
                return 0;
        while (1) {
                it = get_reference(revs, arg, sha1, 0);
@@@ -1116,16 -1114,16 +1116,16 @@@ static void prepare_show_merge(struct r
        revs->limited = 1;
  }
  
 -int handle_revision_arg(const char *arg_, struct rev_info *revs,
 -                      int flags,
 -                      int cant_be_filename)
 +int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
  {
 -      unsigned mode;
 +      struct object_context oc;
        char *dotdot;
        struct object *object;
        unsigned char sha1[20];
        int local_flags;
        const char *arg = arg_;
 +      int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
 +      unsigned get_sha1_flags = 0;
  
        dotdot = strstr(arg, "..");
        if (dotdot) {
                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)
-                       next = "HEAD";
+                       next = head_by_default;
                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)) {
 +              if (!get_sha1_committish(this, from_sha1) &&
 +                  !get_sha1_committish(next, sha1)) {
                        struct commit *a, *b;
                        struct commit_list *exclude;
  
                local_flags = UNINTERESTING;
                arg++;
        }
 -      if (get_sha1_with_mode(arg, sha1, &mode))
 +
 +      if (revarg_opt & REVARG_COMMITTISH)
 +              get_sha1_flags = GET_SHA1_COMMITTISH;
 +
 +      if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc))
                return revs->ignore_missing ? 0 : -1;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
        add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
 -      add_pending_object_with_mode(revs, object, arg, mode);
 +      add_pending_object_with_mode(revs, object, arg, oc.mode);
        return 0;
  }
  
@@@ -1263,7 -1269,7 +1275,7 @@@ static void read_revisions_from_stdin(s
                        }
                        die("options not supported in --stdin mode");
                }
 -              if (handle_revision_arg(sb.buf, revs, 0, 1))
 +              if (handle_revision_arg(sb.buf, revs, 0, REVARG_CANNOT_BE_FILENAME))
                        die("bad revision '%s'", sb.buf);
        }
        if (seen_dashdash)
@@@ -1714,7 -1720,7 +1726,7 @@@ static int handle_revision_pseudo_opt(c
   */
  int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
  {
 -      int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
 +      int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0, revarg_opt;
        struct cmdline_pathspec prune_data;
        const char *submodule = NULL;
  
  
        /* Second, deal with arguments and options */
        flags = 0;
 +      revarg_opt = opt ? opt->revarg_opt : 0;
 +      if (seen_dashdash)
 +              revarg_opt |= REVARG_CANNOT_BE_FILENAME;
        read_from_stdin = 0;
        for (left = i = 1; i < argc; i++) {
                const char *arg = argv[i];
                        continue;
                }
  
 -              if (handle_revision_arg(arg, revs, flags, seen_dashdash)) {
 +
 +              if (handle_revision_arg(arg, revs, flags, revarg_opt)) {
                        int j;
                        if (seen_dashdash || *arg == '^')
                                die("bad revision '%s'", arg);
        if (revs->def && !revs->pending.nr && !got_rev_arg) {
                unsigned char sha1[20];
                struct object *object;
 -              unsigned mode;
 -              if (get_sha1_with_mode(revs->def, sha1, &mode))
 +              struct object_context oc;
 +              if (get_sha1_with_context(revs->def, 0, sha1, &oc))
                        die("bad default revision '%s'", revs->def);
                object = get_reference(revs, revs->def, sha1, 0);
 -              add_pending_object_with_mode(revs, object, revs->def, mode);
 +              add_pending_object_with_mode(revs, object, revs->def, oc.mode);
        }
  
        /* Did the user ask for any diff output? Run the diff! */
        if (revs->combine_merges)
                revs->ignore_merges = 0;
        revs->diffopt.abbrev = revs->abbrev;
 -      if (diff_setup_done(&revs->diffopt) < 0)
 -              die("diff_setup_done failed");
 +      diff_setup_done(&revs->diffopt);
  
        compile_grep_patterns(&revs->grep_filter);
  
@@@ -2370,28 -2373,29 +2382,28 @@@ static struct commit *get_revision_inte
        }
  
        /*
 -       * Now pick up what they want to give us
 +       * If our max_count counter has reached zero, then we are done. We
 +       * don't simply return NULL because we still might need to show
 +       * boundary commits. But we want to avoid calling get_revision_1, which
 +       * might do a considerable amount of work finding the next commit only
 +       * for us to throw it away.
 +       *
 +       * If it is non-zero, then either we don't have a max_count at all
 +       * (-1), or it is still counting, in which case we decrement.
         */
 -      c = get_revision_1(revs);
 -      if (c) {
 -              while (0 < revs->skip_count) {
 -                      revs->skip_count--;
 -                      c = get_revision_1(revs);
 -                      if (!c)
 -                              break;
 +      if (revs->max_count) {
 +              c = get_revision_1(revs);
 +              if (c) {
 +                      while (0 < revs->skip_count) {
 +                              revs->skip_count--;
 +                              c = get_revision_1(revs);
 +                              if (!c)
 +                                      break;
 +                      }
                }
 -      }
  
 -      /*
 -       * Check the max_count.
 -       */
 -      switch (revs->max_count) {
 -      case -1:
 -              break;
 -      case 0:
 -              c = NULL;
 -              break;
 -      default:
 -              revs->max_count--;
 +              if (revs->max_count > 0)
 +                      revs->max_count--;
        }
  
        if (c)
diff --combined t/t4202-log.sh
index 31869dc0dbcb5f79e4e0f5e307a4d8f617aadbe4,45058cc8cbe038edfb7bb953d9e10067eb5dc4b6..0baaad2a240d401a29b341e1bf01173842a2ab54
@@@ -803,7 -803,14 +803,14 @@@ sanitize_output () 
  test_expect_success 'log --graph with diff and stats' '
        git log --graph --pretty=short --stat -p >actual &&
        sanitize_output >actual.sanitized <actual &&
 -      test_cmp expect actual.sanitized
 +      test_i18ncmp 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