Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Thu, 17 Jul 2008 00:10:28 +0000 (17:10 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 17 Jul 2008 00:10:28 +0000 (17:10 -0700)
* maint:
Start preparing 1.5.6.4 release notes
git fetch-pack: do not complain about "no common commits" in an empty repo
rebase-i: keep old parents when preserving merges
t7600-merge: Use test_expect_failure to test option parsing
Fix buffer overflow in prepare_attr_stack
Fix buffer overflow in git diff
Fix buffer overflow in git-grep
git-cvsserver: fix call to nonexistant cleanupWorkDir()
Documentation/git-cherry-pick.txt et al.: Fix misleading -n description

Conflicts:
RelNotes

1  2 
Documentation/git-cherry-pick.txt
Documentation/git-revert.txt
diff.c
git-rebase--interactive.sh
revision.c
t/t7600-merge.sh
index a691173ba13370330b97907cf751342cf1c0fe44,4ef5af4ca9ff31933c14f3d965348d67be9f983f..50fb3d5d545351ce5caf1576f529dc537d8353da
@@@ -7,7 -7,7 +7,7 @@@ git-cherry-pick - Apply the change intr
  
  SYNOPSIS
  --------
 -'git-cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] <commit>
 +'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] <commit>
  
  DESCRIPTION
  -----------
@@@ -19,12 -19,12 +19,12 @@@ OPTION
  -------
  <commit>::
        Commit to cherry-pick.
 -      For a more complete list of ways to spell commits, see
 +      For a more complete list of ways to spell commits, see the
        "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
  
  -e::
  --edit::
 -      With this option, `git-cherry-pick` will let you edit the commit
 +      With this option, 'git-cherry-pick' will let you edit the commit
        message prior to committing.
  
  -x::
        Usually the command automatically creates a commit with
        a commit log message stating which commit was
        cherry-picked.  This flag applies the change necessary
-       to cherry-pick the named commit to your working tree,
-       but does not make the commit.  In addition, when this
-       option is used, your working tree does not have to match
+       to cherry-pick the named commit to your working tree
+       and the index, but does not make the commit.  In addition,
+       when this option is used, your index does not have to match
        the HEAD commit.  The cherry-pick is done against the
-       beginning state of your working tree.
+       beginning state of your index.
  +
  This is useful when cherry-picking more than one commits'
- effect to your working tree in a row.
+ effect to your index in a row.
  
  -s::
  --signoff::
index 5411edca96b50e28996357bf75ce4e32796ee620,f7f4bd4689a3b160d27bc28e54a8c9a0e12e81cc..271850f51134ee9b991048b5da07775b5dde65fc
@@@ -7,7 -7,7 +7,7 @@@ git-revert - Revert an existing commi
  
  SYNOPSIS
  --------
 -'git-revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>
 +'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>
  
  DESCRIPTION
  -----------
@@@ -24,7 -24,7 +24,7 @@@ OPTION
  
  -e::
  --edit::
 -      With this option, `git-revert` will let you edit the commit
 +      With this option, 'git-revert' will let you edit the commit
        message prior to committing the revert. This is the default if
        you run the command from a terminal.
  
        relative to the specified parent.
  
  --no-edit::
 -      With this option, `git-revert` will not start the commit
 +      With this option, 'git-revert' will not start the commit
        message editor.
  
  -n::
  --no-commit::
        Usually the command automatically creates a commit with
-       a commit log message stating which commit was reverted.
-       This flag applies the change necessary to revert the
-       named commit to your working tree, but does not make the
-       commit.  In addition, when this option is used, your
-       working tree does not have to match the HEAD commit.
-       The revert is done against the beginning state of your
-       working tree.
+       a commit log message stating which commit was
+       reverted.  This flag applies the change necessary
+       to revert the named commit to your working tree
+       and the index, but does not make the commit.  In addition,
+       when this option is used, your index does not have to match
+       the HEAD commit.  The revert is done against the
+       beginning state of your index.
  +
  This is useful when reverting more than one commits'
- effect to your working tree in a row.
+ effect to your index in a row.
  
  -s::
  --signoff::
diff --combined diff.c
index 6a39b393f3e276dd0e0352b5c05eec6989940854,386de826d3a050e5dfce55636fc2e65129b6da2b..a07812c5c75f1f579295df392efed27f6a5f6fc1
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -531,9 -531,9 +531,9 @@@ static void emit_add_line(const char *r
        else {
                /* Emit just the prefix, then the rest. */
                emit_line(ecbdata->file, set, reset, line, ecbdata->nparents);
 -              (void)check_and_emit_line(line + ecbdata->nparents,
 -                  len - ecbdata->nparents, ecbdata->ws_rule,
 -                  ecbdata->file, set, reset, ws);
 +              ws_check_emit(line + ecbdata->nparents,
 +                            len - ecbdata->nparents, ecbdata->ws_rule,
 +                            ecbdata->file, set, reset, ws);
        }
  }
  
@@@ -826,12 -826,12 +826,12 @@@ static void show_stats(struct diffstat_
        /* Sanity: give at least 5 columns to the graph,
         * but leave at least 10 columns for the name.
         */
 -      if (width < name_width + 15) {
 -              if (name_width <= 25)
 -                      width = name_width + 15;
 -              else
 -                      name_width = width - 15;
 -      }
 +      if (width < 25)
 +              width = 25;
 +      if (name_width < 10)
 +              name_width = 10;
 +      else if (width < name_width + 15)
 +              name_width = width - 15;
  
        /* Find the longest filename and max number of changes */
        reset = diff_get_color_opt(options, DIFF_RESET);
                        total = add + del;
                }
                show_name(options->file, prefix, name, len, reset, set);
 -              fprintf(options->file, "%5d ", added + deleted);
 +              fprintf(options->file, "%5d%s", added + deleted,
 +                              added + deleted ? " " : "");
                show_graph(options->file, '+', add, add_c, reset);
                show_graph(options->file, '-', del, del_c, reset);
                fprintf(options->file, "\n");
@@@ -1132,85 -1131,42 +1132,85 @@@ static void free_diffstat_info(struct d
  struct checkdiff_t {
        struct xdiff_emit_state xm;
        const char *filename;
 -      int lineno, color_diff;
 +      int lineno;
 +      struct diff_options *o;
        unsigned ws_rule;
        unsigned status;
 -      FILE *file;
 +      int trailing_blanks_start;
  };
  
 +static int is_conflict_marker(const char *line, unsigned long len)
 +{
 +      char firstchar;
 +      int cnt;
 +
 +      if (len < 8)
 +              return 0;
 +      firstchar = line[0];
 +      switch (firstchar) {
 +      case '=': case '>': case '<':
 +              break;
 +      default:
 +              return 0;
 +      }
 +      for (cnt = 1; cnt < 7; cnt++)
 +              if (line[cnt] != firstchar)
 +                      return 0;
 +      /* line[0] thru line[6] are same as firstchar */
 +      if (firstchar == '=') {
 +              /* divider between ours and theirs? */
 +              if (len != 8 || line[7] != '\n')
 +                      return 0;
 +      } else if (len < 8 || !isspace(line[7])) {
 +              /* not divider before ours nor after theirs */
 +              return 0;
 +      }
 +      return 1;
 +}
 +
  static void checkdiff_consume(void *priv, char *line, unsigned long len)
  {
        struct checkdiff_t *data = priv;
 -      const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE);
 -      const char *reset = diff_get_color(data->color_diff, DIFF_RESET);
 -      const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW);
 +      int color_diff = DIFF_OPT_TST(data->o, COLOR_DIFF);
 +      const char *ws = diff_get_color(color_diff, DIFF_WHITESPACE);
 +      const char *reset = diff_get_color(color_diff, DIFF_RESET);
 +      const char *set = diff_get_color(color_diff, DIFF_FILE_NEW);
        char *err;
  
        if (line[0] == '+') {
                unsigned bad;
                data->lineno++;
 -              bad = check_and_emit_line(line + 1, len - 1,
 -                  data->ws_rule, NULL, NULL, NULL, NULL);
 +              if (!ws_blank_line(line + 1, len - 1, data->ws_rule))
 +                      data->trailing_blanks_start = 0;
 +              else if (!data->trailing_blanks_start)
 +                      data->trailing_blanks_start = data->lineno;
 +              if (is_conflict_marker(line + 1, len - 1)) {
 +                      data->status |= 1;
 +                      fprintf(data->o->file,
 +                              "%s:%d: leftover conflict marker\n",
 +                              data->filename, data->lineno);
 +              }
 +              bad = ws_check(line + 1, len - 1, data->ws_rule);
                if (!bad)
                        return;
                data->status |= bad;
                err = whitespace_error_string(bad);
 -              fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err);
 +              fprintf(data->o->file, "%s:%d: %s.\n",
 +                      data->filename, data->lineno, err);
                free(err);
 -              emit_line(data->file, set, reset, line, 1);
 -              (void)check_and_emit_line(line + 1, len - 1, data->ws_rule,
 -                  data->file, set, reset, ws);
 -      } else if (line[0] == ' ')
 +              emit_line(data->o->file, set, reset, line, 1);
 +              ws_check_emit(line + 1, len - 1, data->ws_rule,
 +                            data->o->file, set, reset, ws);
 +      } else if (line[0] == ' ') {
                data->lineno++;
 -      else if (line[0] == '@') {
 +              data->trailing_blanks_start = 0;
 +      } else if (line[0] == '@') {
                char *plus = strchr(line, '+');
                if (plus)
                        data->lineno = strtol(plus, NULL, 10) - 1;
                else
                        die("invalid diff");
 +              data->trailing_blanks_start = 0;
        }
  }
  
@@@ -1583,9 -1539,8 +1583,9 @@@ static void builtin_diffstat(const cha
  
  static void builtin_checkdiff(const char *name_a, const char *name_b,
                              const char *attr_path,
 -                           struct diff_filespec *one,
 -                           struct diff_filespec *two, struct diff_options *o)
 +                            struct diff_filespec *one,
 +                            struct diff_filespec *two,
 +                            struct diff_options *o)
  {
        mmfile_t mf1, mf2;
        struct checkdiff_t data;
        data.xm.consume = checkdiff_consume;
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
 -      data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
 +      data.o = o;
        data.ws_rule = whitespace_rule(attr_path);
 -      data.file = o->file;
  
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
  
 +      /*
 +       * All the other codepaths check both sides, but not checking
 +       * the "old" side here is deliberate.  We are checking the newly
 +       * introduced changes, and as long as the "new" side is text, we
 +       * can and should check what it introduces.
 +       */
        if (diff_filespec_is_binary(two))
                goto free_and_return;
        else {
                ecb.outf = xdiff_outf;
                ecb.priv = &data;
                xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 +
 +              if (data.trailing_blanks_start) {
 +                      fprintf(o->file, "%s:%d: ends with blank lines.\n",
 +                              data.filename, data.trailing_blanks_start);
 +                      data.status = 1; /* report errors */
 +              }
        }
   free_and_return:
        diff_free_filespec_data(one);
@@@ -3412,9 -3356,8 +3412,8 @@@ int diff_result_code(struct diff_option
  void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
-                   const char *base, const char *path)
+                   const char *concatpath)
  {
-       char concatpath[PATH_MAX];
        struct diff_filespec *one, *two;
  
        if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode))
                addremove = (addremove == '+' ? '-' :
                             addremove == '-' ? '+' : addremove);
  
-       if (!path) path = "";
-       sprintf(concatpath, "%s%s", base, path);
        if (options->prefix &&
            strncmp(concatpath, options->prefix, options->prefix_length))
                return;
@@@ -3459,9 -3399,8 +3455,8 @@@ void diff_change(struct diff_options *o
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
-                const char *base, const char *path)
+                const char *concatpath)
  {
-       char concatpath[PATH_MAX];
        struct diff_filespec *one, *two;
  
        if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode)
                tmp = old_mode; old_mode = new_mode; new_mode = tmp;
                tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
        }
-       if (!path) path = "";
-       sprintf(concatpath, "%s%s", base, path);
  
        if (options->prefix &&
            strncmp(concatpath, options->prefix, options->prefix_length))
index da79a2456814e4038a42238e0865d8950c14dff0,e3f65bd8808112aebf98f554bd24df38a5e6408c..e63a864c7b1b5057737fda1e52c88d1d5e40322f
  # The original idea comes from Eric W. Biederman, in
  # http://article.gmane.org/gmane.comp.version-control.git/22407
  
 -USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose]
 -      [--onto <branch>] <upstream> [<branch>])'
 +OPTIONS_KEEPDASHDASH=
 +OPTIONS_SPEC="\
 +git-rebase [-i] [options] [--] <upstream> [<branch>]
 +git-rebase [-i] (--continue | --abort | --skip)
 +--
 + Available options are
 +v,verbose          display a diffstat of what changed upstream
 +onto=              rebase onto given branch instead of upstream
 +p,preserve-merges  try to recreate merges instead of ignoring them
 +s,strategy=        use the given merge strategy
 +m,merge            always used (no-op)
 +i,interactive      always used (no-op)
 + Actions:
 +continue           continue rebasing process
 +abort              abort rebasing process and restore original branch
 +skip               skip current patch and continue rebasing process
 +"
  
 -OPTIONS_SPEC=
  . git-sh-setup
  require_work_tree
  
 -DOTEST="$GIT_DIR/.dotest-merge"
 +DOTEST="$GIT_DIR/rebase-merge"
  TODO="$DOTEST"/git-rebase-todo
  DONE="$DOTEST"/done
  MSG="$DOTEST"/message
@@@ -39,8 -25,10 +39,8 @@@ SQUASH_MSG="$DOTEST"/message-squas
  REWRITTEN="$DOTEST"/rewritten
  PRESERVE_MERGES=
  STRATEGY=
 +ONTO=
  VERBOSE=
 -test -d "$REWRITTEN" && PRESERVE_MERGES=t
 -test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
 -test -f "$DOTEST"/verbose && VERBOSE=t
  
  GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
  mark the corrected paths with 'git add <paths>', and
@@@ -174,6 -162,8 +174,8 @@@ pick_one_preserving_merges () 
                                new_parents="$new_parents $new_p"
                                ;;
                        esac
+               else
+                       new_parents="$new_parents $p"
                fi
        done
        case $fast_forward in
@@@ -378,27 -368,10 +380,27 @@@ do_rest () 
        done
  }
  
 +# check if no other options are set
 +is_standalone () {
 +      test $# -eq 2 -a "$2" = '--' &&
 +      test -z "$ONTO" &&
 +      test -z "$PRESERVE_MERGES" &&
 +      test -z "$STRATEGY" &&
 +      test -z "$VERBOSE"
 +}
 +
 +get_saved_options () {
 +      test -d "$REWRITTEN" && PRESERVE_MERGES=t
 +      test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
 +      test -f "$DOTEST"/verbose && VERBOSE=t
 +}
 +
  while test $# != 0
  do
        case "$1" in
        --continue)
 +              is_standalone "$@" || usage
 +              get_saved_options
                comment_for_reflog continue
  
                test -d "$DOTEST" || die "No interactive rebase running"
                do_rest
                ;;
        --abort)
 +              is_standalone "$@" || usage
 +              get_saved_options
                comment_for_reflog abort
  
                git rerere clear
                exit
                ;;
        --skip)
 +              is_standalone "$@" || usage
 +              get_saved_options
                comment_for_reflog skip
  
                git rerere clear
  
                output git reset --hard && do_rest
                ;;
 -      -s|--strategy)
 +      -s)
                case "$#,$1" in
                *,*=*)
                        STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
                        shift ;;
                esac
                ;;
 -      -m|--merge)
 +      -m)
                # we use merge anyway
                ;;
 -      -C*)
 -              die "Interactive rebase uses merge, so $1 does not make sense"
 -              ;;
 -      -v|--verbose)
 +      -v)
                VERBOSE=t
                ;;
 -      -p|--preserve-merges)
 +      -p)
                PRESERVE_MERGES=t
                ;;
 -      -i|--interactive)
 +      -i)
                # yeah, we know
                ;;
 -      ''|-h)
 -              usage
 +      --onto)
 +              shift
 +              ONTO=$(git rev-parse --verify "$1") ||
 +                      die "Does not point to a valid commit: $1"
                ;;
 -      *)
 +      --)
 +              shift
 +              test $# -eq 1 -o $# -eq 2 || usage
                test -d "$DOTEST" &&
                        die "Interactive rebase already started"
  
  
                comment_for_reflog start
  
 -              ONTO=
 -              case "$1" in
 -              --onto)
 -                      ONTO=$(git rev-parse --verify "$2") ||
 -                              die "Does not point to a valid commit: $2"
 -                      shift; shift
 -                      ;;
 -              esac
 -
                require_clean_work_tree
  
                UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
@@@ -574,7 -551,6 +576,7 @@@ EO
                has_action "$TODO" ||
                        die_abort "Nothing to do"
  
 +              git update-ref ORIG_HEAD $HEAD
                output git checkout $ONTO && do_rest
                ;;
        esac
diff --combined revision.c
index 846af7bd20274fba2801fbafe50dc12b7252aecc,8dc3ca7bf696c14ad73f4ebe0fe845cba298c20e..3897fec53170c50921eb1952bc4bdf9c08480638
@@@ -10,7 -10,6 +10,7 @@@
  #include "grep.h"
  #include "reflog-walk.h"
  #include "patch-ids.h"
 +#include "decorate.h"
  
  volatile show_early_output_fn_t show_early_output;
  
@@@ -260,7 -259,7 +260,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,
-                   const char *base, const char *path)
+                   const char *fullpath)
  {
        int diff = REV_TREE_DIFFERENT;
  
@@@ -286,7 -285,7 +286,7 @@@ static void file_change(struct diff_opt
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
-                const char *base, const char *path)
+                const char *fullpath)
  {
        tree_difference = REV_TREE_DIFFERENT;
        DIFF_OPT_SET(options, HAS_CHANGES);
@@@ -413,26 -412,10 +413,26 @@@ static void try_to_simplify_commit(stru
        commit->object.flags |= TREESAME;
  }
  
 -static int add_parents_to_list(struct rev_info *revs, struct commit *commit, struct commit_list **list)
 +static void insert_by_date_cached(struct commit *p, struct commit_list **head,
 +                  struct commit_list *cached_base, struct commit_list **cache)
 +{
 +      struct commit_list *new_entry;
 +
 +      if (cached_base && p->date < cached_base->item->date)
 +              new_entry = insert_by_date(p, &cached_base->next);
 +      else
 +              new_entry = insert_by_date(p, head);
 +
 +      if (cache && (!*cache || p->date < (*cache)->item->date))
 +              *cache = new_entry;
 +}
 +
 +static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
 +                  struct commit_list **list, struct commit_list **cache_ptr)
  {
        struct commit_list *parent = commit->parents;
        unsigned left_flag;
 +      struct commit_list *cached_base = cache_ptr ? *cache_ptr : NULL;
  
        if (commit->object.flags & ADDED)
                return 0;
                        if (p->object.flags & SEEN)
                                continue;
                        p->object.flags |= SEEN;
 -                      insert_by_date(p, list);
 +                      insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
                return 0;
        }
                p->object.flags |= left_flag;
                if (!(p->object.flags & SEEN)) {
                        p->object.flags |= SEEN;
 -                      insert_by_date(p, list);
 +                      insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
                if(revs->first_parent_only)
                        break;
@@@ -628,7 -611,7 +628,7 @@@ static int limit_list(struct rev_info *
  
                if (revs->max_age != -1 && (commit->date < revs->max_age))
                        obj->flags |= UNINTERESTING;
 -              if (add_parents_to_list(revs, commit, &list) < 0)
 +              if (add_parents_to_list(revs, commit, &list, NULL) < 0)
                        return -1;
                if (obj->flags & UNINTERESTING) {
                        mark_parents_uninteresting(commit);
@@@ -927,23 -910,6 +927,23 @@@ int handle_revision_arg(const char *arg
        return 0;
  }
  
 +void read_revisions_from_stdin(struct rev_info *revs)
 +{
 +      char line[1000];
 +
 +      while (fgets(line, sizeof(line), stdin) != NULL) {
 +              int len = strlen(line);
 +              if (len && line[len - 1] == '\n')
 +                      line[--len] = '\0';
 +              if (!len)
 +                      break;
 +              if (line[0] == '-')
 +                      die("options not supported in --stdin mode");
 +              if (handle_revision_arg(line, revs, 0, 1))
 +                      die("bad revision '%s'", line);
 +      }
 +}
 +
  static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
  {
        if (!revs->grep_filter) {
@@@ -990,226 -956,6 +990,226 @@@ static void add_ignore_packed(struct re
        revs->ignore_packed[num] = NULL;
  }
  
 +static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
 +                             int *unkc, const char **unkv)
 +{
 +      const char *arg = argv[0];
 +
 +      /* pseudo revision arguments */
 +      if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
 +          !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
 +          !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
 +          !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk"))
 +      {
 +              unkv[(*unkc)++] = arg;
 +              return 0;
 +      }
 +
 +      if (!prefixcmp(arg, "--max-count=")) {
 +              revs->max_count = atoi(arg + 12);
 +      } else if (!prefixcmp(arg, "--skip=")) {
 +              revs->skip_count = atoi(arg + 7);
 +      } else if ((*arg == '-') && isdigit(arg[1])) {
 +      /* accept -<digit>, like traditional "head" */
 +              revs->max_count = atoi(arg + 1);
 +      } else if (!strcmp(arg, "-n")) {
 +              if (argc <= 1)
 +                      return error("-n requires an argument");
 +              revs->max_count = atoi(argv[1]);
 +              return 2;
 +      } else if (!prefixcmp(arg, "-n")) {
 +              revs->max_count = atoi(arg + 2);
 +      } else if (!prefixcmp(arg, "--max-age=")) {
 +              revs->max_age = atoi(arg + 10);
 +      } else if (!prefixcmp(arg, "--since=")) {
 +              revs->max_age = approxidate(arg + 8);
 +      } else if (!prefixcmp(arg, "--after=")) {
 +              revs->max_age = approxidate(arg + 8);
 +      } else if (!prefixcmp(arg, "--min-age=")) {
 +              revs->min_age = atoi(arg + 10);
 +      } else if (!prefixcmp(arg, "--before=")) {
 +              revs->min_age = approxidate(arg + 9);
 +      } else if (!prefixcmp(arg, "--until=")) {
 +              revs->min_age = approxidate(arg + 8);
 +      } else if (!strcmp(arg, "--first-parent")) {
 +              revs->first_parent_only = 1;
 +      } else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
 +              init_reflog_walk(&revs->reflog_info);
 +      } else if (!strcmp(arg, "--default")) {
 +              if (argc <= 1)
 +                      return error("bad --default argument");
 +              revs->def = argv[1];
 +              return 2;
 +      } else if (!strcmp(arg, "--merge")) {
 +              revs->show_merge = 1;
 +      } else if (!strcmp(arg, "--topo-order")) {
 +              revs->lifo = 1;
 +              revs->topo_order = 1;
 +      } else if (!strcmp(arg, "--date-order")) {
 +              revs->lifo = 0;
 +              revs->topo_order = 1;
 +      } else if (!prefixcmp(arg, "--early-output")) {
 +              int count = 100;
 +              switch (arg[14]) {
 +              case '=':
 +                      count = atoi(arg+15);
 +                      /* Fallthrough */
 +              case 0:
 +                      revs->topo_order = 1;
 +                     revs->early_output = count;
 +              }
 +      } else if (!strcmp(arg, "--parents")) {
 +              revs->rewrite_parents = 1;
 +              revs->print_parents = 1;
 +      } else if (!strcmp(arg, "--dense")) {
 +              revs->dense = 1;
 +      } else if (!strcmp(arg, "--sparse")) {
 +              revs->dense = 0;
 +      } else if (!strcmp(arg, "--show-all")) {
 +              revs->show_all = 1;
 +      } else if (!strcmp(arg, "--remove-empty")) {
 +              revs->remove_empty_trees = 1;
 +      } else if (!strcmp(arg, "--no-merges")) {
 +              revs->no_merges = 1;
 +      } else if (!strcmp(arg, "--boundary")) {
 +              revs->boundary = 1;
 +      } else if (!strcmp(arg, "--left-right")) {
 +              revs->left_right = 1;
 +      } else if (!strcmp(arg, "--cherry-pick")) {
 +              revs->cherry_pick = 1;
 +              revs->limited = 1;
 +      } else if (!strcmp(arg, "--objects")) {
 +              revs->tag_objects = 1;
 +              revs->tree_objects = 1;
 +              revs->blob_objects = 1;
 +      } else if (!strcmp(arg, "--objects-edge")) {
 +              revs->tag_objects = 1;
 +              revs->tree_objects = 1;
 +              revs->blob_objects = 1;
 +              revs->edge_hint = 1;
 +      } else if (!strcmp(arg, "--unpacked")) {
 +              revs->unpacked = 1;
 +              free(revs->ignore_packed);
 +              revs->ignore_packed = NULL;
 +              revs->num_ignore_packed = 0;
 +      } else if (!prefixcmp(arg, "--unpacked=")) {
 +              revs->unpacked = 1;
 +              add_ignore_packed(revs, arg+11);
 +      } else if (!strcmp(arg, "-r")) {
 +              revs->diff = 1;
 +              DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
 +      } else if (!strcmp(arg, "-t")) {
 +              revs->diff = 1;
 +              DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
 +              DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
 +      } else if (!strcmp(arg, "-m")) {
 +              revs->ignore_merges = 0;
 +      } else if (!strcmp(arg, "-c")) {
 +              revs->diff = 1;
 +              revs->dense_combined_merges = 0;
 +              revs->combine_merges = 1;
 +      } else if (!strcmp(arg, "--cc")) {
 +              revs->diff = 1;
 +              revs->dense_combined_merges = 1;
 +              revs->combine_merges = 1;
 +      } else if (!strcmp(arg, "-v")) {
 +              revs->verbose_header = 1;
 +      } else if (!strcmp(arg, "--pretty")) {
 +              revs->verbose_header = 1;
 +              get_commit_format(arg+8, revs);
 +      } else if (!prefixcmp(arg, "--pretty=")) {
 +              revs->verbose_header = 1;
 +              get_commit_format(arg+9, revs);
 +      } else if (!strcmp(arg, "--graph")) {
 +              revs->topo_order = 1;
 +              revs->rewrite_parents = 1;
 +              revs->graph = graph_init(revs);
 +      } else if (!strcmp(arg, "--root")) {
 +              revs->show_root_diff = 1;
 +      } else if (!strcmp(arg, "--no-commit-id")) {
 +              revs->no_commit_id = 1;
 +      } else if (!strcmp(arg, "--always")) {
 +              revs->always_show_header = 1;
 +      } else if (!strcmp(arg, "--no-abbrev")) {
 +              revs->abbrev = 0;
 +      } else if (!strcmp(arg, "--abbrev")) {
 +              revs->abbrev = DEFAULT_ABBREV;
 +      } else if (!prefixcmp(arg, "--abbrev=")) {
 +              revs->abbrev = strtoul(arg + 9, NULL, 10);
 +              if (revs->abbrev < MINIMUM_ABBREV)
 +                      revs->abbrev = MINIMUM_ABBREV;
 +              else if (revs->abbrev > 40)
 +                      revs->abbrev = 40;
 +      } else if (!strcmp(arg, "--abbrev-commit")) {
 +              revs->abbrev_commit = 1;
 +      } else if (!strcmp(arg, "--full-diff")) {
 +              revs->diff = 1;
 +              revs->full_diff = 1;
 +      } else if (!strcmp(arg, "--full-history")) {
 +              revs->simplify_history = 0;
 +      } else if (!strcmp(arg, "--relative-date")) {
 +              revs->date_mode = DATE_RELATIVE;
 +      } else if (!strncmp(arg, "--date=", 7)) {
 +              revs->date_mode = parse_date_format(arg + 7);
 +      } else if (!strcmp(arg, "--log-size")) {
 +              revs->show_log_size = 1;
 +      }
 +      /*
 +       * Grepping the commit log
 +       */
 +      else if (!prefixcmp(arg, "--author=")) {
 +              add_header_grep(revs, "author", arg+9);
 +      } else if (!prefixcmp(arg, "--committer=")) {
 +              add_header_grep(revs, "committer", arg+12);
 +      } else if (!prefixcmp(arg, "--grep=")) {
 +              add_message_grep(revs, arg+7);
 +      } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
 +              if (revs->grep_filter)
 +                      revs->grep_filter->regflags |= REG_EXTENDED;
 +      } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
 +              if (revs->grep_filter)
 +                      revs->grep_filter->regflags |= REG_ICASE;
 +      } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
 +              if (revs->grep_filter)
 +                      revs->grep_filter->fixed = 1;
 +      } else if (!strcmp(arg, "--all-match")) {
 +              if (revs->grep_filter)
 +                      revs->grep_filter->all_match = 1;
 +      } else if (!prefixcmp(arg, "--encoding=")) {
 +              arg += 11;
 +              if (strcmp(arg, "none"))
 +                      git_log_output_encoding = xstrdup(arg);
 +              else
 +                      git_log_output_encoding = "";
 +      } else if (!strcmp(arg, "--reverse")) {
 +              revs->reverse ^= 1;
 +      } else if (!strcmp(arg, "--children")) {
 +              revs->children.name = "children";
 +              revs->limited = 1;
 +      } else {
 +              int opts = diff_opt_parse(&revs->diffopt, argv, argc);
 +              if (!opts)
 +                      unkv[(*unkc)++] = arg;
 +              return opts;
 +      }
 +
 +      return 1;
 +}
 +
 +void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
 +                      const struct option *options,
 +                      const char * const usagestr[])
 +{
 +      int n = handle_revision_opt(revs, ctx->argc, ctx->argv,
 +                                  &ctx->cpidx, ctx->out);
 +      if (n <= 0) {
 +              error("unknown option `%s'", ctx->argv[0]);
 +              usage_with_options(usagestr, options);
 +      }
 +      ctx->argv += n;
 +      ctx->argc -= n;
 +}
 +
  /*
   * Parse revision information, filling in the "rev_info" structure,
   * and removing the used arguments from the argument list.
   */
  int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
  {
 -      int i, flags, seen_dashdash, show_merge;
 -      const char **unrecognized = argv + 1;
 -      int left = 1;
 -      int all_match = 0;
 -      int regflags = 0;
 -      int fixed = 0;
 +      int i, flags, left, seen_dashdash;
  
        /* First, search for "--" */
        seen_dashdash = 0;
                break;
        }
  
 -      flags = show_merge = 0;
 -      for (i = 1; i < argc; i++) {
 +      /* Second, deal with arguments and options */
 +      flags = 0;
 +      for (left = i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if (*arg == '-') {
                        int opts;
 -                      if (!prefixcmp(arg, "--max-count=")) {
 -                              revs->max_count = atoi(arg + 12);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--skip=")) {
 -                              revs->skip_count = atoi(arg + 7);
 -                              continue;
 -                      }
 -                      /* accept -<digit>, like traditional "head" */
 -                      if ((*arg == '-') && isdigit(arg[1])) {
 -                              revs->max_count = atoi(arg + 1);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "-n")) {
 -                              if (argc <= i + 1)
 -                                      die("-n requires an argument");
 -                              revs->max_count = atoi(argv[++i]);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "-n")) {
 -                              revs->max_count = atoi(arg + 2);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--max-age=")) {
 -                              revs->max_age = atoi(arg + 10);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--since=")) {
 -                              revs->max_age = approxidate(arg + 8);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--after=")) {
 -                              revs->max_age = approxidate(arg + 8);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--min-age=")) {
 -                              revs->min_age = atoi(arg + 10);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--before=")) {
 -                              revs->min_age = approxidate(arg + 9);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--until=")) {
 -                              revs->min_age = approxidate(arg + 8);
 -                              continue;
 -                      }
 +
                        if (!strcmp(arg, "--all")) {
                                handle_refs(revs, flags, for_each_ref);
                                continue;
                                handle_refs(revs, flags, for_each_remote_ref);
                                continue;
                        }
 -                      if (!strcmp(arg, "--first-parent")) {
 -                              revs->first_parent_only = 1;
 -                              continue;
 -                      }
                        if (!strcmp(arg, "--reflog")) {
                                handle_reflog(revs, flags);
                                continue;
                        }
 -                      if (!strcmp(arg, "-g") ||
 -                                      !strcmp(arg, "--walk-reflogs")) {
 -                              init_reflog_walk(&revs->reflog_info);
 -                              continue;
 -                      }
                        if (!strcmp(arg, "--not")) {
                                flags ^= UNINTERESTING;
                                continue;
                        }
 -                      if (!strcmp(arg, "--default")) {
 -                              if (++i >= argc)
 -                                      die("bad --default argument");
 -                              def = argv[i];
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--merge")) {
 -                              show_merge = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--topo-order")) {
 -                              revs->lifo = 1;
 -                              revs->topo_order = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--date-order")) {
 -                              revs->lifo = 0;
 -                              revs->topo_order = 1;
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--early-output")) {
 -                              int count = 100;
 -                              switch (arg[14]) {
 -                              case '=':
 -                                      count = atoi(arg+15);
 -                                      /* Fallthrough */
 -                              case 0:
 -                                      revs->topo_order = 1;
 -                                      revs->early_output = count;
 -                                      continue;
 -                              }
 -                      }
 -                      if (!strcmp(arg, "--parents")) {
 -                              revs->rewrite_parents = 1;
 -                              revs->print_parents = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--dense")) {
 -                              revs->dense = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--sparse")) {
 -                              revs->dense = 0;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--show-all")) {
 -                              revs->show_all = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--remove-empty")) {
 -                              revs->remove_empty_trees = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--no-merges")) {
 -                              revs->no_merges = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--boundary")) {
 -                              revs->boundary = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--left-right")) {
 -                              revs->left_right = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--cherry-pick")) {
 -                              revs->cherry_pick = 1;
 -                              revs->limited = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--objects")) {
 -                              revs->tag_objects = 1;
 -                              revs->tree_objects = 1;
 -                              revs->blob_objects = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--objects-edge")) {
 -                              revs->tag_objects = 1;
 -                              revs->tree_objects = 1;
 -                              revs->blob_objects = 1;
 -                              revs->edge_hint = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--unpacked")) {
 -                              revs->unpacked = 1;
 -                              free(revs->ignore_packed);
 -                              revs->ignore_packed = NULL;
 -                              revs->num_ignore_packed = 0;
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--unpacked=")) {
 -                              revs->unpacked = 1;
 -                              add_ignore_packed(revs, arg+11);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "-r")) {
 -                              revs->diff = 1;
 -                              DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "-t")) {
 -                              revs->diff = 1;
 -                              DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
 -                              DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "-m")) {
 -                              revs->ignore_merges = 0;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "-c")) {
 -                              revs->diff = 1;
 -                              revs->dense_combined_merges = 0;
 -                              revs->combine_merges = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--cc")) {
 -                              revs->diff = 1;
 -                              revs->dense_combined_merges = 1;
 -                              revs->combine_merges = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "-v")) {
 -                              revs->verbose_header = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--pretty")) {
 -                              revs->verbose_header = 1;
 -                              get_commit_format(arg+8, revs);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--pretty=")) {
 -                              revs->verbose_header = 1;
 -                              get_commit_format(arg+9, revs);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--graph")) {
 -                              revs->topo_order = 1;
 -                              revs->rewrite_parents = 1;
 -                              revs->graph = graph_init(revs);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--root")) {
 -                              revs->show_root_diff = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--no-commit-id")) {
 -                              revs->no_commit_id = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--always")) {
 -                              revs->always_show_header = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--no-abbrev")) {
 -                              revs->abbrev = 0;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--abbrev")) {
 -                              revs->abbrev = DEFAULT_ABBREV;
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--abbrev=")) {
 -                              revs->abbrev = strtoul(arg + 9, NULL, 10);
 -                              if (revs->abbrev < MINIMUM_ABBREV)
 -                                      revs->abbrev = MINIMUM_ABBREV;
 -                              else if (revs->abbrev > 40)
 -                                      revs->abbrev = 40;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--abbrev-commit")) {
 -                              revs->abbrev_commit = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--full-diff")) {
 -                              revs->diff = 1;
 -                              revs->full_diff = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--full-history")) {
 -                              revs->simplify_history = 0;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--relative-date")) {
 -                              revs->date_mode = DATE_RELATIVE;
 -                              continue;
 -                      }
 -                      if (!strncmp(arg, "--date=", 7)) {
 -                              revs->date_mode = parse_date_format(arg + 7);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--log-size")) {
 -                              revs->show_log_size = 1;
 -                              continue;
 -                      }
 -
 -                      /*
 -                       * Grepping the commit log
 -                       */
 -                      if (!prefixcmp(arg, "--author=")) {
 -                              add_header_grep(revs, "author", arg+9);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--committer=")) {
 -                              add_header_grep(revs, "committer", arg+12);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--grep=")) {
 -                              add_message_grep(revs, arg+7);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--extended-regexp") ||
 -                          !strcmp(arg, "-E")) {
 -                              regflags |= REG_EXTENDED;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--regexp-ignore-case") ||
 -                          !strcmp(arg, "-i")) {
 -                              regflags |= REG_ICASE;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--fixed-strings") ||
 -                          !strcmp(arg, "-F")) {
 -                              fixed = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--all-match")) {
 -                              all_match = 1;
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--encoding=")) {
 -                              arg += 11;
 -                              if (strcmp(arg, "none"))
 -                                      git_log_output_encoding = xstrdup(arg);
 -                              else
 -                                      git_log_output_encoding = "";
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--reverse")) {
 -                              revs->reverse ^= 1;
 -                              continue;
 -                      }
                        if (!strcmp(arg, "--no-walk")) {
                                revs->no_walk = 1;
                                continue;
                                continue;
                        }
  
 -                      opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
 +                      opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv);
                        if (opts > 0) {
                                i += opts - 1;
                                continue;
                        }
 -                      *unrecognized++ = arg;
 -                      left++;
 +                      if (opts < 0)
 +                              exit(128);
                        continue;
                }
  
                }
        }
  
 -      if (revs->grep_filter) {
 -              revs->grep_filter->regflags |= regflags;
 -              revs->grep_filter->fixed = fixed;
 -      }
 -
 -      if (show_merge)
 +      if (revs->def == NULL)
 +              revs->def = def;
 +      if (revs->show_merge)
                prepare_show_merge(revs);
 -      if (def && !revs->pending.nr) {
 +      if (revs->def && !revs->pending.nr) {
                unsigned char sha1[20];
                struct object *object;
                unsigned mode;
 -              if (get_sha1_with_mode(def, sha1, &mode))
 -                      die("bad default revision '%s'", def);
 -              object = get_reference(revs, def, sha1, 0);
 -              add_pending_object_with_mode(revs, object, def, mode);
 +              if (get_sha1_with_mode(revs->def, sha1, &mode))
 +                      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);
        }
  
        /* Did the user ask for any diff output? Run the diff! */
                die("diff_setup_done failed");
  
        if (revs->grep_filter) {
 -              revs->grep_filter->all_match = all_match;
                compile_grep_patterns(revs->grep_filter);
        }
  
        if (revs->reverse && revs->reflog_info)
                die("cannot combine --reverse with --walk-reflogs");
 +      if (revs->rewrite_parents && revs->children.name)
 +              die("cannot combine --parents and --children");
  
        /*
         * Limitations on the graph functionality
        return left;
  }
  
 +static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 +{
 +      struct commit_list *l = xcalloc(1, sizeof(*l));
 +
 +      l->item = child;
 +      l->next = add_decoration(&revs->children, &parent->object, l);
 +}
 +
 +static void set_children(struct rev_info *revs)
 +{
 +      struct commit_list *l;
 +      for (l = revs->commits; l; l = l->next) {
 +              struct commit *commit = l->item;
 +              struct commit_list *p;
 +
 +              for (p = commit->parents; p; p = p->next)
 +                      add_child(revs, p->item, commit);
 +      }
 +}
 +
  int prepare_revision_walk(struct rev_info *revs)
  {
        int nr = revs->pending.nr;
                        return -1;
        if (revs->topo_order)
                sort_in_topological_order(&revs->commits, revs->lifo);
 +      if (revs->children.name)
 +              set_children(revs);
        return 0;
  }
  
@@@ -1431,12 -1458,10 +1431,12 @@@ enum rewrite_result 
  
  static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
  {
 +      struct commit_list *cache = NULL;
 +
        for (;;) {
                struct commit *p = *pp;
                if (!revs->limited)
 -                      if (add_parents_to_list(revs, p, &revs->commits) < 0)
 +                      if (add_parents_to_list(revs, p, &revs->commits, &cache) < 0)
                                return rewrite_one_error;
                if (p->parents && p->parents->next)
                        return rewrite_one_ok;
@@@ -1499,11 -1524,6 +1499,11 @@@ static int commit_match(struct commit *
                           commit->buffer, strlen(commit->buffer));
  }
  
 +static inline int want_ancestry(struct rev_info *revs)
 +{
 +      return (revs->rewrite_parents || revs->children.name);
 +}
 +
  enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
  {
        if (commit->object.flags & SHOWN)
                /* Commit without changes? */
                if (commit->object.flags & TREESAME) {
                        /* drop merges unless we want parenthood */
 -                      if (!revs->rewrite_parents)
 +                      if (!want_ancestry(revs))
                                return commit_ignore;
                        /* non-merge - always ignore it */
                        if (!commit->parents || !commit->parents->next)
                                return commit_ignore;
                }
 -              if (revs->rewrite_parents && rewrite_parents(revs, commit) < 0)
 +              if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0)
                        return commit_error;
        }
        return commit_show;
@@@ -1560,7 -1580,7 +1560,7 @@@ static struct commit *get_revision_1(st
                        if (revs->max_age != -1 &&
                            (commit->date < revs->max_age))
                                continue;
 -                      if (add_parents_to_list(revs, commit, &revs->commits) < 0)
 +                      if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0)
                                return NULL;
                }
  
diff --combined t/t7600-merge.sh
index d4cf6289a4c06ec9436235b7325379f26a388e06,daf45b7ffa5bc80a4129a7f4b48dce36711d4c87..9ab5bdacc41d026ea81c65b0d95a61a61521429a
@@@ -221,37 -221,13 +221,13 @@@ test_expect_success 'setup' 
  
  test_debug 'gitk --all'
  
--test_expect_success 'test option parsing' '
-       if git merge -$ c1
-       then
-               echo "[OOPS] -$ accepted"
-               false
-       fi &&
-       if git merge --no-such c1
-       then
-               echo "[OOPS] --no-such accepted"
-               false
-       fi &&
-       if git merge -s foobar c1
-       then
-               echo "[OOPS] -s foobar accepted"
-               false
-       fi &&
-       if git merge -s=foobar c1
-       then
-               echo "[OOPS] -s=foobar accepted"
-               false
-       fi &&
-       if git merge -m
-       then
-               echo "[OOPS] missing commit msg accepted"
-               false
-       fi &&
-       if git merge
-       then
-               echo "[OOPS] missing commit references accepted"
-               false
-       fi
++test_expect_failure 'test option parsing' '
+       test_must_fail git merge -$ c1 &&
+       test_must_fail git merge --no-such c1 &&
+       test_must_fail git merge -s foobar c1 &&
+       test_must_fail git merge -s=foobar c1 &&
+       test_must_fail git merge -m &&
+       test_must_fail git merge
  '
  
  test_expect_success 'merge c0 with c1' '
@@@ -465,51 -441,11 +441,51 @@@ test_expect_success 'merge log message
        git merge --no-log c2 &&
        git show -s --pretty=format:%b HEAD >msg.act &&
        verify_diff msg.nolog msg.act "[OOPS] bad merge log message" &&
 +
        git merge --log c3 &&
        git show -s --pretty=format:%b HEAD >msg.act &&
 +      verify_diff msg.log msg.act "[OOPS] bad merge log message" &&
 +
 +      git reset --hard HEAD^ &&
 +      git config merge.log yes &&
 +      git merge c3 &&
 +      git show -s --pretty=format:%b HEAD >msg.act &&
        verify_diff msg.log msg.act "[OOPS] bad merge log message"
  '
  
  test_debug 'gitk --all'
  
 +test_expect_success 'merge c1 with c0, c2, c0, and c1' '
 +       git reset --hard c1 &&
 +       git config branch.master.mergeoptions "" &&
 +       test_tick &&
 +       git merge c0 c2 c0 c1 &&
 +       verify_merge file result.1-5 &&
 +       verify_parents $c1 $c2
 +'
 +
 +test_debug 'gitk --all'
 +
 +test_expect_success 'merge c1 with c0, c2, c0, and c1' '
 +       git reset --hard c1 &&
 +       git config branch.master.mergeoptions "" &&
 +       test_tick &&
 +       git merge c0 c2 c0 c1 &&
 +       verify_merge file result.1-5 &&
 +       verify_parents $c1 $c2
 +'
 +
 +test_debug 'gitk --all'
 +
 +test_expect_success 'merge c1 with c1 and c2' '
 +       git reset --hard c1 &&
 +       git config branch.master.mergeoptions "" &&
 +       test_tick &&
 +       git merge c1 c2 &&
 +       verify_merge file result.1-5 &&
 +       verify_parents $c1 $c2
 +'
 +
 +test_debug 'gitk --all'
 +
  test_done