From b81f925f709a10b37fb4fcd03629b55bc890a478 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 01:52:48 -0500 Subject: [PATCH 01/24] merge: do not mistake (ancestor of) tag for branch If no branch 'foo' exists but a tag 'foo' does, then git merge foo^ results in Merge branch 'foo' (early part) as a commit message, because the relevant code path checks that refs/heads/foo is a valid refname for writing rather than for reading. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- builtin/merge.c | 2 +- t/t7600-merge.sh | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/builtin/merge.c b/builtin/merge.c index 37ce4f589f..2207f79969 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -437,7 +437,7 @@ static void merge_name(const char *remote, struct strbuf *msg) strbuf_addstr(&truname, "refs/heads/"); strbuf_addstr(&truname, remote); strbuf_setlen(&truname, truname.len - len); - if (resolve_ref(truname.buf, buf_sha, 0, NULL)) { + if (resolve_ref(truname.buf, buf_sha, 1, NULL)) { strbuf_addf(msg, "%s\t\tbranch '%s'%s of .\n", sha1_to_hex(remote_head->sha1), diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index cde8390c1b..16e5ba1d8c 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -558,8 +558,11 @@ test_expect_success 'refresh the index before merging' ' git merge c3 ' -cat >expected <expected.branch <<\EOF +Merge branch 'c5-branch' (early part) +EOF +cat >expected.tag <<\EOF +Merge commit 'c5~1' EOF test_expect_success 'merge early part of c2' ' @@ -577,9 +580,14 @@ test_expect_success 'merge early part of c2' ' git add c6.c && git commit -m c6 && git tag c6 && + git branch -f c5-branch c5 && + git merge c5-branch~1 && + git show -s --pretty=format:%s HEAD >actual.branch && + git reset --keep HEAD^ && git merge c5~1 && - git show -s --pretty=format:%s HEAD > actual && - test_cmp actual expected + git show -s --pretty=format:%s HEAD >actual.tag && + test_cmp expected.branch actual.branch && + test_cmp expected.tag actual.tag ' test_debug 'gitk --all' From 4c0734578f3eee3fe6282014257edfcd7084174b Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 01:53:56 -0500 Subject: [PATCH 02/24] t7600 (merge): modernize style Guard setup commands with test_expect_success, so they are easier to visually skip over and get to the good part. While at it: - use "printf '%s\n' a b ..." instead of "cat < Cc: Matthieu Moy Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/t7600-merge.sh | 282 ++++++++++++++++------------------------------- 1 file changed, 98 insertions(+), 184 deletions(-) diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 16e5ba1d8c..48a91ba3ca 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -5,189 +5,103 @@ test_description='git merge -Testing basic merge operations/option parsing.' +Testing basic merge operations/option parsing. + +! [c0] commit 0 + ! [c1] commit 1 + ! [c2] commit 2 + ! [c3] commit 3 + ! [c4] c4 + ! [c5] c5 + ! [c6] c6 + * [master] Merge commit 'c1' +-------- + - [master] Merge commit 'c1' + + * [c1] commit 1 + + [c6] c6 + + [c5] c5 + ++ [c4] c4 + ++++ [c3] commit 3 + + [c2] commit 2 ++++++++* [c0] commit 0 +' . ./test-lib.sh -cat >file <file && + printf "%s\n" "1 X" 2 3 4 5 6 7 8 9 >file.1 && + printf "%s\n" 1 2 3 4 "5 X" 6 7 8 9 >file.5 && + printf "%s\n" 1 2 3 4 5 6 7 8 "9 X" >file.9 && + printf "%s\n" "1 X" 2 3 4 5 6 7 8 9 >result.1 && + printf "%s\n" "1 X" 2 3 4 "5 X" 6 7 8 9 >result.1-5 && + printf "%s\n" "1 X" 2 3 4 "5 X" 6 7 8 "9 X" >result.1-5-9 && -cat >file.1 <msg.1-5 && + echo "Merge commit '\''c2'\''; commit '\''c3'\''" >msg.1-5-9 && + { + echo "Squashed commit of the following:" && + echo && + git log --no-merges ^HEAD c1 + } >squash.1 && + { + echo "Squashed commit of the following:" && + echo && + git log --no-merges ^HEAD c2 + } >squash.1-5 && + { + echo "Squashed commit of the following:" && + echo && + git log --no-merges ^HEAD c2 c3 + } >squash.1-5-9 && + echo >msg.nolog && + { + echo "* commit '\''c3'\'':" && + echo " commit 3" && + echo + } >msg.log + } && -cat >file.5 <file.9 <result.1 <result.1-5 <result.1-5-9 <msg.1-5 && - echo "Merge commit 'c2'; commit 'c3'" >msg.1-5-9 && - echo "Squashed commit of the following:" >squash.1 && - echo >>squash.1 && - git log --no-merges ^HEAD c1 >>squash.1 && - echo "Squashed commit of the following:" >squash.1-5 && - echo >>squash.1-5 && - git log --no-merges ^HEAD c2 >>squash.1-5 && - echo "Squashed commit of the following:" >squash.1-5-9 && - echo >>squash.1-5-9 && - git log --no-merges ^HEAD c2 c3 >>squash.1-5-9 && - echo > msg.nolog && - echo "* commit 'c3':" >msg.log && - echo " commit 3" >>msg.log && - echo >>msg.log -} - -verify_diff() { - if ! test_cmp "$1" "$2" - then - echo "$3" - false - fi -} - -verify_merge() { - verify_diff "$2" "$1" "[OOPS] bad merge result" && - if test $(git ls-files -u | wc -l) -gt 0 - then - echo "[OOPS] unmerged files" - false - fi && - if test_must_fail git diff --exit-code - then - echo "[OOPS] working tree != index" - false - fi && - if test -n "$3" - then - git show -s --pretty=format:%s HEAD >msg.act && - verify_diff "$3" msg.act "[OOPS] bad merge message" - fi -} - -verify_head() { - if test "$1" != "$(git rev-parse HEAD)" - then - echo "[OOPS] HEAD != $1" - false - fi -} - -verify_parents() { - i=1 - while test $# -gt 0 - do - if test "$1" != "$(git rev-parse HEAD^$i)" + verify_merge() { + test_cmp "$2" "$1" && + git update-index --refresh && + git diff --exit-code && + if test -n "$3" then - echo "[OOPS] HEAD^$i != $1" - return 1 + git show -s --pretty=format:%s HEAD >msg.act && + test_cmp "$3" msg.act fi - i=$(expr $i + 1) - shift - done -} + } && -verify_mergeheads() { - i=1 - if ! test -f .git/MERGE_HEAD - then - echo "[OOPS] MERGE_HEAD is missing" - false - fi && - while test $# -gt 0 - do - head=$(head -n $i .git/MERGE_HEAD | sed -ne \$p) - if test "$1" != "$head" - then - echo "[OOPS] MERGE_HEAD $i != $1" + verify_head() { + echo "$1" >head.expected && + git rev-parse HEAD >head.actual && + test_cmp head.expected head.actual + } && + + verify_parents() { + printf "%s\n" "$@" >parents.expected && + >parents.actual && + i=1 && + while test $i -le $# + do + git rev-parse HEAD^$i >>parents.actual && + i=$(expr $i + 1) || return 1 - fi - i=$(expr $i + 1) - shift - done -} + done && + test_cmp parents.expected parents.actual + } && -verify_no_mergehead() { - if test -f .git/MERGE_HEAD - then - echo "[OOPS] MERGE_HEAD exists" - false - fi -} + verify_mergeheads() { + printf "%s\n" "$@" >mergehead.expected && + test_cmp mergehead.expected .git/MERGE_HEAD + } && + verify_no_mergehead() { + ! test -e .git/MERGE_HEAD + } +' test_expect_success 'setup' ' git add file && @@ -316,7 +230,7 @@ test_expect_success 'merge c0 with c1 (squash)' ' verify_merge file result.1 && verify_head $c0 && verify_no_mergehead && - verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message" + test_cmp squash.1 .git/SQUASH_MSG ' test_debug 'gitk --all' @@ -327,7 +241,7 @@ test_expect_success 'merge c0 with c1 (squash, ff-only)' ' verify_merge file result.1 && verify_head $c0 && verify_no_mergehead && - verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message" + test_cmp squash.1 .git/SQUASH_MSG ' test_debug 'gitk --all' @@ -338,7 +252,7 @@ test_expect_success 'merge c1 with c2 (squash)' ' verify_merge file result.1-5 && verify_head $c1 && verify_no_mergehead && - verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message" + test_cmp squash.1-5 .git/SQUASH_MSG ' test_debug 'gitk --all' @@ -356,7 +270,7 @@ test_expect_success 'merge c1 with c2 and c3 (squash)' ' verify_merge file result.1-5-9 && verify_head $c1 && verify_no_mergehead && - verify_diff squash.1-5-9 .git/SQUASH_MSG "[OOPS] bad squash message" + test_cmp squash.1-5-9 .git/SQUASH_MSG ' test_debug 'gitk --all' @@ -379,7 +293,7 @@ test_expect_success 'merge c1 with c2 (squash in config)' ' verify_merge file result.1-5 && verify_head $c1 && verify_no_mergehead && - verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message" + test_cmp squash.1-5 .git/SQUASH_MSG ' test_debug 'gitk --all' @@ -485,17 +399,17 @@ test_expect_success 'merge log message' ' git reset --hard c0 && 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" && + test_cmp msg.nolog msg.act && git merge --log c3 && git show -s --pretty=format:%b HEAD >msg.act && - verify_diff msg.log msg.act "[OOPS] bad merge log message" && + test_cmp msg.log msg.act && 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_cmp msg.log msg.act ' test_debug 'gitk --all' @@ -545,7 +459,7 @@ test_debug 'gitk --all' test_expect_success 'in-index merge' ' git reset --hard c0 && - git merge --no-ff -s resolve c1 > out && + git merge --no-ff -s resolve c1 >out && grep "Wonderful." out && verify_parents $c0 $c1 ' @@ -567,16 +481,16 @@ EOF test_expect_success 'merge early part of c2' ' git reset --hard c3 && - echo c4 > c4.c && + echo c4 >c4.c && git add c4.c && git commit -m c4 && git tag c4 && - echo c5 > c5.c && + echo c5 >c5.c && git add c5.c && git commit -m c5 && git tag c5 && git reset --hard c3 && - echo c6 > c6.c && + echo c6 >c6.c && git add c6.c && git commit -m c6 && git tag c6 && From df516fb55891ae57676515d437d0e2cb3c8ad429 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 01:54:59 -0500 Subject: [PATCH 03/24] t7600 (merge): do not launch gitk for --debug Probably as a development aid, this test script runs gitk --all to allow the driver to inspect history between tests when run with --debug. As a result, running all tests with --debug requires closing a long series of gitk displays, one at a time. Use git log --graph --oneline instead. This way, the history is available for viewing with "git show" but the test script finishes without interaction. Longer term, it would be nice to have an option to run a user-specified command between tests. This patch does not do that. Cc: Lars Hjemli Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/t7600-merge.sh | 58 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 48a91ba3ca..64a4265cfc 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -133,7 +133,7 @@ test_expect_success 'setup' ' create_merge_msgs ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'test option parsing' ' test_must_fail git merge -$ c1 && @@ -155,7 +155,7 @@ test_expect_success 'merge c0 with c1' ' verify_head "$c1" ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c0 with c1 with --ff-only' ' git reset --hard c0 && @@ -165,7 +165,7 @@ test_expect_success 'merge c0 with c1 with --ff-only' ' verify_head "$c1" ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2' ' git reset --hard c1 && @@ -175,7 +175,7 @@ test_expect_success 'merge c1 with c2' ' verify_parents $c1 $c2 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 and c3' ' git reset --hard c1 && @@ -185,7 +185,7 @@ test_expect_success 'merge c1 with c2 and c3' ' verify_parents $c1 $c2 $c3 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'failing merges with --ff-only' ' git reset --hard c1 && @@ -202,7 +202,7 @@ test_expect_success 'merge c0 with c1 (no-commit)' ' verify_head $c1 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 (no-commit)' ' git reset --hard c1 && @@ -212,7 +212,7 @@ test_expect_success 'merge c1 with c2 (no-commit)' ' verify_mergeheads $c2 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 and c3 (no-commit)' ' git reset --hard c1 && @@ -222,7 +222,7 @@ test_expect_success 'merge c1 with c2 and c3 (no-commit)' ' verify_mergeheads $c2 $c3 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c0 with c1 (squash)' ' git reset --hard c0 && @@ -233,7 +233,7 @@ test_expect_success 'merge c0 with c1 (squash)' ' test_cmp squash.1 .git/SQUASH_MSG ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c0 with c1 (squash, ff-only)' ' git reset --hard c0 && @@ -244,7 +244,7 @@ test_expect_success 'merge c0 with c1 (squash, ff-only)' ' test_cmp squash.1 .git/SQUASH_MSG ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 (squash)' ' git reset --hard c1 && @@ -255,14 +255,14 @@ test_expect_success 'merge c1 with c2 (squash)' ' test_cmp squash.1-5 .git/SQUASH_MSG ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' ' git reset --hard c1 && test_must_fail git merge --squash --ff-only c2 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 and c3 (squash)' ' git reset --hard c1 && @@ -273,7 +273,7 @@ test_expect_success 'merge c1 with c2 and c3 (squash)' ' test_cmp squash.1-5-9 .git/SQUASH_MSG ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 (no-commit in config)' ' git reset --hard c1 && @@ -284,7 +284,7 @@ test_expect_success 'merge c1 with c2 (no-commit in config)' ' verify_mergeheads $c2 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 (squash in config)' ' git reset --hard c1 && @@ -296,7 +296,7 @@ test_expect_success 'merge c1 with c2 (squash in config)' ' test_cmp squash.1-5 .git/SQUASH_MSG ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'override config option -n with --summary' ' git reset --hard c1 && @@ -326,7 +326,7 @@ test_expect_success 'override config option -n with --stat' ' fi ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'override config option --stat' ' git reset --hard c1 && @@ -342,7 +342,7 @@ test_expect_success 'override config option --stat' ' fi ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 (override --no-commit)' ' git reset --hard c1 && @@ -353,7 +353,7 @@ test_expect_success 'merge c1 with c2 (override --no-commit)' ' verify_parents $c1 $c2 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 (override --squash)' ' git reset --hard c1 && @@ -364,7 +364,7 @@ test_expect_success 'merge c1 with c2 (override --squash)' ' verify_parents $c1 $c2 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c0 with c1 (no-ff)' ' git reset --hard c0 && @@ -375,7 +375,7 @@ test_expect_success 'merge c0 with c1 (no-ff)' ' verify_parents $c0 $c1 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'combining --squash and --no-ff is refused' ' test_must_fail git merge --squash --no-ff c1 && @@ -412,7 +412,7 @@ test_expect_success 'merge log message' ' test_cmp msg.log msg.act ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c0, c2, c0, and c1' ' git reset --hard c1 && @@ -423,7 +423,7 @@ test_expect_success 'merge c1 with c0, c2, c0, and c1' ' verify_parents $c1 $c2 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c0, c2, c0, and c1' ' git reset --hard c1 && @@ -434,7 +434,7 @@ test_expect_success 'merge c1 with c0, c2, c0, and c1' ' verify_parents $c1 $c2 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c1 and c2' ' git reset --hard c1 && @@ -445,7 +445,7 @@ test_expect_success 'merge c1 with c1 and c2' ' verify_parents $c1 $c2 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge fast-forward in a dirty tree' ' git reset --hard c0 && @@ -455,7 +455,7 @@ test_expect_success 'merge fast-forward in a dirty tree' ' git merge c2 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'in-index merge' ' git reset --hard c0 && @@ -464,7 +464,7 @@ test_expect_success 'in-index merge' ' verify_parents $c0 $c1 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'refresh the index before merging' ' git reset --hard c1 && @@ -504,7 +504,7 @@ test_expect_success 'merge early part of c2' ' test_cmp expected.tag actual.tag ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge --no-ff --no-commit && commit' ' git reset --hard c0 && @@ -513,13 +513,13 @@ test_expect_success 'merge --no-ff --no-commit && commit' ' verify_parents $c0 $c1 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'amending no-ff merge commit' ' EDITOR=: git commit --amend && verify_parents $c0 $c1 ' -test_debug 'gitk --all' +test_debug 'git log --graph --decorate --oneline --all' test_done From ff372c7851f36bfe0e113e23d247cb9033e79559 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 01:56:13 -0500 Subject: [PATCH 04/24] t7600 (merge): check reflog entry The details of the reflog message are not important, but including something sane in the reflog is. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/t7600-merge.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 64a4265cfc..6e839a609c 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -149,10 +149,16 @@ test_expect_success 'reject non-strategy with a git-merge-foo name' ' ' test_expect_success 'merge c0 with c1' ' + echo "OBJID HEAD@{0}: merge c1: Fast-forward" >reflog.expected && + git reset --hard c0 && git merge c1 && verify_merge file result.1 && - verify_head "$c1" + verify_head "$c1" && + + git reflog -1 >reflog.actual && + sed "s/$_x05[0-9a-f]*/OBJID/g" reflog.actual >reflog.fuzzy && + test_cmp reflog.expected reflog.fuzzy ' test_debug 'git log --graph --decorate --oneline --all' From 94d63ce2abfd8de9dc3c928383270ec6ca3605bb Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 01:56:46 -0500 Subject: [PATCH 05/24] t7600 (merge): test merge from branch yet to be born Some people like to "git fetch origin && merge origin/master" from the unborn branch provided when first initializing a repository. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/t7600-merge.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 6e839a609c..b4f40e4c3a 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -173,6 +173,27 @@ test_expect_success 'merge c0 with c1 with --ff-only' ' test_debug 'git log --graph --decorate --oneline --all' +test_expect_success 'merge from unborn branch' ' + git checkout -f master && + test_might_fail git branch -D kid && + + echo "OBJID HEAD@{0}: initial pull" >reflog.expected && + + git checkout --orphan kid && + test_when_finished "git checkout -f master" && + git rm -fr . && + test_tick && + git merge --ff-only c1 && + verify_merge file result.1 && + verify_head "$c1" && + + git reflog -1 >reflog.actual && + sed "s/$_x05[0-9a-f][0-9a-f]/OBJID/g" reflog.actual >reflog.fuzzy && + test_cmp reflog.expected reflog.fuzzy +' + +test_debug 'git log --graph --decorate --oneline --all' + test_expect_success 'merge c1 with c2' ' git reset --hard c1 && test_tick && From 9cbdd76e097b01fa434dec9fdbe73b9cb6361e26 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 01:57:12 -0500 Subject: [PATCH 06/24] t6010 (merge-base): modernize style Guard setup with test_expect_success, put the opening quote starting each test on the same line as the test_expect_* invocation, and combine related actions into single tests. While at it: - use test_cmp instead of expr or test $foo = $bar, for more helpful output with -v when tests fail; - use test_commit for brevity. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/t6010-merge-base.sh | 283 ++++++++++++++++++++++-------------------- 1 file changed, 150 insertions(+), 133 deletions(-) diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 0144d9e858..4466e45790 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -8,170 +8,187 @@ test_description='Merge base computation. . ./test-lib.sh -T=$(git write-tree) +test_expect_success 'setup' ' + T=$(git write-tree) && -M=1130000000 -Z=+0000 + M=1130000000 && + Z=+0000 && -GIT_COMMITTER_EMAIL=git@comm.iter.xz -GIT_COMMITTER_NAME='C O Mmiter' -GIT_AUTHOR_NAME='A U Thor' -GIT_AUTHOR_EMAIL=git@au.thor.xz -export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL + GIT_COMMITTER_EMAIL=git@comm.iter.xz && + GIT_COMMITTER_NAME="C O Mmiter" && + GIT_AUTHOR_NAME="A U Thor" && + GIT_AUTHOR_EMAIL=git@au.thor.xz && + export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL && -doit() { - OFFSET=$1; shift - NAME=$1; shift - PARENTS= - for P - do - PARENTS="${PARENTS}-p $P " - done - GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z" - GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE - export GIT_COMMITTER_DATE GIT_AUTHOR_DATE - commit=$(echo $NAME | git commit-tree $T $PARENTS) - echo $commit >.git/refs/tags/$NAME - echo $commit -} + doit() { + OFFSET=$1 && + NAME=$2 && + shift 2 && -# E---D---C---B---A -# \'-_ \ \ -# \ `---------G \ -# \ \ -# F----------------H + PARENTS= && + for P + do + PARENTS="${PARENTS}-p $P " + done && -# Setup... -E=$(doit 5 E) -D=$(doit 4 D $E) -F=$(doit 6 F $E) -C=$(doit 3 C $D) -B=$(doit 2 B $C) -A=$(doit 1 A $B) -G=$(doit 7 G $B $E) -H=$(doit 8 H $A $F) + GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z" && + GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE && + export GIT_COMMITTER_DATE GIT_AUTHOR_DATE && -test_expect_success 'compute merge-base (single)' \ - 'MB=$(git merge-base G H) && - expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"' + commit=$(echo $NAME | git commit-tree $T $PARENTS) && -test_expect_success 'compute merge-base (all)' \ - 'MB=$(git merge-base --all G H) && - expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"' + echo $commit >.git/refs/tags/$NAME && + echo $commit + } +' -test_expect_success 'compute merge-base with show-branch' \ - 'MB=$(git show-branch --merge-base G H) && - expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"' +test_expect_success 'set up G and H' ' + # E---D---C---B---A + # \"-_ \ \ + # \ `---------G \ + # \ \ + # F----------------H + E=$(doit 5 E) && + D=$(doit 4 D $E) && + F=$(doit 6 F $E) && + C=$(doit 3 C $D) && + B=$(doit 2 B $C) && + A=$(doit 1 A $B) && + G=$(doit 7 G $B $E) && + H=$(doit 8 H $A $F) +' -# Setup for second test to demonstrate that relying on timestamps in a -# distributed SCM to provide a _consistent_ partial ordering of commits -# leads to insanity. -# -# Relative -# Structure timestamps -# -# PL PR +4 +4 -# / \/ \ / \/ \ -# L2 C2 R2 +3 -1 +3 -# | | | | | | -# L1 C1 R1 +2 -2 +2 -# | | | | | | -# L0 C0 R0 +1 -3 +1 -# \ | / \ | / -# S 0 -# -# The left and right chains of commits can be of any length and complexity as -# long as all of the timestamps are greater than that of S. +test_expect_success 'merge-base G H' ' + git name-rev $B >expected && -S=$(doit 0 S) + MB=$(git merge-base G H) && + git name-rev "$MB" >actual.single && -C0=$(doit -3 C0 $S) -C1=$(doit -2 C1 $C0) -C2=$(doit -1 C2 $C1) + MB=$(git merge-base --all G H) && + git name-rev "$MB" >actual.all && -L0=$(doit 1 L0 $S) -L1=$(doit 2 L1 $L0) -L2=$(doit 3 L2 $L1) + MB=$(git show-branch --merge-base G H) && + git name-rev "$MB" >actual.sb && -R0=$(doit 1 R0 $S) -R1=$(doit 2 R1 $R0) -R2=$(doit 3 R2 $R1) + test_cmp expected actual.single && + test_cmp expected actual.all && + test_cmp expected actual.sb +' -PL=$(doit 4 PL $L2 $C2) -PR=$(doit 4 PR $C2 $R2) +test_expect_success 'unsynchronized clocks' ' + # This test is to demonstrate that relying on timestamps in a distributed + # SCM to provide a _consistent_ partial ordering of commits leads to + # insanity. + # + # Relative + # Structure timestamps + # + # PL PR +4 +4 + # / \/ \ / \/ \ + # L2 C2 R2 +3 -1 +3 + # | | | | | | + # L1 C1 R1 +2 -2 +2 + # | | | | | | + # L0 C0 R0 +1 -3 +1 + # \ | / \ | / + # S 0 + # + # The left and right chains of commits can be of any length and complexity as + # long as all of the timestamps are greater than that of S. -test_expect_success 'compute merge-base (single)' \ - 'MB=$(git merge-base PL PR) && - expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"' + S=$(doit 0 S) && -test_expect_success 'compute merge-base (all)' \ - 'MB=$(git merge-base --all PL PR) && - expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"' + C0=$(doit -3 C0 $S) && + C1=$(doit -2 C1 $C0) && + C2=$(doit -1 C2 $C1) && -# Another set to demonstrate base between one commit and a merge -# in the documentation. -# -# * C (MMC) * B (MMB) * A (MMA) -# * o * o * o -# * o * o * o -# * o * o * o -# * o | _______/ -# | |/ -# | * 1 (MM1) -# | _______/ -# |/ -# * root (MMR) + L0=$(doit 1 L0 $S) && + L1=$(doit 2 L1 $L0) && + L2=$(doit 3 L2 $L1) && + R0=$(doit 1 R0 $S) && + R1=$(doit 2 R1 $R0) && + R2=$(doit 3 R2 $R1) && + + PL=$(doit 4 PL $L2 $C2) && + PR=$(doit 4 PR $C2 $R2) + + git name-rev $C2 >expected && + + MB=$(git merge-base PL PR) && + git name-rev "$MB" >actual.single && + + MB=$(git merge-base --all PL PR) && + git name-rev "$MB" >actual.all && + + test_cmp expected actual.single && + test_cmp expected actual.all +' test_expect_success 'merge-base for octopus-step (setup)' ' - test_tick && git commit --allow-empty -m root && git tag MMR && - test_tick && git commit --allow-empty -m 1 && git tag MM1 && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m A && git tag MMA && + # Another set to demonstrate base between one commit and a merge + # in the documentation. + # + # * C (MMC) * B (MMB) * A (MMA) + # * o * o * o + # * o * o * o + # * o * o * o + # * o | _______/ + # | |/ + # | * 1 (MM1) + # | _______/ + # |/ + # * root (MMR) + + test_commit MMR && + test_commit MM1 && + test_commit MM-o && + test_commit MM-p && + test_commit MM-q && + test_commit MMA && git checkout MM1 && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m B && git tag MMB && + test_commit MM-r && + test_commit MM-s && + test_commit MM-t && + test_commit MMB && git checkout MMR && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m C && git tag MMC + test_commit MM-u && + test_commit MM-v && + test_commit MM-w && + test_commit MM-x && + test_commit MMC ' test_expect_success 'merge-base A B C' ' - MB=$(git merge-base --all MMA MMB MMC) && - MM1=$(git rev-parse --verify MM1) && - test "$MM1" = "$MB" + git rev-parse --verify MM1 >expected && + git rev-parse --verify MMR >expected.sb && + + git merge-base --all MMA MMB MMC >actual && + git show-branch --merge-base MMA MMB MMC >actual.sb && + + test_cmp expected actual && + test_cmp expected.sb actual.sb ' -test_expect_success 'merge-base A B C using show-branch' ' - MB=$(git show-branch --merge-base MMA MMB MMC) && - MMR=$(git rev-parse --verify MMR) && - test "$MMR" = "$MB" -' - -test_expect_success 'criss-cross merge-base for octopus-step (setup)' ' +test_expect_success 'criss-cross merge-base for octopus-step' ' git reset --hard MMR && - test_tick && git commit --allow-empty -m 1 && git tag CC1 && + test_commit CC1 && git reset --hard E && - test_tick && git commit --allow-empty -m 2 && git tag CC2 && - test_tick && git merge -s ours CC1 && - test_tick && git commit --allow-empty -m o && - test_tick && git commit --allow-empty -m B && git tag CCB && + test_commit CC2 && + test_tick && + git merge -s ours CC1 && + test_commit CC-o && + test_commit CCB && git reset --hard CC1 && - test_tick && git merge -s ours CC2 && - test_tick && git commit --allow-empty -m A && git tag CCA -' + git merge -s ours CC2 && + test_commit CCA && -test_expect_success 'merge-base B A^^ A^^2' ' - MB0=$(git merge-base --all CCB CCA^^ CCA^^2 | sort) && - MB1=$(git rev-parse CC1 CC2 | sort) && - test "$MB0" = "$MB1" + git rev-parse CC1 CC2 >expected && + git merge-base --all CCB CCA^^ CCA^^2 >actual && + + sort expected >expected.sorted && + sort actual >actual.sorted && + test_cmp expected.sorted actual.sorted ' test_done From d834c96a0eba93de78aa8252f6fcca39d18343d5 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 01:57:40 -0500 Subject: [PATCH 07/24] t6200 (fmt-merge-msg): style nitpicks Guard setup with test_expect_success. Use test_might_fail instead of ignoring the exit code from git config --unset. Point out setup commands that are shared by multiple tests, to make it easy to write GIT_SKIP_TESTS specifications that work. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/t6200-fmt-merge-msg.sh | 272 +++++++++++++++++---------------------- 1 file changed, 116 insertions(+), 156 deletions(-) diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index 42f8ece097..5553dff55a 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -70,14 +70,13 @@ test_expect_success setup ' i=$(($i+1)) done && - git show-branch + git show-branch && + + apos="'\''" ' -cat >expected <<\EOF -Merge branch 'left' -EOF - -test_expect_success 'merge-msg test #1' ' +test_expect_success 'message for merging local branch' ' + echo "Merge branch ${apos}left${apos}" >expected && git checkout master && git fetch . left && @@ -86,11 +85,8 @@ test_expect_success 'merge-msg test #1' ' test_cmp expected actual ' -cat >expected <expected && git checkout master && git fetch "$(pwd)" left && @@ -99,139 +95,108 @@ test_expect_success 'merge-msg test #2' ' test_cmp expected actual ' -cat >expected <<\EOF -Merge branch 'left' +test_expect_success '[merge] summary/log configuration' ' + cat >expected <<-EOF && + Merge branch ${apos}left${apos} -* left: - Left #5 - Left #4 - Left #3 - Common #2 - Common #1 -EOF + * left: + Left #5 + Left #4 + Left #3 + Common #2 + Common #1 + EOF -test_expect_success 'merge-msg test #3-1' ' - - git config --unset-all merge.log - git config --unset-all merge.summary git config merge.log true && + test_might_fail git config --unset-all merge.summary && git checkout master && test_tick && git fetch . left && - git fmt-merge-msg <.git/FETCH_HEAD >actual && - test_cmp expected actual -' + git fmt-merge-msg <.git/FETCH_HEAD >actual1 && -test_expect_success 'merge-msg test #3-2' ' - - git config --unset-all merge.log - git config --unset-all merge.summary + test_might_fail git config --unset-all merge.log && git config merge.summary true && git checkout master && test_tick && git fetch . left && - git fmt-merge-msg <.git/FETCH_HEAD >actual && - test_cmp expected actual + git fmt-merge-msg <.git/FETCH_HEAD >actual2 && + + test_cmp expected actual1 && + test_cmp expected actual2 ' -cat >expected <<\EOF -Merge branches 'left' and 'right' +test_expect_success 'setup: expected shortlog for two branches' ' + cat >expected <<-EOF + Merge branches ${apos}left${apos} and ${apos}right${apos} -* left: - Left #5 - Left #4 - Left #3 - Common #2 - Common #1 + * left: + Left #5 + Left #4 + Left #3 + Common #2 + Common #1 -* right: - Right #5 - Right #4 - Right #3 - Common #2 - Common #1 -EOF + * right: + Right #5 + Right #4 + Right #3 + Common #2 + Common #1 + EOF +' -test_expect_success 'merge-msg test #4-1' ' - - git config --unset-all merge.log - git config --unset-all merge.summary +test_expect_success 'shortlog for two branches' ' git config merge.log true && - + test_might_fail git config --unset-all merge.summary && git checkout master && test_tick && git fetch . left right && + git fmt-merge-msg <.git/FETCH_HEAD >actual1 && - git fmt-merge-msg <.git/FETCH_HEAD >actual && - test_cmp expected actual -' - -test_expect_success 'merge-msg test #4-2' ' - - git config --unset-all merge.log - git config --unset-all merge.summary + test_might_fail git config --unset-all merge.log && git config merge.summary true && - git checkout master && test_tick && git fetch . left right && + git fmt-merge-msg <.git/FETCH_HEAD >actual2 && - git fmt-merge-msg <.git/FETCH_HEAD >actual && - test_cmp expected actual -' - -test_expect_success 'merge-msg test #5-1' ' - - git config --unset-all merge.log - git config --unset-all merge.summary git config merge.log yes && - + test_might_fail git config --unset-all merge.summary && git checkout master && test_tick && git fetch . left right && + git fmt-merge-msg <.git/FETCH_HEAD >actual3 && - git fmt-merge-msg <.git/FETCH_HEAD >actual && - test_cmp expected actual -' - -test_expect_success 'merge-msg test #5-2' ' - - git config --unset-all merge.log - git config --unset-all merge.summary + test_might_fail git config --unset-all merge.log && git config merge.summary yes && - git checkout master && test_tick && git fetch . left right && + git fmt-merge-msg <.git/FETCH_HEAD >actual4 && - git fmt-merge-msg <.git/FETCH_HEAD >actual && - test_cmp expected actual + test_cmp expected actual1 && + test_cmp expected actual2 && + test_cmp expected actual3 && + test_cmp expected actual4 ' test_expect_success 'merge-msg -F' ' - - git config --unset-all merge.log - git config --unset-all merge.summary + test_might_fail git config --unset-all merge.log && git config merge.summary yes && - git checkout master && test_tick && git fetch . left right && - git fmt-merge-msg -F .git/FETCH_HEAD >actual && test_cmp expected actual ' test_expect_success 'merge-msg -F in subdirectory' ' - - git config --unset-all merge.log - git config --unset-all merge.summary + test_might_fail git config --unset-all merge.log && git config merge.summary yes && - git checkout master && test_tick && git fetch . left right && @@ -245,11 +210,11 @@ test_expect_success 'merge-msg -F in subdirectory' ' ' test_expect_success 'merge-msg with nothing to merge' ' - - git config --unset-all merge.log - git config --unset-all merge.summary + test_might_fail git config --unset-all merge.log && git config merge.summary yes && + >empty && + ( cd remote && git checkout -b unrelated && @@ -258,22 +223,20 @@ test_expect_success 'merge-msg with nothing to merge' ' git fmt-merge-msg <.git/FETCH_HEAD >../actual ) && - test_cmp /dev/null actual + test_cmp empty actual ' -cat >expected <<\EOF -Merge tag 'tag-r3' - -* tag 'tag-r3': - Right #3 - Common #2 - Common #1 -EOF - test_expect_success 'merge-msg tag' ' + cat >expected <<-EOF && + Merge tag ${apos}tag-r3${apos} - git config --unset-all merge.log - git config --unset-all merge.summary + * tag ${apos}tag-r3${apos}: + Right #3 + Common #2 + Common #1 + EOF + + test_might_fail git config --unset-all merge.log && git config merge.summary yes && git checkout master && @@ -284,26 +247,24 @@ test_expect_success 'merge-msg tag' ' test_cmp expected actual ' -cat >expected <<\EOF -Merge tags 'tag-r3' and 'tag-l5' - -* tag 'tag-r3': - Right #3 - Common #2 - Common #1 - -* tag 'tag-l5': - Left #5 - Left #4 - Left #3 - Common #2 - Common #1 -EOF - test_expect_success 'merge-msg two tags' ' + cat >expected <<-EOF && + Merge tags ${apos}tag-r3${apos} and ${apos}tag-l5${apos} - git config --unset-all merge.log - git config --unset-all merge.summary + * tag ${apos}tag-r3${apos}: + Right #3 + Common #2 + Common #1 + + * tag ${apos}tag-l5${apos}: + Left #5 + Left #4 + Left #3 + Common #2 + Common #1 + EOF + + test_might_fail git config --unset-all merge.log && git config merge.summary yes && git checkout master && @@ -314,26 +275,24 @@ test_expect_success 'merge-msg two tags' ' test_cmp expected actual ' -cat >expected <<\EOF -Merge branch 'left', tag 'tag-r3' - -* tag 'tag-r3': - Right #3 - Common #2 - Common #1 - -* left: - Left #5 - Left #4 - Left #3 - Common #2 - Common #1 -EOF - test_expect_success 'merge-msg tag and branch' ' + cat >expected <<-EOF && + Merge branch ${apos}left${apos}, tag ${apos}tag-r3${apos} - git config --unset-all merge.log - git config --unset-all merge.summary + * tag ${apos}tag-r3${apos}: + Right #3 + Common #2 + Common #1 + + * left: + Left #5 + Left #4 + Left #3 + Common #2 + Common #1 + EOF + + test_might_fail git config --unset-all merge.log && git config merge.summary yes && git checkout master && @@ -344,26 +303,27 @@ test_expect_success 'merge-msg tag and branch' ' test_cmp expected actual ' -cat >expected <<\EOF -Merge branch 'long' - -* long: (35 commits) -EOF - test_expect_success 'merge-msg lots of commits' ' + { + cat <<-EOF && + Merge branch ${apos}long${apos} + + * long: (35 commits) + EOF + + i=29 && + while test $i -gt 9 + do + echo " $i" && + i=$(($i-1)) + done && + echo " ..." + } >expected && git checkout master && test_tick && git fetch . long && - i=29 && - while test $i -gt 9 - do - echo " $i" && - i=$(($i-1)) - done >>expected && - echo " ..." >>expected - git fmt-merge-msg <.git/FETCH_HEAD >actual && test_cmp expected actual ' From 1846e9edf6b9c8b8e84b96eab8753a1420b6b28d Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 01:59:55 -0500 Subject: [PATCH 08/24] Documentation: add a SEE ALSO section for merge-base For example, a person reading the merge-base man page might wonder about the fastest way to check if one commit is an ancestor of another (which would require rev-list). Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Documentation/git-merge-base.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index ce5b369985..09b34b0501 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -96,6 +96,12 @@ Documentation -------------- Documentation by David Greaves, Junio C Hamano and the git-list . +See also +-------- +linkgit:git-rev-list[1], +linkgit:git-show-branch[1], +linkgit:git-merge[1] + GIT --- Part of the linkgit:git[1] suite From aa8f98c1bfcf162e0bd23d20c34857940f2c2256 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 02:01:15 -0500 Subject: [PATCH 09/24] merge-base --octopus to mimic show-branch --merge-base While show-branch --merge-base does not support more than MAX_REVS revs, git supports more with a different algorithm (v1.6.0-rc0~51^2~13, Introduce get_octopus_merge_bases() in commit.c, 2008-06-27). Expose that functionality. This should help scripts to catch up with builtin merge in supporting dodecapus. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Documentation/git-merge-base.txt | 19 ++++++++++------- builtin/merge-base.c | 35 +++++++++++++++++++++++++++++--- t/t6010-merge-base.sh | 2 ++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index 09b34b0501..125207ef1e 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -8,7 +8,7 @@ git-merge-base - Find as good common ancestors as possible for a merge SYNOPSIS -------- -'git merge-base' [-a|--all] ... +'git merge-base' [-a|--all] [--octopus] ... DESCRIPTION ----------- @@ -20,12 +20,12 @@ that does not have any better common ancestor is a 'best common ancestor', i.e. a 'merge base'. Note that there can be more than one merge base for a pair of commits. -Among the two commits to compute the merge base from, one is specified by -the first commit argument on the command line; the other commit is a -(possibly hypothetical) commit that is a merge across all the remaining -commits on the command line. As the most common special case, specifying only -two commits on the command line means computing the merge base between -the given two commits. +Unless `--octopus` is given, among the two commits to compute the merge +base from, one is specified by the first commit argument on the command +line; the other commit is a (possibly hypothetical) commit that is a merge +across all the remaining commits on the command line. As the most common +special case, specifying only two commits on the command line means +computing the merge base between the given two commits. As a consequence, the 'merge base' is not necessarily contained in each of the commit arguments if more than two commits are specified. This is different @@ -37,6 +37,11 @@ OPTIONS --all:: Output all merge bases for the commits, instead of just one. +--octopus:: + Compute the best common ancestors of all supplied commits, + in preparation for an n-way merge. This mimics the behavior + of 'git show-branch --merge-base'. + DISCUSSION ---------- diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 54e7ec2237..c30128659f 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -23,7 +23,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) } static const char * const merge_base_usage[] = { - "git merge-base [-a|--all] ...", + "git merge-base [-a|--all] [--octopus] ...", NULL }; @@ -41,21 +41,50 @@ static struct commit *get_commit_reference(const char *arg) return r; } +static int show_octopus_merge_bases(int count, const char **args, int show_all) +{ + struct commit_list *revs = NULL; + struct commit_list *result; + int i; + + for (i = count - 1; i >= 0; i++) + commit_list_insert(get_commit_reference(args[i]), &revs); + result = get_octopus_merge_bases(revs); + + if (!result) + return 1; + + while (result) { + printf("%s\n", sha1_to_hex(result->item->object.sha1)); + if (!show_all) + return 0; + result = result->next; + } + + return 0; +} + int cmd_merge_base(int argc, const char **argv, const char *prefix) { struct commit **rev; int rev_nr = 0; int show_all = 0; + int octopus = 0; struct option options[] = { - OPT_BOOLEAN('a', "all", &show_all, "outputs all common ancestors"), + OPT_BOOLEAN('a', "all", &show_all, "output all common ancestors"), + OPT_BOOLEAN(0, "octopus", &octopus, "find ancestors for a single n-way merge"), OPT_END() }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); - if (argc < 2) + if (!octopus && argc < 2) usage_with_options(merge_base_usage, options); + + if (octopus) + return show_octopus_merge_bases(argc, argv, show_all); + rev = xmalloc(argc * sizeof(*rev)); while (argc-- > 0) rev[rev_nr++] = get_commit_reference(*argv++); diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 4466e45790..001431b363 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -164,9 +164,11 @@ test_expect_success 'merge-base A B C' ' git rev-parse --verify MMR >expected.sb && git merge-base --all MMA MMB MMC >actual && + git merge-base --all --octopus MMA MMB MMC >actual.common && git show-branch --merge-base MMA MMB MMC >actual.sb && test_cmp expected actual && + test_cmp expected.sb actual.common && test_cmp expected.sb actual.sb ' From a1e0ad78b784fd7c47c7bc2847f4813aca4cebaf Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 02:01:54 -0500 Subject: [PATCH 10/24] merge-base --independent to print reduced parent list in a merge While show-branch --independent does not support more than MAX_REVS revs, git internally supports more with a different algorithm. Expose that functionality as "git merge-base --independent". This should help scripts to catch up with builtin merge in supporting dodecapus. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Documentation/git-merge-base.txt | 9 ++++++++ builtin/merge-base.c | 21 ++++++++++++----- t/t6010-merge-base.sh | 39 +++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index 125207ef1e..eedef1bb1a 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -8,7 +8,9 @@ git-merge-base - Find as good common ancestors as possible for a merge SYNOPSIS -------- +[verse] 'git merge-base' [-a|--all] [--octopus] ... +'git merge-base' --independent ... DESCRIPTION ----------- @@ -42,6 +44,13 @@ OPTIONS in preparation for an n-way merge. This mimics the behavior of 'git show-branch --merge-base'. +--independent:: + Instead of printing merge bases, print a minimal subset of + the supplied commits with the same ancestors. In other words, + among the commits given, list those which cannot be reached + from any other. This mimics the behavior of 'git show-branch + --independent'. + DISCUSSION ---------- diff --git a/builtin/merge-base.c b/builtin/merge-base.c index c30128659f..96dd160731 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -24,6 +24,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) static const char * const merge_base_usage[] = { "git merge-base [-a|--all] [--octopus] ...", + "git merge-base --independent ...", NULL }; @@ -41,15 +42,19 @@ static struct commit *get_commit_reference(const char *arg) return r; } -static int show_octopus_merge_bases(int count, const char **args, int show_all) +static int handle_octopus(int count, const char **args, int reduce, int show_all) { struct commit_list *revs = NULL; struct commit_list *result; int i; - for (i = count - 1; i >= 0; i++) + if (reduce) + show_all = 1; + + for (i = count - 1; i >= 0; i--) commit_list_insert(get_commit_reference(args[i]), &revs); - result = get_octopus_merge_bases(revs); + + result = reduce ? reduce_heads(revs) : get_octopus_merge_bases(revs); if (!result) return 1; @@ -70,20 +75,24 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) int rev_nr = 0; int show_all = 0; int octopus = 0; + int reduce = 0; struct option options[] = { OPT_BOOLEAN('a', "all", &show_all, "output all common ancestors"), OPT_BOOLEAN(0, "octopus", &octopus, "find ancestors for a single n-way merge"), + OPT_BOOLEAN(0, "independent", &reduce, "list revs not reachable from others"), OPT_END() }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); - if (!octopus && argc < 2) + if (!octopus && !reduce && argc < 2) usage_with_options(merge_base_usage, options); + if (reduce && (show_all || octopus)) + die("--independent cannot be used with other options"); - if (octopus) - return show_octopus_merge_bases(argc, argv, show_all); + if (octopus || reduce) + return handle_octopus(argc, argv, reduce, show_all); rev = xmalloc(argc * sizeof(*rev)); while (argc-- > 0) diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 001431b363..62197a3d35 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano # -test_description='Merge base computation. +test_description='Merge base and parent list computation. ' . ./test-lib.sh @@ -75,6 +75,26 @@ test_expect_success 'merge-base G H' ' test_cmp expected actual.sb ' +test_expect_success 'merge-base/show-branch --independent' ' + git name-rev "$H" >expected1 && + git name-rev "$H" "$G" >expected2 && + + parents=$(git merge-base --independent H) && + git name-rev $parents >actual1.mb && + parents=$(git merge-base --independent A H G) && + git name-rev $parents >actual2.mb && + + parents=$(git show-branch --independent H) && + git name-rev $parents >actual1.sb && + parents=$(git show-branch --independent A H G) && + git name-rev $parents >actual2.sb && + + test_cmp expected1 actual1.mb && + test_cmp expected2 actual2.mb && + test_cmp expected1 actual1.sb && + test_cmp expected2 actual2.sb +' + test_expect_success 'unsynchronized clocks' ' # This test is to demonstrate that relying on timestamps in a distributed # SCM to provide a _consistent_ partial ordering of commits leads to @@ -125,6 +145,23 @@ test_expect_success 'unsynchronized clocks' ' test_cmp expected actual.all ' +test_expect_success '--independent with unsynchronized clocks' ' + IB=$(doit 0 IB) && + I1=$(doit -10 I1 $IB) && + I2=$(doit -9 I2 $I1) && + I3=$(doit -8 I3 $I2) && + I4=$(doit -7 I4 $I3) && + I5=$(doit -6 I5 $I4) && + I6=$(doit -5 I6 $I5) && + I7=$(doit -4 I7 $I6) && + I8=$(doit -3 I8 $I7) && + IH=$(doit -2 IH $I8) && + + echo $IH >expected && + git merge-base --independent IB IH >actual && + test_cmp expected actual +' + test_expect_success 'merge-base for octopus-step (setup)' ' # Another set to demonstrate base between one commit and a merge # in the documentation. From 2102440c17f990fcb5c9269459c39c6312e80d1c Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 18:00:34 -0500 Subject: [PATCH 11/24] fmt-merge-msg -m to override merge title Since v1.7.1.1~23^2 (merge: --log appends shortlog to message if specified, 2010-05-11), the fmt-merge-msg backend supports custom text to override the merge title "Merge into ". Expose this functionality for scripted callers. Example: git fmt-merge-msg --log -m \ "$(printf '%s\n' \ "Merge branch 'api-cleanup' into feature" \ '' \ 'This is to use a few functions refactored for this purpose.' )" <.git/FETCH_HEAD Cc: Tay Ray Chuan Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Documentation/git-fmt-merge-msg.txt | 9 ++++++-- builtin/fmt-merge-msg.c | 18 ++++++++++++++-- t/t6200-fmt-merge-msg.sh | 32 +++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/Documentation/git-fmt-merge-msg.txt b/Documentation/git-fmt-merge-msg.txt index a585dbe898..302f56b889 100644 --- a/Documentation/git-fmt-merge-msg.txt +++ b/Documentation/git-fmt-merge-msg.txt @@ -9,8 +9,8 @@ git-fmt-merge-msg - Produce a merge commit message SYNOPSIS -------- [verse] -'git fmt-merge-msg' [--log | --no-log] <$GIT_DIR/FETCH_HEAD -'git fmt-merge-msg' [--log | --no-log] -F +'git fmt-merge-msg' [-m ] [--log | --no-log] <$GIT_DIR/FETCH_HEAD +'git fmt-merge-msg' [-m ] [--log | --no-log] -F DESCRIPTION ----------- @@ -38,6 +38,11 @@ OPTIONS Synonyms to --log and --no-log; these are deprecated and will be removed in the future. +-m :: +--message :: + Use instead of the branch names for the first line + of the log message. For use with `--log`. + -F :: --file :: Take the list of merged objects from instead of diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index bc3c5e6d3e..937d5a717b 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -7,7 +7,7 @@ #include "string-list.h" static const char * const fmt_merge_msg_usage[] = { - "git fmt-merge-msg [--log|--no-log] [--file ]", + "git fmt-merge-msg [-m ] [--log|--no-log] [--file ]", NULL }; @@ -319,11 +319,14 @@ int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out) { int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) { const char *inpath = NULL; + const char *message = NULL; struct option options[] = { OPT_BOOLEAN(0, "log", &merge_summary, "populate log with the shortlog"), { OPTION_BOOLEAN, 0, "summary", &merge_summary, NULL, "alias for --log (deprecated)", PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, + OPT_STRING('m', "message", &message, "text", + "use as start of message"), OPT_FILENAME('F', "file", &inpath, "file to read from"), OPT_END() }; @@ -337,6 +340,12 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) 0); if (argc > 0) usage_with_options(fmt_merge_msg_usage, options); + if (message && !merge_summary) { + char nl = '\n'; + write_in_full(STDOUT_FILENO, message, strlen(message)); + write_in_full(STDOUT_FILENO, &nl, 1); + return 0; + } if (inpath && strcmp(inpath, "-")) { in = fopen(inpath, "r"); @@ -346,7 +355,12 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) if (strbuf_read(&input, fileno(in), 0) < 0) die_errno("could not read input file"); - ret = fmt_merge_msg(merge_summary, &input, &output); + if (message) { + strbuf_addstr(&output, message); + ret = fmt_merge_msg_shortlog(&input, &output); + } else { + ret = fmt_merge_msg(merge_summary, &input, &output); + } if (ret) return ret; write_in_full(STDOUT_FILENO, output.buf, output.len); diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index 5553dff55a..71f6cad3c2 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -129,6 +129,38 @@ test_expect_success '[merge] summary/log configuration' ' test_cmp expected actual2 ' +test_expect_success 'fmt-merge-msg -m' ' + echo "Sync with left" >expected && + cat >expected.log <<-EOF && + Sync with left + + * ${apos}left${apos} of $(pwd): + Left #5 + Left #4 + Left #3 + Common #2 + Common #1 + EOF + + test_might_fail git config --unset merge.log && + test_might_fail git config --unset merge.summary && + git checkout master && + git fetch "$(pwd)" left && + git fmt-merge-msg -m "Sync with left" <.git/FETCH_HEAD >actual && + git fmt-merge-msg --log -m "Sync with left" \ + <.git/FETCH_HEAD >actual.log && + git config merge.log true && + git fmt-merge-msg -m "Sync with left" \ + <.git/FETCH_HEAD >actual.log-config && + git fmt-merge-msg --no-log -m "Sync with left" \ + <.git/FETCH_HEAD >actual.nolog && + + test_cmp expected actual && + test_cmp expected.log actual.log && + test_cmp expected.log actual.log-config && + test_cmp expected actual.nolog +' + test_expect_success 'setup: expected shortlog for two branches' ' cat >expected <<-EOF Merge branches ${apos}left${apos} and ${apos}right${apos} From 22e0560a70f4b5bfbbf571647e03fa154a6d6bd1 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 02:03:07 -0500 Subject: [PATCH 12/24] merge script: --squash, --ff from unborn branch are errors Port v1.6.1-rc1~319 (provide more errors for the "merge into empty head" case, 2008-08-21) to the example merge script. Noticed by comparison with builtin merge. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- contrib/examples/git-merge.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh index 8f617fcb70..72853cf7fe 100755 --- a/contrib/examples/git-merge.sh +++ b/contrib/examples/git-merge.sh @@ -245,6 +245,10 @@ then exit 1 fi + test "$squash" != t || + die "Squash commit into empty head not supported yet" + test "$allow_fast_forward" = t || + die "Non-fast-forward into an empty head does not make sense" rh=$(git rev-parse --verify "$1^0") || die "$1 - not something we can merge" From 64048d670b4810125493ff0a2bab88cb12344e7f Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 02:03:36 -0500 Subject: [PATCH 13/24] merge script: tweak unmerged files message to match builtin Before: You are in the middle of a conflicted merge. After: Merge is not possible because you have unmerged files. I prefer the old message, but the new one is more consistent with other commands and tests expect it. In particular, without this change the scripted merge does not pass t3030. Based on v1.7.0-rc0~66^2 (Be more user-friendly when refusing to do something because of conflict., 2010-01-12). Cc: Matthieu Moy Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- contrib/examples/git-merge.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh index 72853cf7fe..99fe1f2594 100755 --- a/contrib/examples/git-merge.sh +++ b/contrib/examples/git-merge.sh @@ -25,7 +25,7 @@ require_work_tree cd_to_toplevel test -z "$(git ls-files -u)" || - die "You are in the middle of a conflicted merge." + die "Merge is not possible because you have unmerged files." LF=' ' From 6239af1c6c272eeb2b15359ff0dc25d769d5ac78 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 02:03:58 -0500 Subject: [PATCH 14/24] merge script: refuse to merge during merge Check MERGE_HEAD and bail out if it exists. Based on v1.6.3.3~3^2 (refuse to merge during a merge, 2009-06-01). Without this change, the scripted merge does not pass t3030. Cc: Clemens Buchacher Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- contrib/examples/git-merge.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh index 99fe1f2594..50042df1b9 100755 --- a/contrib/examples/git-merge.sh +++ b/contrib/examples/git-merge.sh @@ -27,6 +27,9 @@ cd_to_toplevel test -z "$(git ls-files -u)" || die "Merge is not possible because you have unmerged files." +! test -e "$GIT_DIR/MERGE_HEAD" || + die 'You have not concluded your merge (MERGE_HEAD exists).' + LF=' ' From 13dd255e4abb95895aaef72e2b93564be90cb49c Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 02:04:43 -0500 Subject: [PATCH 15/24] merge script: improve log message subject - point out remote-tracking branches as "remote branch 'upstream/master'"; - avoid misleading log messages when a tag and branch share a name. This approximates the builtin merge command's behavior well enough to pass the relevant tests. Based roughly on v1.6.4.2~10^2 (merge: indicate remote tracking branches in merge message, 2009-08-09) and v1.6.4.2~10^2~1 (merge: fix incorrect merge message for ambiguous tag/branch, 2009-08-09). Cc: Jeff King Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- contrib/examples/git-merge.sh | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh index 50042df1b9..d491566660 100755 --- a/contrib/examples/git-merge.sh +++ b/contrib/examples/git-merge.sh @@ -134,21 +134,32 @@ finish () { merge_name () { remote="$1" rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return - bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null) - if test "$rh" = "$bh" - then - echo "$rh branch '$remote' of ." - elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') && + if truname=$(expr "$remote" : '\(.*\)~[0-9]*$') && git show-ref -q --verify "refs/heads/$truname" 2>/dev/null then echo "$rh branch '$truname' (early part) of ." - elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD" + return + fi + if found_ref=$(git rev-parse --symbolic-full-name --verify \ + "$remote" 2>/dev/null) + then + if test "${found_ref#refs/heads/}" != "$found_ref" + then + echo "$rh branch '$remote' of ." + return + elif test "${found_ref#refs/remotes/}" != "$found_ref" + then + echo "$rh remote branch '$remote' of ." + return + fi + fi + if test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD" then sed -e 's/ not-for-merge / /' -e 1q \ "$GIT_DIR/FETCH_HEAD" - else - echo "$rh commit '$remote'" + return fi + echo "$rh commit '$remote'" } parse_config () { From 5d75a52b3a8870c3c1b27c5d791da5c9267e1fb5 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Aug 2010 02:05:10 -0500 Subject: [PATCH 16/24] merge script: merge -X