Merge branch 'mz/cherry-pick-cmdline-order' into maint
authorJunio C Hamano <gitster@pobox.com>
Sat, 15 Sep 2012 04:24:18 +0000 (21:24 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 15 Sep 2012 04:24:18 +0000 (21:24 -0700)
* mz/cherry-pick-cmdline-order:
cherry-pick/revert: respect order of revisions to pick
demonstrate broken 'git cherry-pick three one two'
teach log --no-walk=unsorted, which avoids sorting

1  2 
Documentation/rev-list-options.txt
builtin/log.c
revision.c
revision.h
t/t4202-log.sh
index def1340ac73cc3abab8084f14dfb8d54d2476177,9780f4646ac867ef79e143a1976c02f08a085ed1..5436eba6e761716cfeaee2683566137736497791
@@@ -578,33 -578,16 +578,33 @@@ Commit Orderin
  
  By default, the commits are shown in reverse chronological order.
  
 ---topo-order::
 +--date-order::
 +      Show no parents before all of its children are shown, but
 +      otherwise show commits in the commit timestamp order.
  
 -      This option makes them appear in topological order (i.e.
 -      descendant commits are shown before their parents).
 +--topo-order::
 +      Show no parents before all of its children are shown, and
 +      avoid showing commits on multiple lines of history
 +      intermixed.
 ++
 +For example, in a commit history like this:
 ++
 +----------------------------------------------------------------
  
 ---date-order::
 +    ---1----2----4----7
 +      \              \
 +       3----5----6----8---
  
 -      This option is similar to '--topo-order' in the sense that no
 -      parent comes before all of its children, but otherwise things
 -      are still ordered in the commit timestamp order.
 +----------------------------------------------------------------
 ++
 +where the numbers denote the order of commit timestamps, `git
 +rev-list` and friends with `--date-order` show the commits in the
 +timestamp order: 8 7 6 5 4 3 2 1.
 ++
 +With `--topo-order`, they would show 8 6 5 3 7 4 2 1 (or 8 7 4 2 6 5
 +3 1); some older commits are shown before newer ones in order to
 +avoid showing the commits from two parallel development track mixed
 +together.
  
  --reverse::
  
@@@ -636,10 -619,14 +636,14 @@@ These options are mostly targeted for p
        Only useful with '--objects'; print the object IDs that are not
        in packs.
  
- --no-walk::
-       Only show the given revs, but do not traverse their ancestors.
-       This has no effect if a range is specified.
+ --no-walk[=(sorted|unsorted)]::
+       Only show the given commits, but do not traverse their ancestors.
+       This has no effect if a range is specified. If the argument
+       "unsorted" is given, the commits are show in the order they were
+       given on the command line. Otherwise (if "sorted" or no argument
+       was given), the commits are show in reverse chronological order
+       by commit time.
  
  --do-walk::
  
@@@ -777,7 -764,7 +781,7 @@@ options may be given. See linkgit:git-d
  
  --cc::
  
 -      This flag implies the '-c' options and further compresses the
 +      This flag implies the '-c' option and further compresses the
        patch output by omitting uninteresting hunks whose contents in
        the parents have only two variants and the merge result picks
        one of them without modification.
diff --combined builtin/log.c
index c22469cab62cbd5bf9095c876e45b6dfb0c94cac,9e21d1b4909784a4c4104cdcef67568c795307b3..dff79212d0f6e027f3ec316a5892c80b6b55e7af
@@@ -21,7 -21,6 +21,7 @@@
  #include "parse-options.h"
  #include "branch.h"
  #include "streaming.h"
 +#include "version.h"
  
  /* Set a default date-time format for git log ("log.date" config variable) */
  static const char *default_date_mode = NULL;
@@@ -109,9 -108,9 +109,9 @@@ static void cmd_log_init_finish(int arg
                             PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
                             PARSE_OPT_KEEP_DASHDASH);
  
 -      argc = setup_revisions(argc, argv, rev, opt);
        if (quiet)
                rev->diffopt.output_format |= DIFF_FORMAT_NO_OUTPUT;
 +      argc = setup_revisions(argc, argv, rev, opt);
  
        /* Any arguments at this point are not recognized */
        if (argc > 1)
@@@ -367,7 -366,6 +367,7 @@@ int cmd_whatchanged(int argc, const cha
        rev.simplify_history = 0;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
 +      opt.revarg_opt = REVARG_COMMITTISH;
        cmd_log_init(argc, argv, prefix, &rev, &opt);
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_RAW;
@@@ -456,7 -454,7 +456,7 @@@ int cmd_show(int argc, const char **arg
        init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.always_show_header = 1;
-       rev.no_walk = 1;
+       rev.no_walk = REVISION_WALK_NO_WALK_SORTED;
        rev.diffopt.stat_width = -1;    /* Scale to real terminal size */
  
        memset(&opt, 0, sizeof(opt));
        opt.tweak = show_rev_tweak_rev;
        cmd_log_init(argc, argv, prefix, &rev, &opt);
  
 +      if (!rev.no_walk)
 +              return cmd_log_walk(&rev);
 +
        count = rev.pending.nr;
        objects = rev.pending.objects;
        for (i = 0; i < count && !ret; i++) {
@@@ -558,7 -553,6 +558,7 @@@ int cmd_log(int argc, const char **argv
        rev.always_show_header = 1;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
 +      opt.revarg_opt = REVARG_COMMITTISH;
        cmd_log_init(argc, argv, prefix, &rev, &opt);
        return cmd_log_walk(&rev);
  }
@@@ -1134,7 -1128,6 +1134,7 @@@ int cmd_format_patch(int argc, const ch
        rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
        s_r_opt.def = "HEAD";
 +      s_r_opt.revarg_opt = REVARG_COMMITTISH;
  
        if (default_attach) {
                rev.mime_boundary = default_attach;
diff --combined revision.c
index cbcae1086b346e2b956db7ef9b2729385eaa2ec9,2f9dbddf60e966853f776a4e657fb5191790f5e5..dc3fecf903aa0ce4927b9ebe5b95f99cc9c1018d
@@@ -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";
 -              if (!get_sha1(this, from_sha1) &&
 -                  !get_sha1(next, sha1)) {
 +                      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_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;
  }
  
@@@ -1275,7 -1257,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)
@@@ -1312,7 -1294,7 +1312,7 @@@ static int handle_revision_opt(struct r
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
            !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") ||
            !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") ||
-           !prefixcmp(arg, "--remotes="))
+           !prefixcmp(arg, "--remotes=") || !prefixcmp(arg, "--no-walk="))
        {
                unkv[(*unkc)++] = arg;
                return 1;
                revs->topo_order = 1;
        } else if (!strcmp(arg, "--simplify-merges")) {
                revs->simplify_merges = 1;
 +              revs->topo_order = 1;
                revs->rewrite_parents = 1;
                revs->simplify_history = 0;
                revs->limited = 1;
        } else if (!strcmp(arg, "--simplify-by-decoration")) {
                revs->simplify_merges = 1;
 +              revs->topo_order = 1;
                revs->rewrite_parents = 1;
                revs->simplify_history = 0;
                revs->simplify_by_decoration = 1;
@@@ -1707,7 -1687,18 +1707,18 @@@ static int handle_revision_pseudo_opt(c
        } else if (!strcmp(arg, "--not")) {
                *flags ^= UNINTERESTING;
        } else if (!strcmp(arg, "--no-walk")) {
-               revs->no_walk = 1;
+               revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
+       } else if (!prefixcmp(arg, "--no-walk=")) {
+               /*
+                * Detached form ("--no-walk X" as opposed to "--no-walk=X")
+                * not allowed, since the argument is optional.
+                */
+               if (!strcmp(arg + 10, "sorted"))
+                       revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
+               else if (!strcmp(arg + 10, "unsorted"))
+                       revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
+               else
+                       return error("invalid argument to --no-walk");
        } else if (!strcmp(arg, "--do-walk")) {
                revs->no_walk = 0;
        } else {
   */
  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);
                         * but the latter we have checked in the main loop.
                         */
                        for (j = i; j < argc; j++)
 -                              verify_filename(revs->prefix, argv[j]);
 +                              verify_filename(revs->prefix, argv[j], j == i);
  
                        append_prune_data(&prune_data, argv + i);
                        break;
        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);
  
@@@ -1970,9 -1958,8 +1981,9 @@@ static struct commit_list **simplify_on
        }
  
        /*
 -       * Do we know what commit all of our parents should be rewritten to?
 -       * Otherwise we are not ready to rewrite this one yet.
 +       * Do we know what commit all of our parents that matter
 +       * should be rewritten to?  Otherwise we are not ready to
 +       * rewrite this one yet.
         */
        for (cnt = 0, p = commit->parents; p; p = p->next) {
                pst = locate_simplify_state(revs, p->item);
                        tail = &commit_list_insert(p->item, tail)->next;
                        cnt++;
                }
 +              if (revs->first_parent_only)
 +                      break;
        }
        if (cnt) {
                tail = &commit_list_insert(commit, tail)->next;
        for (p = commit->parents; p; p = p->next) {
                pst = locate_simplify_state(revs, p->item);
                p->item = pst->simplified;
 +              if (revs->first_parent_only)
 +                      break;
        }
 -      cnt = remove_duplicate_parents(commit);
 +      if (!revs->first_parent_only)
 +              cnt = remove_duplicate_parents(commit);
 +      else
 +              cnt = 1;
  
        /*
         * It is possible that we are a merge and one side branch
  
  static void simplify_merges(struct rev_info *revs)
  {
 -      struct commit_list *list;
 +      struct commit_list *list, *next;
        struct commit_list *yet_to_do, **tail;
 +      struct commit *commit;
  
 -      if (!revs->topo_order)
 -              sort_in_topological_order(&revs->commits, revs->lifo);
        if (!revs->prune)
                return;
  
        /* feed the list reversed */
        yet_to_do = NULL;
 -      for (list = revs->commits; list; list = list->next)
 -              commit_list_insert(list->item, &yet_to_do);
 +      for (list = revs->commits; list; list = next) {
 +              commit = list->item;
 +              next = list->next;
 +              /*
 +               * Do not free(list) here yet; the original list
 +               * is used later in this function.
 +               */
 +              commit_list_insert(commit, &yet_to_do);
 +      }
        while (yet_to_do) {
                list = yet_to_do;
                yet_to_do = NULL;
                tail = &yet_to_do;
                while (list) {
 -                      struct commit *commit = list->item;
 -                      struct commit_list *next = list->next;
 +                      commit = list->item;
 +                      next = list->next;
                        free(list);
                        list = next;
                        tail = simplify_one(revs, commit, tail);
        revs->commits = NULL;
        tail = &revs->commits;
        while (list) {
 -              struct commit *commit = list->item;
 -              struct commit_list *next = list->next;
                struct merge_simplify_state *st;
 +
 +              commit = list->item;
 +              next = list->next;
                free(list);
                list = next;
                st = locate_simplify_state(revs, commit);
@@@ -2129,10 -2102,11 +2140,11 @@@ int prepare_revision_walk(struct rev_in
                }
                e++;
        }
-       commit_list_sort_by_date(&revs->commits);
        if (!revs->leak_pending)
                free(list);
  
+       if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
+               commit_list_sort_by_date(&revs->commits);
        if (revs->no_walk)
                return 0;
        if (revs->limited)
@@@ -2382,28 -2356,29 +2394,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 revision.h
index cb5ab3513bc9982095e292119b9119d333556256,80e22fea4a93a2ca8c5de3aae34dfce02e2ca3d3..a95bd0b3f3026068046d469a74d1a2d85e50e314
@@@ -41,6 -41,10 +41,10 @@@ struct rev_cmdline_info 
        } *rev;
  };
  
+ #define REVISION_WALK_WALK 0
+ #define REVISION_WALK_NO_WALK_SORTED 1
+ #define REVISION_WALK_NO_WALK_UNSORTED 2
  struct rev_info {
        /* Starting list */
        struct commit_list *commits;
@@@ -62,7 -66,7 +66,7 @@@
        /* Traversal flags */
        unsigned int    dense:1,
                        prune:1,
-                       no_walk:1,
+                       no_walk:2,
                        show_all:1,
                        remove_empty_trees:1,
                        simplify_history:1,
@@@ -184,7 -188,6 +188,7 @@@ struct setup_revision_opt 
        void (*tweak)(struct rev_info *, struct setup_revision_opt *);
        const char *submodule;
        int assume_dashdash;
 +      unsigned revarg_opt;
  };
  
  extern void init_revisions(struct rev_info *revs, const char *prefix);
@@@ -192,9 -195,7 +196,9 @@@ extern int setup_revisions(int argc, co
  extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
                                 const struct option *options,
                                 const char * const usagestr[]);
 -extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
 +#define REVARG_CANNOT_BE_FILENAME 01
 +#define REVARG_COMMITTISH 02
 +extern int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt);
  
  extern void reset_revision_walk(void);
  extern int prepare_revision_walk(struct rev_info *revs);
diff --combined t/t4202-log.sh
index 45058cc8cbe038edfb7bb953d9e10067eb5dc4b6,bd8335580d0fa02e0c7e1b9b1459cac37050ed44..b3ac6be3b0d2cc47af4559bcf7401ef858dae322
@@@ -178,11 -178,21 +178,21 @@@ test_expect_success 'git log --no-walk 
        test_cmp expect actual
  '
  
+ test_expect_success 'git log --no-walk=sorted <commits> sorts by commit time' '
+       git log --no-walk=sorted --oneline 5d31159 804a787 394ef78 > actual &&
+       test_cmp expect actual
+ '
  cat > expect << EOF
  5d31159 fourth
  804a787 sixth
  394ef78 fifth
  EOF
+ test_expect_success 'git log --no-walk=unsorted <commits> leaves list of commits as given' '
+       git log --no-walk=unsorted --oneline 5d31159 804a787 394ef78 > actual &&
+       test_cmp expect actual
+ '
  test_expect_success 'git show <commits> leaves list of commits as given' '
        git show --oneline -s 5d31159 804a787 394ef78 > actual &&
        test_cmp expect actual
@@@ -806,11 -816,4 +816,11 @@@ test_expect_success 'log --graph with d
        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