From a85d1b69172f397e815e1ce02db41e4b82b86162 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:40 +0200 Subject: Add test-script for git-merge porcelain This test-script excercises the porcelainish aspects of git-merge, and does it thoroughly enough to detect a small bug already noticed by Junio: squashing an octopus generates a faulty .git/SQUASH_MSG. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh new file mode 100755 index 0000000..dec6ea2 --- /dev/null +++ b/t/t7600-merge.sh @@ -0,0 +1,344 @@ +#!/bin/sh +# +# Copyright (c) 2007 Lars Hjemli +# + +test_description='git-merge + +Testing basic merge operations/option parsing.' + +. ./test-lib.sh + +cat >file <file.1 <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 +} + +verify_diff() { + if ! diff -u "$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 ! 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)" + then + echo "[OOPS] HEAD^$i != $1" + return 1 + 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 | tail -n 1) + if test "$1" != "$head" + then + echo "[OOPS] MERGE_HEAD $i != $1" + return 1 + fi + i=$(expr $i + 1) + shift + done +} + +verify_no_mergehead() { + if test -f .git/MERGE_HEAD + then + echo "[OOPS] MERGE_HEAD exists" + false + fi +} + + +test_expect_success 'setup' ' + git add file && + test_tick && + git commit -m "commit 0" && + git tag c0 && + c0=$(git rev-parse HEAD) && + cp file.1 file && + git add file && + test_tick && + git commit -m "commit 1" && + git tag c1 && + c1=$(git rev-parse HEAD) && + git reset --hard "$c0" && + cp file.5 file && + git add file && + test_tick && + git commit -m "commit 2" && + git tag c2 && + c2=$(git rev-parse HEAD) && + git reset --hard "$c0" && + cp file.9 file && + git add file && + test_tick && + git commit -m "commit 3" && + git tag c3 && + c3=$(git rev-parse HEAD) + git reset --hard "$c0" && + create_merge_msgs +' + +test_debug 'gitk --all' + +test_expect_success 'test option parsing' ' + if git merge -$ c1 + then + echo "[OOPS] -$ accepted" + false + fi && + if git merge --no-such c1 + then + echo "[OOPS] --no-such accepted" + false + fi && + if git merge -s foobar c1 + then + echo "[OOPS] -s foobar accepted" + false + fi && + if git merge -s=foobar c1 + then + echo "[OOPS] -s=foobar accepted" + false + fi && + if git merge -m + then + echo "[OOPS] missing commit msg accepted" + false + fi && + if git merge + then + echo "[OOPS] missing commit references accepted" + false + fi +' + +test_expect_success 'merge c0 with c1' ' + git reset --hard c0 && + git merge c1 && + verify_merge file result.1 && + verify_head "$c1" +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2' ' + git reset --hard c1 && + test_tick && + git merge c2 && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 and c3' ' + git reset --hard c1 && + test_tick && + git merge c2 c3 && + verify_merge file result.1-5-9 msg.1-5-9 && + verify_parents $c1 $c2 $c3 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with c1 (no-commit)' ' + git reset --hard c0 && + git merge --no-commit c1 && + verify_merge file result.1 && + verify_head $c1 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 (no-commit)' ' + git reset --hard c1 && + git merge --no-commit c2 && + verify_merge file result.1-5 && + verify_head $c1 && + verify_mergeheads $c2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 and c3 (no-commit)' ' + git reset --hard c1 && + git merge --no-commit c2 c3 && + verify_merge file result.1-5-9 && + verify_head $c1 && + verify_mergeheads $c2 $c3 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with c1 (squash)' ' + git reset --hard c0 && + git merge --squash c1 && + verify_merge file result.1 && + verify_head $c0 && + verify_no_mergehead && + verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message" +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 (squash)' ' + git reset --hard c1 && + git merge --squash c2 && + 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_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 and c3 (squash)' ' + git reset --hard c1 && + git merge --squash c2 c3 && + 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_debug 'gitk --all' + +test_done -- cgit v0.10.2-6-g49f6 From 2ae4fd7695abcf2ab36f3fe984cc4ca44559635f Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:41 +0200 Subject: git-merge: fix faulty SQUASH_MSG Only the first 'remote' head is currently specified as an argument to 'git log' when generating a SQUSH_MSG, which makes the generated message fail to mention every commit involved in the merge. This fixes the problem. Noticed-by: Junio C Hamano Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/git-merge.sh b/git-merge.sh index cde09d4..919e6be 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -59,7 +59,7 @@ finish_up_to_date () { squash_message () { echo Squashed commit of the following: echo - git log --no-merges ^"$head" $remote + git log --no-merges ^"$head" $remoteheads } finish () { -- cgit v0.10.2-6-g49f6 From d38eb710d92864b0b1f7cd36f17e273e3d8c735c Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:42 +0200 Subject: git-merge: refactor option parsing Move the option parsing into a separate function as preparation for reuse by the next commit. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/git-merge.sh b/git-merge.sh index 919e6be..49185eb 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -119,11 +119,7 @@ merge_name () { fi } -case "$#" in 0) usage ;; esac - -have_message= -while test $# != 0 -do +parse_option () { case "$1" in -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\ --no-summa|--no-summar|--no-summary) @@ -166,9 +162,21 @@ do have_message=t ;; -*) usage ;; - *) break ;; + *) return 1 ;; esac shift + args_left=$# +} + +test $# != 0 || usage + +have_message= +while parse_option "$@" +do + while test $args_left -lt $# + do + shift + done done if test -z "$show_diffstat"; then -- cgit v0.10.2-6-g49f6 From aec7b362ad07e1a2c58051c8db653dabffee8960 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:43 +0200 Subject: git-merge: add support for branch..mergeoptions This enables per branch configuration of merge options. Currently, the most useful options to specify per branch are --squash, --summary/--no-summary and possibly --strategy, but all options are supported. Note: Options containing whitespace will _not_ be handled correctly. Luckily, the only option which can include whitespace is --message and it doesn't make much sense to give that option a default value. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index 015910f..d3c25f3 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -337,6 +337,12 @@ branch..merge:: branch..merge to the desired branch, and use the special setting `.` (a period) for branch..remote. +branch..mergeoptions:: + Sets default options for merging into branch . The syntax and + supported options are equal to that of gitlink:git-merge[1], but + option values containing whitespace characters are currently not + supported. + clean.requireForce:: A boolean to make git-clean do nothing unless given -f or -n. Defaults to false. diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 144bc16..b1771a1 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -58,6 +58,10 @@ merge.verbosity:: above outputs debugging information. The default is level 2. Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable. +branch..mergeoptions:: + Sets default options for merging into branch . The syntax and + supported options are equal to that of git-merge, but option values + containing whitespace characters are currently not supported. HOW MERGE WORKS --------------- diff --git a/git-merge.sh b/git-merge.sh index 49185eb..a35b151 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -168,9 +168,30 @@ parse_option () { args_left=$# } +parse_config () { + while test $# -gt 0 + do + parse_option "$@" || usage + while test $args_left -lt $# + do + shift + done + done +} + test $# != 0 || usage have_message= + +if branch=$(git-symbolic-ref -q HEAD) +then + mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions") + if test -n "$mergeopts" + then + parse_config $mergeopts + fi +fi + while parse_option "$@" do while test $args_left -lt $# diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index dec6ea2..110974c 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -341,4 +341,58 @@ test_expect_success 'merge c1 with c2 and c3 (squash)' ' test_debug 'gitk --all' +test_expect_success 'merge c1 with c2 (no-commit in config)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "--no-commit" && + git merge c2 && + verify_merge file result.1-5 && + verify_head $c1 && + verify_mergeheads $c2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 (squash in config)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "--squash" && + git merge c2 && + 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_debug 'gitk --all' + +test_expect_success 'override config option -n' ' + git reset --hard c1 && + git config branch.master.mergeoptions "-n" && + test_tick && + git merge --summary c2 >diffstat.txt && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 && + if ! grep -e "^ file | \+2 +-$" diffstat.txt + then + echo "[OOPS] diffstat was not generated" + fi +' + +test_debug 'gitk --all' + +test_expect_success 'override config option --summary' ' + git reset --hard c1 && + git config branch.master.mergeoptions "--summary" && + test_tick && + git merge -n c2 >diffstat.txt && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 && + if grep -e "^ file | \+2 +-$" diffstat.txt + then + echo "[OOPS] diffstat was generated" + false + fi +' + +test_debug 'gitk --all' + test_done -- cgit v0.10.2-6-g49f6 From d08af0ad745869a4fe36bc8df4f9804edfb74eb9 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:44 +0200 Subject: git-merge: add support for --commit and --no-squash These options can be used to override --no-commit and --squash, which is needed since --no-commit and --squash now can be specified as default merge options in $GIT_DIR/config. The change also introduces slightly different behavior for --no-commit: when specified, it explicitly overrides --squash. Earlier, 'git merge --squash --no-commit' would result in a squashed merge (i.e. no $GIT_DIR/MERGE_HEAD was created) but with this patch the command will behave as if --squash hadn't been specified. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index d64c259..0464a34 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -10,6 +10,10 @@ not autocommit, to give the user a chance to inspect and further tweak the merge result before committing. +--commit:: + Perform the merge and commit the result. This option can + be used to override --no-commit. + --squash:: Produce the working tree and index state as if a real merge happened, but do not actually make a commit or @@ -19,6 +23,10 @@ top of the current branch whose effect is the same as merging another branch (or more in case of an octopus). +--no-squash:: + Perform the merge and commit the result. This option can + be used to override --squash. + -s , \--strategy=:: Use the given merge strategy; can be supplied more than once to specify them in the order they should be tried. diff --git a/git-merge.sh b/git-merge.sh index a35b151..a0fc606 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano # -USAGE='[-n] [--summary] [--no-commit] [--squash] [-s ] [-m=] +' +USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [-s ] [-m=] +' SUBDIRECTORY_OK=Yes . git-sh-setup @@ -128,8 +128,12 @@ parse_option () { show_diffstat=t ;; --sq|--squ|--squa|--squas|--squash) squash=t no_commit=t ;; + --no-sq|--no-squ|--no-squa|--no-squas|--no-squash) + squash= no_commit= ;; + --c|--co|--com|--comm|--commi|--commit) + squash= no_commit= ;; --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) - no_commit=t ;; + squash= no_commit=t ;; -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ --strateg=*|--strategy=*|\ -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 110974c..b0ef488 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -395,4 +395,26 @@ test_expect_success 'override config option --summary' ' test_debug 'gitk --all' +test_expect_success 'merge c1 with c2 (override --no-commit)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "--no-commit" && + test_tick && + git merge --commit c2 && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 (override --squash)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "--squash" && + test_tick && + git merge --no-squash c2 && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 +' + +test_debug 'gitk --all' + test_done -- cgit v0.10.2-6-g49f6 From d66424c4ac661c69640765260235452499d80378 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:45 +0200 Subject: git-merge: add --ff and --no-ff options These new options can be used to control the policy for fast-forward merges: --ff allows it (this is the default) while --no-ff will create a merge commit. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index 0464a34..9f1fc82 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -27,6 +27,15 @@ Perform the merge and commit the result. This option can be used to override --squash. +--no-ff:: + Generate a merge commit even if the merge resolved as a + fast-forward. + +--ff:: + Do not generate a merge commit if the merge resolved as + a fast-forward, only update the branch pointer. This is + the default behavior of git-merge. + -s , \--strategy=:: Use the given merge strategy; can be supplied more than once to specify them in the order they should be tried. diff --git a/git-merge.sh b/git-merge.sh index a0fc606..ce66524 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano # -USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [-s ] [-m=] +' +USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s ] [-m=] +' SUBDIRECTORY_OK=Yes . git-sh-setup @@ -127,13 +127,17 @@ parse_option () { --summary) show_diffstat=t ;; --sq|--squ|--squa|--squas|--squash) - squash=t no_commit=t ;; + allow_fast_forward=t squash=t no_commit=t ;; --no-sq|--no-squ|--no-squa|--no-squas|--no-squash) - squash= no_commit= ;; + allow_fast_forward=t squash= no_commit= ;; --c|--co|--com|--comm|--commi|--commit) - squash= no_commit= ;; + allow_fast_forward=t squash= no_commit= ;; --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) - squash= no_commit=t ;; + allow_fast_forward=t squash= no_commit=t ;; + --ff) + allow_fast_forward=t squash= no_commit= ;; + --no-ff) + allow_fast_forward=false squash= no_commit= ;; -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ --strateg=*|--strategy=*|\ -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) @@ -477,7 +481,13 @@ done # auto resolved the merge cleanly. if test '' != "$result_tree" then - parents=$(git show-branch --independent "$head" "$@" | sed -e 's/^/-p /') + if test "$allow_fast_forward" = "t" + then + parents=$(git show-branch --independent "$head" "$@") + else + parents=$(git rev-parse "$head" "$@") + fi + parents=$(echo "$parents" | sed -e 's/^/-p /') result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit finish "$result_commit" "Merge made by $wt_strategy." dropsave diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index b0ef488..6424c6e 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -417,4 +417,24 @@ test_expect_success 'merge c1 with c2 (override --squash)' ' test_debug 'gitk --all' +test_expect_success 'merge c0 with c1 (no-ff)' ' + git reset --hard c0 && + test_tick && + git merge --no-ff c1 && + verify_merge file result.1 && + verify_parents $c0 $c1 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with c1 (ff overrides no-ff)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "--no-ff" && + git merge --ff c1 && + verify_merge file result.1 && + verify_head $c1 +' + +test_debug 'gitk --all' + test_done -- cgit v0.10.2-6-g49f6