summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2011-03-26 20:13:17 -0700
committerJunio C Hamano <gitster@pobox.com>2011-03-26 20:13:17 -0700
commitbf0c5bbe2566d756d7468331161461ea9fec3b1f (patch)
tree3179922b7a98d866fcf86406ac62c5825cb37f63
parent8194fcaa1f0999064a170ed24d36be5432815dce (diff)
parent482ce70e14fdd5d03304a92f1740e6a9022e7958 (diff)
downloadgit-bf0c5bbe2566d756d7468331161461ea9fec3b1f.tar.gz
Merge branch 'mg/rev-list-n-parents'
* 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
-rw-r--r--Documentation/git-rev-list.txt4
-rw-r--r--Documentation/rev-list-options.txt19
-rw-r--r--builtin/log.c2
-rw-r--r--builtin/rev-list.c4
-rw-r--r--builtin/rev-parse.c4
-rwxr-xr-xcontrib/completion/git-completion.bash2
-rw-r--r--revision.c28
-rw-r--r--revision.h4
-rwxr-xr-xt/t6009-rev-list-parent.sh118
9 files changed, 163 insertions, 22 deletions
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index b08dfbc3c4..415f4f0b30 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -16,6 +16,10 @@ SYNOPSIS
[ \--sparse ]
[ \--merges ]
[ \--no-merges ]
+ [ \--min-parents=<number> ]
+ [ \--no-min-parents ]
+ [ \--max-parents=<number> ]
+ [ \--no-max-parents ]
[ \--first-parent ]
[ \--remove-empty ]
[ \--full-history ]
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 5c6850f048..ea5c6c49bd 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -72,11 +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
diff --git a/builtin/log.c b/builtin/log.c
index 796e9e5746..4a0f78dc71 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1061,7 +1061,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
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));
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index f458cb7587..9bfb94201f 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -16,6 +16,10 @@ static const char rev_list_usage[] =
" --min-age=<epoch>\n"
" --sparse\n"
" --no-merges\n"
+" --min-parents=<n>\n"
+" --no-min-parents\n"
+" --max-parents=<n>\n"
+" --no-max-parents\n"
" --remove-empty\n"
" --all\n"
" --branches\n"
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index a5a1c86e92..adb1cae4f2 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -48,6 +48,10 @@ static int is_rev_argument(const char *arg)
"--max-count=",
"--min-age=",
"--no-merges",
+ "--min-parents=",
+ "--no-min-parents",
+ "--max-parents=",
+ "--no-max-parents",
"--objects",
"--objects-edge",
"--parents",
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 1b589fadbb..840ae38760 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1577,6 +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 --git a/revision.c b/revision.c
index e96c281d1f..0f38364cf3 100644
--- a/revision.c
+++ b/revision.c
@@ -941,6 +941,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
revs->min_age = -1;
revs->skip_count = -1;
revs->max_count = -1;
+ revs->max_parents = -1;
revs->commit_format = CMIT_FMT_DEFAULT;
@@ -1277,9 +1278,17 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} 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")) {
@@ -1298,7 +1307,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
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;
@@ -2029,10 +2038,15 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
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 --git a/revision.h b/revision.h
index ae948601f9..9fd8f3016f 100644
--- a/revision.h
+++ b/revision.h
@@ -41,8 +41,6 @@ struct rev_info {
/* Traversal flags */
unsigned int dense:1,
prune:1,
- no_merges:1,
- merges_only:1,
no_walk:1,
show_all:1,
remove_empty_trees:1,
@@ -126,6 +124,8 @@ struct rev_info {
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;
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
index 52f7b277ce..30507407ff 100755
--- a/t/t6009-rev-list-parent.sh
+++ b/t/t6009-rev-list-parent.sh
@@ -1,14 +1,15 @@
#!/bin/sh
-test_description='properly cull all ancestors'
+test_description='ancestor culling and limiting by parent number'
. ./test-lib.sh
-commit () {
- test_tick &&
- echo $1 >file &&
- git commit -a -m $1 &&
- git tag $1
+check_revlist () {
+ rev_list_args="$1" &&
+ shift &&
+ git rev-parse "$@" >expect &&
+ git rev-list $rev_list_args --all >actual &&
+ test_cmp expect actual
}
test_expect_success setup '
@@ -16,13 +17,13 @@ test_expect_success setup '
touch file &&
git add file &&
- commit one &&
+ test_commit one &&
test_tick=$(($test_tick - 2400)) &&
- commit two &&
- commit three &&
- commit four &&
+ test_commit two &&
+ test_commit three &&
+ test_commit four &&
git log --pretty=oneline --abbrev-commit
'
@@ -35,4 +36,101 @@ test_expect_success 'one is ancestor of others and should not be shown' '
'
+test_expect_success 'setup roots, merges and octopuses' '
+
+ git checkout --orphan newroot &&
+ test_commit five &&
+ git checkout -b sidebranch two &&
+ test_commit six &&
+ git checkout -b anotherbranch three &&
+ test_commit seven &&
+ git checkout -b yetanotherbranch four &&
+ test_commit eight &&
+ git checkout master &&
+ test_merge normalmerge newroot &&
+ test_tick &&
+ git merge -m tripus sidebranch anotherbranch &&
+ git tag tripus &&
+ git checkout -b tetrabranch normalmerge &&
+ test_tick &&
+ git merge -m tetrapus sidebranch anotherbranch yetanotherbranch &&
+ git tag tetrapus &&
+ git checkout master
+'
+
+test_expect_success 'rev-list roots' '
+
+ check_revlist "--max-parents=0" one five
+'
+
+test_expect_success 'rev-list no merges' '
+
+ check_revlist "--max-parents=1" one eight seven six five four three two &&
+ check_revlist "--no-merges" one eight seven six five four three two
+'
+
+test_expect_success 'rev-list no octopuses' '
+
+ check_revlist "--max-parents=2" one normalmerge eight seven six five four three two
+'
+
+test_expect_success 'rev-list no roots' '
+
+ check_revlist "--min-parents=1" tetrapus tripus normalmerge eight seven six four three two
+'
+
+test_expect_success 'rev-list merges' '
+
+ check_revlist "--min-parents=2" tetrapus tripus normalmerge &&
+ check_revlist "--merges" tetrapus tripus normalmerge
+'
+
+test_expect_success 'rev-list octopus' '
+
+ check_revlist "--min-parents=3" tetrapus tripus
+'
+
+test_expect_success 'rev-list ordinary commits' '
+
+ check_revlist "--min-parents=1 --max-parents=1" eight seven six four three two
+'
+
+test_expect_success 'rev-list --merges --no-merges yields empty set' '
+
+ check_revlist "--min-parents=2 --no-merges" &&
+ check_revlist "--merges --no-merges" &&
+ check_revlist "--no-merges --merges"
+'
+
+test_expect_success 'rev-list override and infinities' '
+
+ check_revlist "--min-parents=2 --max-parents=1 --max-parents=3" tripus normalmerge &&
+ check_revlist "--min-parents=1 --min-parents=2 --max-parents=7" tetrapus tripus normalmerge &&
+ check_revlist "--min-parents=2 --max-parents=8" tetrapus tripus normalmerge &&
+ check_revlist "--min-parents=2 --max-parents=-1" tetrapus tripus normalmerge &&
+ check_revlist "--min-parents=2 --no-max-parents" tetrapus tripus normalmerge &&
+ check_revlist "--max-parents=0 --min-parents=1 --no-min-parents" one five
+'
+
+test_expect_success 'dodecapus' '
+
+ roots= &&
+ for i in 1 2 3 4 5 6 7 8 9 10 11
+ do
+ git checkout -b root$i five &&
+ test_commit $i &&
+ roots="$roots root$i" ||
+ return
+ done &&
+ git checkout master &&
+ test_tick &&
+ git merge -m dodecapus $roots &&
+ git tag dodecapus &&
+
+ check_revlist "--min-parents=4" dodecapus tetrapus &&
+ check_revlist "--min-parents=8" dodecapus &&
+ check_revlist "--min-parents=12" dodecapus &&
+ check_revlist "--min-parents=13" &&
+ check_revlist "--min-parents=4 --max-parents=11" tetrapus
+'
test_done