From 8ee50594889056322f2fc00a589a36e83b9119fd Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Mon, 21 Mar 2011 11:14:05 +0100 Subject: [PATCH 1/4] t6009: use test_commit() from test-lib.sh Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- t/t6009-rev-list-parent.sh | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh index 52f7b277ce..0f0e457730 100755 --- a/t/t6009-rev-list-parent.sh +++ b/t/t6009-rev-list-parent.sh @@ -4,25 +4,18 @@ test_description='properly cull all ancestors' . ./test-lib.sh -commit () { - test_tick && - echo $1 >file && - git commit -a -m $1 && - git tag $1 -} - 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 ' From ad5aeeded3295589b2573b143f754762a56f8f82 Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Mon, 21 Mar 2011 11:14:06 +0100 Subject: [PATCH 2/4] revision.c: introduce --min-parents and --max-parents options Introduce --min-parents and --max-parents options which limit the revisions to those commits which have at least (or at most) that many commits, where negative arguments for --max-parents= denote infinity (i.e. no upper limit). In particular: --max-parents=1 is the same as --no-merges; --min-parents=2 is the same as --merges; --max-parents=0 shows only roots; and --min-parents=3 shows only octopus merges Using --min-parents=n and --max-parents=m with n>m gives you what you ask for (i.e. nothing) for obvious reasons, just like when you give --merges (show only merge commits) and --no-merges (show only non-merge commits) at the same time. Also, introduce --no-min-parents and --no-max-parents to do the obvious thing for convenience. We compute the number of parents only when we limit by that, so there is no performance impact when there are no limiters. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- builtin/log.c | 2 +- builtin/rev-list.c | 4 ++++ builtin/rev-parse.c | 4 ++++ revision.c | 28 +++++++++++++++++++++------- revision.h | 4 ++-- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index d8c6c28d2f..669796d7b2 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1055,7 +1055,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=\n" " --sparse\n" " --no-merges\n" +" --min-parents=\n" +" --no-min-parents\n" +" --max-parents=\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/revision.c b/revision.c index 626f6a23d1..8540a5d2f1 100644 --- a/revision.c +++ b/revision.c @@ -945,6 +945,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; @@ -1280,9 +1281,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")) { @@ -1301,7 +1310,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; @@ -2032,10 +2041,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 1c0abf0f58..8214a55348 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; From 6a6ebded7f2288d098f825f97cb2c210f0344497 Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Wed, 23 Mar 2011 10:38:51 +0100 Subject: [PATCH 3/4] rev-list --min-parents,--max-parents: doc, test and completion This also adds test for "--merges" and "--no-merges" which we did not have so far. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- Documentation/git-rev-list.txt | 4 + Documentation/rev-list-options.txt | 19 ++++- contrib/completion/git-completion.bash | 2 + t/t6009-rev-list-parent.sh | 107 ++++++++++++++++++++++++- 4 files changed, 129 insertions(+), 3 deletions(-) diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 8a891ca68d..b2be2029dd 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -16,6 +16,10 @@ SYNOPSIS [ \--sparse ] [ \--merges ] [ \--no-merges ] + [ \--min-parents= ] + [ \--no-min-parents ] + [ \--max-parents= ] + [ \--no-max-parents ] [ \--first-parent ] [ \--remove-empty ] [ \--full-history ] diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 95d209c11d..a02bb4ba5b 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -226,11 +226,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=:: +--max-parents=:: +--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/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 893b7716ca..628970a8ae 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1572,6 +1572,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/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh index 0f0e457730..fc89d6d6b4 100755 --- a/t/t6009-rev-list-parent.sh +++ b/t/t6009-rev-list-parent.sh @@ -1,9 +1,17 @@ #!/bin/sh -test_description='properly cull all ancestors' +test_description='ancestor culling and limiting by parent number' . ./test-lib.sh +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 ' touch file && @@ -28,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 'set up dodecapus' ' + + for i in 1 2 3 4 5 6 7 8 9 10 11 + do + git checkout -b root$i five || return + test_commit $i || return + done && + git checkout master && + test_tick && + git merge -m dodecapus root{1,2,3,4,5,6,7,8,9,10,11} && + git tag dodecapus +' + +test_expect_success 'test with 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 From 482ce70e14fdd5d03304a92f1740e6a9022e7958 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Thu, 24 Mar 2011 03:21:24 -0500 Subject: [PATCH 4/4] tests: avoid nonportable {foo,bar} glob Unlike bash and ksh, dash and busybox ash do not support brace expansion (as in 'echo {hello,world}'). So when dash is sh, t6009.13 (set up dodecapus) ends up pass a string beginning with "root{1,2," to "git merge" verbatim and the test fails. Fix it by introducing a variable to hold the list of parents for the dodecapus and populating it in a more low-tech way. While at it, simplify a little by combining this setup code with the test it sets up for. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/t6009-rev-list-parent.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh index fc89d6d6b4..30507407ff 100755 --- a/t/t6009-rev-list-parent.sh +++ b/t/t6009-rev-list-parent.sh @@ -112,20 +112,20 @@ test_expect_success 'rev-list override and infinities' ' check_revlist "--max-parents=0 --min-parents=1 --no-min-parents" one five ' -test_expect_success 'set up dodecapus' ' +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 || return - test_commit $i || return + git checkout -b root$i five && + test_commit $i && + roots="$roots root$i" || + return done && git checkout master && test_tick && - git merge -m dodecapus root{1,2,3,4,5,6,7,8,9,10,11} && - git tag dodecapus -' - -test_expect_success 'test with dodecapus' ' + git merge -m dodecapus $roots && + git tag dodecapus && check_revlist "--min-parents=4" dodecapus tetrapus && check_revlist "--min-parents=8" dodecapus &&