Merge branch 'mz/cherry-pick-cmdline-order'
authorJunio C Hamano <gitster@pobox.com>
Mon, 10 Sep 2012 22:42:54 +0000 (15:42 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 10 Sep 2012 22:42:55 +0000 (15:42 -0700)
"git cherry-pick A C B" used to replay changes in A and then B and
then C if these three commits had committer timestamps in that
order, which is not what the user who said "A C B" naturally expects.

* 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
builtin/revert.c
revision.c
revision.h
sequencer.c
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 e4e15dd55aef5b1cbcb8ddeae6432e6188ff8e4a,9e21d1b4909784a4c4104cdcef67568c795307b3..09cf43e6d40efc13f6b7bda2094fca5bdc1bbc87
@@@ -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;
@@@ -34,8 -33,8 +34,8 @@@ static const char *fmt_patch_subject_pr
  static const char *fmt_pretty;
  
  static const char * const builtin_log_usage[] = {
 -      "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
 -      "   or: git show [options] <object>...",
 +      N_("git log [<options>] [<since>..<until>] [[--] <path>...]\n")
 +      N_("   or: git show [options] <object>..."),
        NULL
  };
  
@@@ -97,9 -96,9 +97,9 @@@ static void cmd_log_init_finish(int arg
        int quiet = 0, source = 0;
  
        const struct option builtin_log_options[] = {
 -              OPT_BOOLEAN(0, "quiet", &quiet, "suppress diff output"),
 -              OPT_BOOLEAN(0, "source", &source, "show source"),
 -              { OPTION_CALLBACK, 0, "decorate", NULL, NULL, "decorate options",
 +              OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")),
 +              OPT_BOOLEAN(0, "source", &source, N_("show source")),
 +              { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
                  PARSE_OPT_OPTARG, decorate_callback},
                OPT_END()
        };
                             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);
  }
@@@ -696,7 -690,7 +696,7 @@@ static int reopen_stdout(struct commit 
        return 0;
  }
  
 -static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)
 +static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
  {
        struct rev_info check_rev;
        struct commit *commit;
        init_patch_ids(ids);
  
        /* given a range a..b get all patch ids for b..a */
 -      init_revisions(&check_rev, prefix);
 +      init_revisions(&check_rev, rev->prefix);
 +      check_rev.max_parents = 1;
        o1->flags ^= UNINTERESTING;
        o2->flags ^= UNINTERESTING;
        add_pending_object(&check_rev, o1, "o1");
                die(_("revision walk setup failed"));
  
        while ((commit = get_revision(&check_rev)) != NULL) {
 -              /* ignore merges */
 -              if (commit->parents && commit->parents->next)
 -                      continue;
 -
                add_commit_patch_id(commit, ids);
        }
  
@@@ -887,7 -884,7 +887,7 @@@ static const char *set_outdir(const cha
  }
  
  static const char * const builtin_format_patch_usage[] = {
 -      "git format-patch [options] [<since> | <revision range>]",
 +      N_("git format-patch [options] [<since> | <revision range>]"),
        NULL
  };
  
@@@ -1060,61 -1057,61 +1060,61 @@@ int cmd_format_patch(int argc, const ch
        char *branch_name = NULL;
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
 -                          "use [PATCH n/m] even with a single patch",
 +                          N_("use [PATCH n/m] even with a single patch"),
                            PARSE_OPT_NOARG, numbered_callback },
                { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
 -                          "use [PATCH] even with multiple patches",
 +                          N_("use [PATCH] even with multiple patches"),
                            PARSE_OPT_NOARG, no_numbered_callback },
 -              OPT_BOOLEAN('s', "signoff", &do_signoff, "add Signed-off-by:"),
 +              OPT_BOOLEAN('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
                OPT_BOOLEAN(0, "stdout", &use_stdout,
 -                          "print patches to standard out"),
 +                          N_("print patches to standard out")),
                OPT_BOOLEAN(0, "cover-letter", &cover_letter,
 -                          "generate a cover letter"),
 +                          N_("generate a cover letter")),
                OPT_BOOLEAN(0, "numbered-files", &numbered_files,
 -                          "use simple number sequence for output file names"),
 -              OPT_STRING(0, "suffix", &fmt_patch_suffix, "sfx",
 -                          "use <sfx> instead of '.patch'"),
 +                          N_("use simple number sequence for output file names")),
 +              OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"),
 +                          N_("use <sfx> instead of '.patch'")),
                OPT_INTEGER(0, "start-number", &start_number,
 -                          "start numbering patches at <n> instead of 1"),
 -              { OPTION_CALLBACK, 0, "subject-prefix", &rev, "prefix",
 -                          "Use [<prefix>] instead of [PATCH]",
 +                          N_("start numbering patches at <n> instead of 1")),
 +              { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
 +                          N_("Use [<prefix>] instead of [PATCH]"),
                            PARSE_OPT_NONEG, subject_prefix_callback },
                { OPTION_CALLBACK, 'o', "output-directory", &output_directory,
 -                          "dir", "store resulting files in <dir>",
 +                          N_("dir"), N_("store resulting files in <dir>"),
                            PARSE_OPT_NONEG, output_directory_callback },
                { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL,
 -                          "don't strip/add [PATCH]",
 +                          N_("don't strip/add [PATCH]"),
                            PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
                OPT_BOOLEAN(0, "no-binary", &no_binary_diff,
 -                          "don't output binary diffs"),
 +                          N_("don't output binary diffs")),
                OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
 -                          "don't include a patch matching a commit upstream"),
 +                          N_("don't include a patch matching a commit upstream")),
                { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL,
 -                "show patch format instead of default (patch + stat)",
 +                N_("show patch format instead of default (patch + stat)"),
                  PARSE_OPT_NONEG | PARSE_OPT_NOARG },
 -              OPT_GROUP("Messaging"),
 -              { OPTION_CALLBACK, 0, "add-header", NULL, "header",
 -                          "add email header", 0, header_callback },
 -              { OPTION_CALLBACK, 0, "to", NULL, "email", "add To: header",
 +              OPT_GROUP(N_("Messaging")),
 +              { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"),
 +                          N_("add email header"), 0, header_callback },
 +              { OPTION_CALLBACK, 0, "to", NULL, N_("email"), N_("add To: header"),
                            0, to_callback },
 -              { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header",
 +              { OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
                            0, cc_callback },
 -              OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id",
 -                          "make first mail a reply to <message-id>"),
 -              { OPTION_CALLBACK, 0, "attach", &rev, "boundary",
 -                          "attach the patch", PARSE_OPT_OPTARG,
 +              OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
 +                          N_("make first mail a reply to <message-id>")),
 +              { OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
 +                          N_("attach the patch"), PARSE_OPT_OPTARG,
                            attach_callback },
 -              { OPTION_CALLBACK, 0, "inline", &rev, "boundary",
 -                          "inline the patch",
 +              { OPTION_CALLBACK, 0, "inline", &rev, N_("boundary"),
 +                          N_("inline the patch"),
                            PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
                            inline_callback },
 -              { OPTION_CALLBACK, 0, "thread", &thread, "style",
 -                          "enable message threading, styles: shallow, deep",
 +              { OPTION_CALLBACK, 0, "thread", &thread, N_("style"),
 +                          N_("enable message threading, styles: shallow, deep"),
                            PARSE_OPT_OPTARG, thread_callback },
 -              OPT_STRING(0, "signature", &signature, "signature",
 -                          "add a signature"),
 +              OPT_STRING(0, "signature", &signature, N_("signature"),
 +                          N_("add a signature")),
                OPT_BOOLEAN(0, "quiet", &quiet,
 -                          "don't print the patch filenames"),
 +                          N_("don't print the patch filenames")),
                OPT_END()
        };
  
        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;
                        if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
                                return 0;
                }
 -              get_patch_ids(&rev, &ids, prefix);
 +              get_patch_ids(&rev, &ids);
        }
  
        if (!use_stdout)
@@@ -1440,7 -1436,7 +1440,7 @@@ static int add_pending_commit(const cha
  }
  
  static const char * const cherry_usage[] = {
 -      "git cherry [-v] [<upstream> [<head> [<limit>]]]",
 +      N_("git cherry [-v] [<upstream> [<head> [<limit>]]]"),
        NULL
  };
  
@@@ -1474,7 -1470,7 +1474,7 @@@ int cmd_cherry(int argc, const char **a
  
        struct option options[] = {
                OPT__ABBREV(&abbrev),
 -              OPT__VERBOSE(&verbose, "be verbose"),
 +              OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT_END()
        };
  
        }
  
        init_revisions(&revs, prefix);
 -      revs.diff = 1;
 -      revs.combine_merges = 0;
 -      revs.ignore_merges = 1;
 -      DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
 +      revs.max_parents = 1;
  
        if (add_pending_commit(head, &revs, 0))
                die(_("Unknown commit %s"), head);
                        return 0;
        }
  
 -      get_patch_ids(&revs, &ids, prefix);
 +      get_patch_ids(&revs, &ids);
  
        if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
                die(_("Unknown commit %s"), limit);
        if (prepare_revision_walk(&revs))
                die(_("revision walk setup failed"));
        while ((commit = get_revision(&revs)) != NULL) {
 -              /* ignore merges */
 -              if (commit->parents && commit->parents->next)
 -                      continue;
 -
                commit_list_insert(commit, &list);
        }
  
diff --combined builtin/revert.c
index 17dc8c8d1e837ceac780b0de8e3b3921e1329894,98ad6410abb603cb8285f88df58ed0937ea64609..c5e36b94c080212ec9241146c20ef2e323663bea
   */
  
  static const char * const revert_usage[] = {
 -      "git revert [options] <commit-ish>",
 -      "git revert <subcommand>",
 +      N_("git revert [options] <commit-ish>"),
 +      N_("git revert <subcommand>"),
        NULL
  };
  
  static const char * const cherry_pick_usage[] = {
 -      "git cherry-pick [options] <commit-ish>",
 -      "git cherry-pick <subcommand>",
 +      N_("git cherry-pick [options] <commit-ish>"),
 +      N_("git cherry-pick <subcommand>"),
        NULL
  };
  
@@@ -100,19 -100,18 +100,19 @@@ static void parse_args(int argc, const 
        int contin = 0;
        int rollback = 0;
        struct option options[] = {
 -              OPT_BOOLEAN(0, "quit", &remove_state, "end revert or cherry-pick sequence"),
 -              OPT_BOOLEAN(0, "continue", &contin, "resume revert or cherry-pick sequence"),
 -              OPT_BOOLEAN(0, "abort", &rollback, "cancel revert or cherry-pick sequence"),
 -              OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
 -              OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
 +              OPT_BOOLEAN(0, "quit", &remove_state, N_("end revert or cherry-pick sequence")),
 +              OPT_BOOLEAN(0, "continue", &contin, N_("resume revert or cherry-pick sequence")),
 +              OPT_BOOLEAN(0, "abort", &rollback, N_("cancel revert or cherry-pick sequence")),
 +              OPT_BOOLEAN('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
 +              OPT_BOOLEAN('e', "edit", &opts->edit, N_("edit the commit message")),
                OPT_NOOP_NOARG('r', NULL),
 -              OPT_BOOLEAN('s', "signoff", &opts->signoff, "add Signed-off-by:"),
 -              OPT_INTEGER('m', "mainline", &opts->mainline, "parent number"),
 +              OPT_BOOLEAN('s', "signoff", &opts->signoff, N_("add Signed-off-by:")),
 +              OPT_INTEGER('m', "mainline", &opts->mainline, N_("parent number")),
                OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
 -              OPT_STRING(0, "strategy", &opts->strategy, "strategy", "merge strategy"),
 -              OPT_CALLBACK('X', "strategy-option", &opts, "option",
 -                      "option for merge strategy", option_parse_x),
 +              OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
 +              OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
 +                      N_("option for merge strategy"), option_parse_x),
 +              OPT_END(),
                OPT_END(),
                OPT_END(),
                OPT_END(),
  
        if (opts->action == REPLAY_PICK) {
                struct option cp_extra[] = {
 -                      OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"),
 -                      OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"),
 -                      OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, "preserve initially empty commits"),
 -                      OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, "keep redundant, empty commits"),
 +                      OPT_BOOLEAN('x', NULL, &opts->record_origin, N_("append commit name")),
 +                      OPT_BOOLEAN(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
 +                      OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
 +                      OPT_BOOLEAN(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
 +                      OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
                        OPT_END(),
                };
                if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
                struct setup_revision_opt s_r_opt;
                opts->revs = xmalloc(sizeof(*opts->revs));
                init_revisions(opts->revs, NULL);
-               opts->revs->no_walk = 1;
+               opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
                if (argc < 2)
                        usage_with_options(usage_str, options);
                memset(&s_r_opt, 0, sizeof(s_r_opt));
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 sequencer.c
index 1ea5293f6ed5395b95fbf63fc41aa28c33db0fa8,bd626806d64b9cd9a814f062d092501315546bdf..f86f116a15326f514fbe0405a9301f4ee2416344
@@@ -311,9 -311,6 +311,9 @@@ static int run_git_commit(const char *d
        if (allow_empty)
                argv_array_push(&array, "--allow-empty");
  
 +      if (opts->allow_empty_message)
 +              argv_array_push(&array, "--allow-empty-message");
 +
        rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
        argv_array_clear(&array);
        return rc;
@@@ -546,7 -543,11 +546,11 @@@ static int do_pick_commit(struct commi
  
  static void prepare_revs(struct replay_opts *opts)
  {
-       if (opts->action != REPLAY_REVERT)
+       /*
+        * picking (but not reverting) ranges (but not individual revisions)
+        * should be done in reverse
+        */
+       if (opts->action == REPLAY_PICK && !opts->revs->no_walk)
                opts->revs->reverse ^= 1;
  
        if (prepare_revision_walk(opts->revs))
diff --combined t/t4202-log.sh
index 0baaad2a240d401a29b341e1bf01173842a2ab54,bd8335580d0fa02e0c7e1b9b1459cac37050ed44..924ba536ca9718da0f4126d53c316a5f7a1926a5
@@@ -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
@@@ -803,14 -813,7 +813,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