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
This commit is contained in:
Junio C Hamano 2011-03-26 20:13:17 -07:00
commit bf0c5bbe25
9 changed files with 163 additions and 22 deletions

View file

@ -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 ]

View file

@ -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

View file

@ -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));

View file

@ -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"

View file

@ -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",

View file

@ -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="

View file

@ -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) {

View file

@ -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;

View file

@ -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