Code

Merge branch 'mg/rev-list-n-parents'
authorJunio C Hamano <gitster@pobox.com>
Sun, 27 Mar 2011 03:13:17 +0000 (20:13 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 27 Mar 2011 03:13:17 +0000 (20:13 -0700)
* mg/rev-list-n-parents:
  tests: avoid nonportable {foo,bar} glob
  rev-list --min-parents,--max-parents: doc, test and completion
  revision.c: introduce --min-parents and --max-parents options
  t6009: use test_commit() from test-lib.sh

1  2 
Documentation/git-rev-list.txt
Documentation/rev-list-options.txt
builtin/log.c
contrib/completion/git-completion.bash
revision.c
revision.h

index b08dfbc3c455a19b3257e7e921c2a1a7436ddf3e,b2be2029dd7c3012475f34e4ce6b3f9e33e94671..415f4f0b303da22e9eec19c1b9e24732a74ecf67
@@@ -16,6 -16,10 +16,10 @@@ SYNOPSI
             [ \--sparse ]
             [ \--merges ]
             [ \--no-merges ]
+            [ \--min-parents=<number> ]
+            [ \--no-min-parents ]
+            [ \--max-parents=<number> ]
+            [ \--no-max-parents ]
             [ \--first-parent ]
             [ \--remove-empty ]
             [ \--full-history ]
@@@ -108,6 -112,16 +112,6 @@@ include::rev-list-options.txt[
  
  include::pretty-formats.txt[]
  
 -
 -Author
 -------
 -Written by Linus Torvalds <torvalds@osdl.org>
 -
 -Documentation
 ---------------
 -Documentation by David Greaves, Junio C Hamano, Jonas Fonseca
 -and the git-list <git@vger.kernel.org>.
 -
  GIT
  ---
  Part of the linkgit:git[1] suite
index 5c6850f0486dbea0c515e2323bc6455d89897628,a02bb4ba5b77025fe8a72ec502cd4ace2a2b5d7e..ea5c6c49bd1b673aba48c057add1b5d15ae840a0
 -Commit Formatting
 -~~~~~~~~~~~~~~~~~
 -
 -ifdef::git-rev-list[]
 -Using these options, linkgit:git-rev-list[1] will act similar to the
 -more specialized family of commit log tools: linkgit:git-log[1],
 -linkgit:git-show[1], and linkgit:git-whatchanged[1]
 -endif::git-rev-list[]
 -
 -include::pretty-options.txt[]
 -
 ---relative-date::
 -
 -      Synonym for `--date=relative`.
 -
 ---date=(relative|local|default|iso|rfc|short|raw)::
 -
 -      Only takes effect for dates shown in human-readable format, such
 -      as when using "--pretty". `log.date` config variable sets a default
 -      value for log command's --date option.
 -+
 -`--date=relative` shows dates relative to the current time,
 -e.g. "2 hours ago".
 -+
 -`--date=local` shows timestamps in user's local timezone.
 -+
 -`--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format.
 -+
 -`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
 -format, often found in E-mail messages.
 -+
 -`--date=short` shows only date but not time, in `YYYY-MM-DD` format.
 -+
 -`--date=raw` shows the date in the internal raw git format `%s %z` format.
 -+
 -`--date=default` shows timestamps in the original timezone
 -(either committer's or author's).
 -
 -ifdef::git-rev-list[]
 ---header::
 -
 -      Print the contents of the commit in raw-format; each record is
 -      separated with a NUL character.
 -endif::git-rev-list[]
 -
 ---parents::
 -
 -      Print also the parents of the commit (in the form "commit parent...").
 -      Also enables parent rewriting, see 'History Simplification' below.
 -
 ---children::
 -
 -      Print also the children of the commit (in the form "commit child...").
 -      Also enables parent rewriting, see 'History Simplification' below.
 -
 -ifdef::git-rev-list[]
 ---timestamp::
 -      Print the raw commit timestamp.
 -endif::git-rev-list[]
 -
 ---left-right::
 -
 -      Mark which side of a symmetric diff a commit is reachable from.
 -      Commits from the left side are prefixed with `<` and those from
 -      the right with `>`.  If combined with `--boundary`, those
 -      commits are prefixed with `-`.
 -+
 -For example, if you have this topology:
 -+
 ------------------------------------------------------------------------
 -             y---b---b  branch B
 -            / \ /
 -           /   .
 -          /   / \
 -         o---x---a---a  branch A
 ------------------------------------------------------------------------
 -+
 -you would get an output like this:
 -+
 ------------------------------------------------------------------------
 -      $ git rev-list --left-right --boundary --pretty=oneline A...B
 -
 -      >bbbbbbb... 3rd on b
 -      >bbbbbbb... 2nd on b
 -      <aaaaaaa... 3rd on a
 -      <aaaaaaa... 2nd on a
 -      -yyyyyyy... 1st on b
 -      -xxxxxxx... 1st on a
 ------------------------------------------------------------------------
 -
 ---graph::
 -
 -      Draw a text-based graphical representation of the commit history
 -      on the left hand side of the output.  This may cause extra lines
 -      to be printed in between commits, in order for the graph history
 -      to be drawn properly.
 -+
 -This enables parent rewriting, see 'History Simplification' below.
 -+
 -This implies the '--topo-order' option by default, but the
 -'--date-order' option may also be specified.
 -
 -ifdef::git-rev-list[]
 ---count::
 -      Print a number stating how many commits would have been
 -      listed, and suppress all other output.  When used together
 -      with '--left-right', instead print the counts for left and
 -      right commits, separated by a tab.
 -endif::git-rev-list[]
 -
 -
 -ifndef::git-rev-list[]
 -Diff Formatting
 -~~~~~~~~~~~~~~~
 -
 -Below are listed options that control the formatting of diff output.
 -Some of them are specific to linkgit:git-rev-list[1], however other diff
 -options may be given. See linkgit:git-diff-files[1] for more options.
 -
 --c::
 -
 -      With this option, diff output for a merge commit
 -      shows the differences from each of the parents to the merge result
 -      simultaneously instead of showing pairwise diff between a parent
 -      and the result one at a time. Furthermore, it lists only files
 -      which were modified from all parents.
 -
 ---cc::
 -
 -      This flag implies the '-c' options 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.
 -
 --m::
 -
 -      This flag makes the merge commits show the full diff like
 -      regular commits; for each merge parent, a separate log entry
 -      and diff is generated. An exception is that only diff against
 -      the first parent is shown when '--first-parent' option is given;
 -      in that case, the output represents the changes the merge
 -      brought _into_ the then-current branch.
 -
 --r::
 -
 -      Show recursive diffs.
 -
 --t::
 -
 -      Show the tree objects in the diff output. This implies '-r'.
 -
 --s::
 -      Suppress diff output.
 -endif::git-rev-list[]
 -
  Commit Limiting
  ~~~~~~~~~~~~~~~
  
  Besides specifying a range of commits that should be listed using the
  special notations explained in the description, additional commit
 -limiting may be applied.
 +limiting may be applied. Note that they are applied before commit
 +ordering and formatting options, such as '--reverse'.
  
  --
  
  -n 'number'::
  --max-count=<number>::
  
 -      Limit the number of commits output.
 +      Limit the number of commits to output.
  
  --skip=<number>::
  
@@@ -72,11 -226,26 +72,26 @@@ endif::git-rev-list[
  
  --merges::
  
-       Print only merge commits.
+       Print only merge commits. This is exactly the same as `--min-parents=2`.
  
  --no-merges::
  
-       Do not print commits with more than one parent.
+       Do not print commits with more than one parent. This is
+       exactly the same as `--max-parents=1`.
+ --min-parents=<number>::
+ --max-parents=<number>::
+ --no-min-parents::
+ --no-max-parents::
+       Show only commits which have at least (or at most) that many
+       commits. In particular, `--max-parents=1` is the same as `--no-merges`,
+       `--min-parents=2` is the same as `--merges`.  `--max-parents=0`
+       gives all root commits and `--min-parents=3` all octopus merges.
+ +
+ `--no-min-parents` and `--no-max-parents` reset these limits (to no limit)
+ again.  Equivalent forms are `--min-parents=0` (any commit has 0 or more
+ parents) and `--max-parents=-1` (negative numbers denote no upper limit).
  
  --first-parent::
        Follow only the first parent commit upon seeing a merge
@@@ -607,158 -776,3 +622,158 @@@ These options are mostly targeted for p
  --do-walk::
  
        Overrides a previous --no-walk.
 +
 +Commit Formatting
 +~~~~~~~~~~~~~~~~~
 +
 +ifdef::git-rev-list[]
 +Using these options, linkgit:git-rev-list[1] will act similar to the
 +more specialized family of commit log tools: linkgit:git-log[1],
 +linkgit:git-show[1], and linkgit:git-whatchanged[1]
 +endif::git-rev-list[]
 +
 +include::pretty-options.txt[]
 +
 +--relative-date::
 +
 +      Synonym for `--date=relative`.
 +
 +--date=(relative|local|default|iso|rfc|short|raw)::
 +
 +      Only takes effect for dates shown in human-readable format, such
 +      as when using "--pretty". `log.date` config variable sets a default
 +      value for log command's --date option.
 ++
 +`--date=relative` shows dates relative to the current time,
 +e.g. "2 hours ago".
 ++
 +`--date=local` shows timestamps in user's local timezone.
 ++
 +`--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format.
 ++
 +`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
 +format, often found in E-mail messages.
 ++
 +`--date=short` shows only date but not time, in `YYYY-MM-DD` format.
 ++
 +`--date=raw` shows the date in the internal raw git format `%s %z` format.
 ++
 +`--date=default` shows timestamps in the original timezone
 +(either committer's or author's).
 +
 +ifdef::git-rev-list[]
 +--header::
 +
 +      Print the contents of the commit in raw-format; each record is
 +      separated with a NUL character.
 +endif::git-rev-list[]
 +
 +--parents::
 +
 +      Print also the parents of the commit (in the form "commit parent...").
 +      Also enables parent rewriting, see 'History Simplification' below.
 +
 +--children::
 +
 +      Print also the children of the commit (in the form "commit child...").
 +      Also enables parent rewriting, see 'History Simplification' below.
 +
 +ifdef::git-rev-list[]
 +--timestamp::
 +      Print the raw commit timestamp.
 +endif::git-rev-list[]
 +
 +--left-right::
 +
 +      Mark which side of a symmetric diff a commit is reachable from.
 +      Commits from the left side are prefixed with `<` and those from
 +      the right with `>`.  If combined with `--boundary`, those
 +      commits are prefixed with `-`.
 ++
 +For example, if you have this topology:
 ++
 +-----------------------------------------------------------------------
 +           y---b---b  branch B
 +          / \ /
 +         /   .
 +        /   / \
 +       o---x---a---a  branch A
 +-----------------------------------------------------------------------
 ++
 +you would get an output like this:
 ++
 +-----------------------------------------------------------------------
 +      $ git rev-list --left-right --boundary --pretty=oneline A...B
 +
 +      >bbbbbbb... 3rd on b
 +      >bbbbbbb... 2nd on b
 +      <aaaaaaa... 3rd on a
 +      <aaaaaaa... 2nd on a
 +      -yyyyyyy... 1st on b
 +      -xxxxxxx... 1st on a
 +-----------------------------------------------------------------------
 +
 +--graph::
 +
 +      Draw a text-based graphical representation of the commit history
 +      on the left hand side of the output.  This may cause extra lines
 +      to be printed in between commits, in order for the graph history
 +      to be drawn properly.
 ++
 +This enables parent rewriting, see 'History Simplification' below.
 ++
 +This implies the '--topo-order' option by default, but the
 +'--date-order' option may also be specified.
 +
 +ifdef::git-rev-list[]
 +--count::
 +      Print a number stating how many commits would have been
 +      listed, and suppress all other output.  When used together
 +      with '--left-right', instead print the counts for left and
 +      right commits, separated by a tab.
 +endif::git-rev-list[]
 +
 +
 +ifndef::git-rev-list[]
 +Diff Formatting
 +~~~~~~~~~~~~~~~
 +
 +Below are listed options that control the formatting of diff output.
 +Some of them are specific to linkgit:git-rev-list[1], however other diff
 +options may be given. See linkgit:git-diff-files[1] for more options.
 +
 +-c::
 +
 +      With this option, diff output for a merge commit
 +      shows the differences from each of the parents to the merge result
 +      simultaneously instead of showing pairwise diff between a parent
 +      and the result one at a time. Furthermore, it lists only files
 +      which were modified from all parents.
 +
 +--cc::
 +
 +      This flag implies the '-c' options 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.
 +
 +-m::
 +
 +      This flag makes the merge commits show the full diff like
 +      regular commits; for each merge parent, a separate log entry
 +      and diff is generated. An exception is that only diff against
 +      the first parent is shown when '--first-parent' option is given;
 +      in that case, the output represents the changes the merge
 +      brought _into_ the then-current branch.
 +
 +-r::
 +
 +      Show recursive diffs.
 +
 +-t::
 +
 +      Show the tree objects in the diff output. This implies '-r'.
 +
 +-s::
 +      Suppress diff output.
 +endif::git-rev-list[]
diff --combined builtin/log.c
index 796e9e57460468748d1e2af975ac52a6470a379d,669796d7b2909aa90abe04c3d161243a42f75fdd..4a0f78dc7139ac529e60e7e1099834ff7efe1163
@@@ -89,7 -89,7 +89,7 @@@ static void cmd_log_init(int argc, cons
                rev->always_show_header = 0;
        if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
                rev->always_show_header = 0;
 -              if (rev->diffopt.nr_paths != 1)
 +              if (rev->diffopt.pathspec.nr != 1)
                        usage("git logs can only follow renames on one pathname at a time");
        }
        for (i = 1; i < argc; i++) {
@@@ -263,13 -263,7 +263,13 @@@ static int cmd_log_walk(struct rev_inf
         * retain that state information if replacing rev->diffopt in this loop
         */
        while ((commit = get_revision(rev)) != NULL) {
 -              log_tree_commit(rev, commit);
 +              if (!log_tree_commit(rev, commit) &&
 +                  rev->max_count >= 0)
 +                      /*
 +                       * We decremented max_count in get_revision,
 +                       * but we didn't actually show the commit.
 +                       */
 +                      rev->max_count++;
                if (!rev->reflog_info) {
                        /* we allow cycles in reflog ancestry */
                        free(commit->buffer);
@@@ -1061,7 -1055,7 +1061,7 @@@ int cmd_format_patch(int argc, const ch
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.verbose_header = 1;
        rev.diff = 1;
-       rev.no_merges = 1;
+       rev.max_parents = 1;
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
        rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
@@@ -1358,23 -1352,6 +1358,23 @@@ static const char * const cherry_usage[
        NULL
  };
  
 +static void print_commit(char sign, struct commit *commit, int verbose,
 +                       int abbrev)
 +{
 +      if (!verbose) {
 +              printf("%c %s\n", sign,
 +                     find_unique_abbrev(commit->object.sha1, abbrev));
 +      } else {
 +              struct strbuf buf = STRBUF_INIT;
 +              struct pretty_print_context ctx = {0};
 +              pretty_print_commit(CMIT_FMT_ONELINE, commit, &buf, &ctx);
 +              printf("%c %s %s\n", sign,
 +                     find_unique_abbrev(commit->object.sha1, abbrev),
 +                     buf.buf);
 +              strbuf_release(&buf);
 +      }
 +}
 +
  int cmd_cherry(int argc, const char **argv, const char *prefix)
  {
        struct rev_info revs;
                commit = list->item;
                if (has_commit_patch_id(commit, &ids))
                        sign = '-';
 -
 -              if (verbose) {
 -                      struct strbuf buf = STRBUF_INIT;
 -                      struct pretty_print_context ctx = {0};
 -                      pretty_print_commit(CMIT_FMT_ONELINE, commit,
 -                                          &buf, &ctx);
 -                      printf("%c %s %s\n", sign,
 -                             find_unique_abbrev(commit->object.sha1, abbrev),
 -                             buf.buf);
 -                      strbuf_release(&buf);
 -              }
 -              else {
 -                      printf("%c %s\n", sign,
 -                             find_unique_abbrev(commit->object.sha1, abbrev));
 -              }
 -
 +              print_commit(sign, commit, verbose, abbrev);
                list = list->next;
        }
  
index 1b589fadbb5dd94827cf1565bce232d0f3a3c42a,628970a8aee287d19c1ececa53002445175a64dd..840ae38760a5de84b369b208a52cc579ce478378
@@@ -246,8 -246,6 +246,8 @@@ __git_ps1 (
                                fi
                        elif [ -f "$g/MERGE_HEAD" ]; then
                                r="|MERGING"
 +                      elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
 +                              r="|CHERRY-PICKING"
                        elif [ -f "$g/BISECT_LOG" ]; then
                                r="|BISECTING"
                        fi
@@@ -664,14 -662,11 +664,14 @@@ __git_compute_merge_strategies (
        : ${__git_merge_strategies:=$(__git_list_merge_strategies)}
  }
  
 -__git_complete_file ()
 +__git_complete_revlist_file ()
  {
        local pfx ls ref cur
        _get_comp_words_by_ref -n =: cur
        case "$cur" in
 +      *..?*:*)
 +              return
 +              ;;
        ?*:*)
                ref="${cur%%:*}"
                cur="${cur#*:}"
                *)
                        ls="$ref"
                        ;;
 -          esac
 +              esac
  
                case "$COMP_WORDBREAKS" in
                *:*) : great ;;
                                       s/^.*    //')" \
                        -- "$cur"))
                ;;
 -      *)
 -              __gitcomp "$(__git_refs)"
 -              ;;
 -      esac
 -}
 -
 -__git_complete_revlist ()
 -{
 -      local pfx cur
 -      _get_comp_words_by_ref -n =: cur
 -      case "$cur" in
        *...*)
                pfx="${cur%...*}..."
                cur="${cur#*...}"
        esac
  }
  
 +
 +__git_complete_file ()
 +{
 +      __git_complete_revlist_file
 +}
 +
 +__git_complete_revlist ()
 +{
 +      __git_complete_revlist_file
 +}
 +
  __git_complete_remote_or_refspec ()
  {
        local cur words cword
@@@ -1359,11 -1354,11 +1359,11 @@@ _git_diff (
                return
                ;;
        esac
 -      __git_complete_file
 +      __git_complete_revlist_file
  }
  
  __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
 -                      tkdiff vimdiff gvimdiff xxdiff araxis p4merge
 +                      tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3
  "
  
  _git_difftool ()
@@@ -1511,7 -1506,7 +1511,7 @@@ _git_help (
                ;;
        esac
        __git_compute_all_commands
 -      __gitcomp "$__git_all_commands
 +      __gitcomp "$__git_all_commands $(__git_aliases)
                attributes cli core-tutorial cvs-migration
                diffcore gitk glossary hooks ignore modules
                repository-layout tutorial tutorial-2
@@@ -1577,6 -1572,8 +1577,8 @@@ __git_log_common_options=
        --max-count=
        --max-age= --since= --after=
        --min-age= --until= --before=
+       --min-parents= --max-parents=
+       --no-min-parents --no-max-parents
  "
  # Options that go well for log and gitk (not shortlog)
  __git_log_gitk_options="
diff --combined revision.c
index e96c281d1f04f37e519e9447e18e43b86cb2363a,8540a5d2f104d6f26113406be58fa380ec612519..0f38364cf3f81a9088305ca802adf8b7c9260e91
@@@ -323,7 -323,7 +323,7 @@@ static int rev_compare_tree(struct rev_
                 * tagged commit by specifying both --simplify-by-decoration
                 * and pathspec.
                 */
 -              if (!revs->prune_data)
 +              if (!revs->prune_data.nr)
                        return REV_TREE_SAME;
        }
  
@@@ -554,7 -554,11 +554,7 @@@ static void cherry_pick_list(struct com
  
        left_first = left_count < right_count;
        init_patch_ids(&ids);
 -      if (revs->diffopt.nr_paths) {
 -              ids.diffopts.nr_paths = revs->diffopt.nr_paths;
 -              ids.diffopts.paths = revs->diffopt.paths;
 -              ids.diffopts.pathlens = revs->diffopt.pathlens;
 -      }
 +      ids.diffopts.pathspec = revs->diffopt.pathspec;
  
        /* Compute patch-ids for one side */
        for (p = list; p; p = p->next) {
@@@ -941,6 -945,7 +941,7 @@@ void init_revisions(struct rev_info *re
        revs->min_age = -1;
        revs->skip_count = -1;
        revs->max_count = -1;
+       revs->max_parents = -1;
  
        revs->commit_format = CMIT_FMT_DEFAULT;
  
@@@ -993,7 -998,7 +994,7 @@@ static void prepare_show_merge(struct r
                struct cache_entry *ce = active_cache[i];
                if (!ce_stage(ce))
                        continue;
 -              if (ce_path_match(ce, revs->prune_data)) {
 +              if (ce_path_match(ce, &revs->prune_data)) {
                        prune_num++;
                        prune = xrealloc(prune, sizeof(*prune) * prune_num);
                        prune[prune_num-2] = ce->name;
                       ce_same_name(ce, active_cache[i+1]))
                        i++;
        }
 -      revs->prune_data = prune;
 +      free_pathspec(&revs->prune_data);
 +      init_pathspec(&revs->prune_data, prune);
        revs->limited = 1;
  }
  
@@@ -1277,9 -1281,17 +1278,17 @@@ static int handle_revision_opt(struct r
        } else if (!strcmp(arg, "--remove-empty")) {
                revs->remove_empty_trees = 1;
        } else if (!strcmp(arg, "--merges")) {
-               revs->merges_only = 1;
+               revs->min_parents = 2;
        } else if (!strcmp(arg, "--no-merges")) {
-               revs->no_merges = 1;
+               revs->max_parents = 1;
+       } else if (!prefixcmp(arg, "--min-parents=")) {
+               revs->min_parents = atoi(arg+14);
+       } else if (!prefixcmp(arg, "--no-min-parents")) {
+               revs->min_parents = 0;
+       } else if (!prefixcmp(arg, "--max-parents=")) {
+               revs->max_parents = atoi(arg+14);
+       } else if (!prefixcmp(arg, "--no-max-parents")) {
+               revs->max_parents = -1;
        } else if (!strcmp(arg, "--boundary")) {
                revs->boundary = 1;
        } else if (!strcmp(arg, "--left-right")) {
                        die("--cherry is incompatible with --left-only");
                revs->cherry_mark = 1;
                revs->right_only = 1;
-               revs->no_merges = 1;
+               revs->max_parents = 1;
                revs->limited = 1;
        } else if (!strcmp(arg, "--count")) {
                revs->count = 1;
@@@ -1664,7 -1676,7 +1673,7 @@@ int setup_revisions(int argc, const cha
        }
  
        if (prune_data)
 -              revs->prune_data = get_pathspec(revs->prefix, prune_data);
 +              init_pathspec(&revs->prune_data, get_pathspec(revs->prefix, prune_data));
  
        if (revs->def == NULL)
                revs->def = opt ? opt->def : NULL;
        if (revs->topo_order)
                revs->limited = 1;
  
 -      if (revs->prune_data) {
 -              diff_tree_setup_paths(revs->prune_data, &revs->pruning);
 +      if (revs->prune_data.nr) {
 +              diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning);
                /* Can't prune commits with rename following: the paths change.. */
                if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
                        revs->prune = 1;
                if (!revs->full_diff)
 -                      diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
 +                      diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt);
        }
        if (revs->combine_merges)
                revs->ignore_merges = 0;
@@@ -2029,10 -2041,15 +2038,15 @@@ enum commit_action get_commit_action(st
                return commit_ignore;
        if (revs->min_age != -1 && (commit->date > revs->min_age))
                return commit_ignore;
-       if (revs->no_merges && commit->parents && commit->parents->next)
-               return commit_ignore;
-       if (revs->merges_only && !(commit->parents && commit->parents->next))
-               return commit_ignore;
+       if (revs->min_parents || (revs->max_parents >= 0)) {
+               int n = 0;
+               struct commit_list *p;
+               for (p = commit->parents; p; p = p->next)
+                       n++;
+               if ((n < revs->min_parents) ||
+                   ((revs->max_parents >= 0) && (n > revs->max_parents)))
+                       return commit_ignore;
+       }
        if (!commit_match(commit, revs))
                return commit_ignore;
        if (revs->prune && revs->dense) {
diff --combined revision.h
index ae948601f94c726c3677f23517dba5c046431904,8214a55348d1311dd3325b843ae572930e582078..9fd8f3016fe8f935f176f57c615f72df8ba8d157
@@@ -35,14 -35,12 +35,12 @@@ struct rev_info 
        /* Basic information */
        const char *prefix;
        const char *def;
 -      void *prune_data;
 +      struct pathspec prune_data;
        unsigned int early_output;
  
        /* Traversal flags */
        unsigned int    dense:1,
                        prune:1,
-                       no_merges:1,
-                       merges_only:1,
                        no_walk:1,
                        show_all:1,
                        remove_empty_trees:1,
        int max_count;
        unsigned long max_age;
        unsigned long min_age;
+       int min_parents;
+       int max_parents;
  
        /* diff info for patches and for paths limiting */
        struct diff_options diffopt;