Merge branch 'lh/merge'

* lh/merge:
  git-merge: add --ff and --no-ff options
  git-merge: add support for --commit and --no-squash
  git-merge: add support for branch.<name>.mergeoptions
  git-merge: refactor option parsing
  git-merge: fix faulty SQUASH_MSG
  Add test-script for git-merge porcelain
This commit is contained in:
Junio C Hamano 2007-10-03 03:05:58 -07:00
commit e66273a6ab
5 changed files with 521 additions and 11 deletions

View File

@ -337,6 +337,12 @@ branch.<name>.merge::
branch.<name>.merge to the desired branch, and use the special setting branch.<name>.merge to the desired branch, and use the special setting
`.` (a period) for branch.<name>.remote. `.` (a period) for branch.<name>.remote.
branch.<name>.mergeoptions::
Sets default options for merging into branch <name>. 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:: clean.requireForce::
A boolean to make git-clean do nothing unless given -f or -n. Defaults A boolean to make git-clean do nothing unless given -f or -n. Defaults
to false. to false.

View File

@ -58,6 +58,10 @@ merge.verbosity::
above outputs debugging information. The default is level 2. above outputs debugging information. The default is level 2.
Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable. Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
branch.<name>.mergeoptions::
Sets default options for merging into branch <name>. 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 HOW MERGE WORKS
--------------- ---------------

View File

@ -10,6 +10,10 @@
not autocommit, to give the user a chance to inspect and not autocommit, to give the user a chance to inspect and
further tweak the merge result before committing. 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:: --squash::
Produce the working tree and index state as if a real Produce the working tree and index state as if a real
merge happened, but do not actually make a commit or merge happened, but do not actually make a commit or
@ -19,6 +23,19 @@
top of the current branch whose effect is the same as top of the current branch whose effect is the same as
merging another branch (or more in case of an octopus). 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.
--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>, \--strategy=<strategy>:: -s <strategy>, \--strategy=<strategy>::
Use the given merge strategy; can be supplied more than Use the given merge strategy; can be supplied more than
once to specify them in the order they should be tried. once to specify them in the order they should be tried.

View File

@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano # Copyright (c) 2005 Junio C Hamano
# #
USAGE='[-n] [--summary] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+' USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s <strategy>] [-m=<merge-message>] <commit>+'
SUBDIRECTORY_OK=Yes SUBDIRECTORY_OK=Yes
. git-sh-setup . git-sh-setup
@ -59,7 +59,7 @@ finish_up_to_date () {
squash_message () { squash_message () {
echo Squashed commit of the following: echo Squashed commit of the following:
echo echo
git log --no-merges ^"$head" $remote git log --no-merges ^"$head" $remoteheads
} }
finish () { finish () {
@ -133,11 +133,7 @@ merge_name () {
fi fi
} }
case "$#" in 0) usage ;; esac parse_option () {
have_message=
while test $# != 0
do
case "$1" in case "$1" in
-n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\ -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
--no-summa|--no-summar|--no-summary) --no-summa|--no-summar|--no-summary)
@ -145,9 +141,17 @@ do
--summary) --summary)
show_diffstat=t ;; show_diffstat=t ;;
--sq|--squ|--squa|--squas|--squash) --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)
allow_fast_forward=t squash= no_commit= ;;
--c|--co|--com|--comm|--commi|--commit)
allow_fast_forward=t squash= no_commit= ;;
--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
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=*|\ -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
--strateg=*|--strategy=*|\ --strateg=*|--strategy=*|\
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
@ -180,9 +184,42 @@ do
have_message=t have_message=t
;; ;;
-*) usage ;; -*) usage ;;
*) break ;; *) return 1 ;;
esac esac
shift shift
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 $#
do
shift
done
done done
if test -z "$show_diffstat"; then if test -z "$show_diffstat"; then
@ -458,7 +495,13 @@ done
# auto resolved the merge cleanly. # auto resolved the merge cleanly.
if test '' != "$result_tree" if test '' != "$result_tree"
then 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 result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit
finish "$result_commit" "Merge made by $wt_strategy." finish "$result_commit" "Merge made by $wt_strategy."
dropsave dropsave

440
t/t7600-merge.sh Executable file
View File

@ -0,0 +1,440 @@
#!/bin/sh
#
# Copyright (c) 2007 Lars Hjemli
#
test_description='git-merge
Testing basic merge operations/option parsing.'
. ./test-lib.sh
cat >file <<EOF
1
2
3
4
5
6
7
8
9
EOF
cat >file.1 <<EOF
1 X
2
3
4
5
6
7
8
9
EOF
cat >file.5 <<EOF
1
2
3
4
5 X
6
7
8
9
EOF
cat >file.9 <<EOF
1
2
3
4
5
6
7
8
9 X
EOF
cat >result.1 <<EOF
1 X
2
3
4
5
6
7
8
9
EOF
cat >result.1-5 <<EOF
1 X
2
3
4
5 X
6
7
8
9
EOF
cat >result.1-5-9 <<EOF
1 X
2
3
4
5 X
6
7
8
9 X
EOF
create_merge_msgs() {
echo "Merge commit 'c2'" >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_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_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_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