>From 779142c6b22f032c877f550733e1bccc99aca761 Mon Sep 17 00:00:00 2001 From: Sverre Hvammen Johansen <hvammen@xxxxxxxxx> Date: Sun, 9 Mar 2008 21:16:03 -0800 Subject: [PATCH] Additional fast forward strategies. New fast forward strategies, common, fork, path, and same is introduced. These new fast forward strategies allows additional work flows. FF strategy "common" does a fast-forward to the common ancestor of the specified heads. The merge will fail unless HEAD is the common ancestor or HEAD can be fast-forwarded to the common ancestor. FF strategy "fork" does a fast-forward to the common ancestor of the real heads. The merge will fail unless HEAD is the common ancestor of these heads or HEAD can be fast-forwarded to the common ancestor of the real heads. FF strategy "path" does a fast-forward to the first possible branch that no other branches are ahead of. HEAD will be fast-forwarded to such a branch if it exist. If no such branch exist, HEAD is considered to be up to date. FF strategy "same" does a fast forward where all branches are required to point to the same commit. HEAD will be fast-forwarded to this branch unless it is up to date. Signed-off-by: Sverre Hvammen Johansen <sj@xxxxxxxxxxx> --- Documentation/fast-forward-strategies.txt | 25 ++ git-merge.sh | 53 ++++- t/t7601-merge-ff-strategies.sh | 373 +++++++++++++++++++++++++++++ 3 files changed, 446 insertions(+), 5 deletions(-) diff --git a/Documentation/fast-forward-strategies.txt b/Documentation/fast-forward-strategies.txt index 1d6da26..be94cfc 100644 --- a/Documentation/fast-forward-strategies.txt +++ b/Documentation/fast-forward-strategies.txt @@ -14,3 +14,28 @@ only:: Only allow a fast-forward. The merge will fail unless HEAD is up to date or the merge resolved as a fast-forward. + +common:: + Fast-forward to the common ancestor of the specified + branches if possible. The merge will fail unless + HEAD is the common ancestor or HEAD can be + fast-forwarded to the common ancestor. + +fork:: + Fast-forward to the earliest fork if possible. + The earliest fork is defined as the common ancestor + of the real branches. The merge will fail unless + HEAD is the earliest fork or HEAD can be + fast-forwarded to the earliest fork. + +path:: + Fast-forward to the first possible branch that + no other branches are ahead of. HEAD will be + fast-forwarded to such a branch if it exist. + If no such branch exist, HEAD is considered to be + up to date. + +same:: + The merge will fail unless HEAD is up to date or + the merge resolved as a fast-forward and each + branch is pointing to the same commit. diff --git a/git-merge.sh b/git-merge.sh index 873e4cb..d474f03 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -164,21 +164,21 @@ parse_config () { no_commit=t ;; --ff) case "$2" in - allow|never|only) + allow|never|only|common|fork|path|same) fast_forward=$2 squash= no_commit= ; shift ;; -*) fast_forward=allow squash= no_commit= ;; *) - die "available fast-forward strategies are: allow, newer, and only" ;; + die "available fast-forward strategies are: allow, newer, only, common, path" ;; esac ;; --ff=*) fast_forward=$(echo $1 |cut -d = -f 2) squash= no_commit= case $fast_forward in - allow|never|only) + allow|never|only|common|fork|path|same) ;; *) - die "available fast-forward strategies are: allow, newer, and only" ;; + die "available fast-forward strategies are: allow, newer, only, common, path" ;; esac ;; --no-ff) @@ -374,12 +374,22 @@ find_real_parents () { done } -if test $fast_forward = never -o +if test $fast_forward = never -o $fast_forward = common then real_parents="$@" ff_head=$head else find_real_parents "$@" + if test $fast_forward = same + then + for b in "$@" + do + if test "$b" != "$1" + then + die "Fast forward strategy same requires all heads to be the same" + fi + done + fi fi if test -n "$real_parents" @@ -387,6 +397,39 @@ then case $fast_forward in only) die "Fast forward strategy only can only handle one real parent" ;; + same) + die "Fast forward strategy same have unexpected parents" ;; + path) + echo "Ignoring branches $real_parents" + real_parents= + ;; + common|fork) + if test $fast_forward = fork + then + common=$(git show-branch --merge-base $ff_head $real_parents) + else + common=$(git show-branch --merge-base $real_parents) + fi + if test -n "$common" + then + common_h=$(git show-branch --merge-base $common $head) + if test "$common_h" = $head + then + if test $common = $head + then + echo "Ignoring all branches" + else + echo "Ignoring all branches except for $common" + fi + ff_head=$common + else + die "HEAD is ahead of the common anchestor" + fi + else + die "The specified branches does not have any common anchestor" + fi + real_parents= + ;; never|allow) if test $head != $ff_head then diff --git a/t/t7601-merge-ff-strategies.sh b/t/t7601-merge-ff-strategies.sh index 6c0a91a..43cbe81 100755 --- a/t/t7601-merge-ff-strategies.sh +++ b/t/t7601-merge-ff-strategies.sh @@ -546,4 +546,377 @@ test_expect_success 'merge x0 with y2, c3, and c0' ' test_debug 'gitk --all' +test_expect_success 'merge y2 with x0, c3, and c0 (--ff=path)' ' + git reset --hard y2 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=path x0 c3 c0 && + verify_merge file result.1-5-13 && + verify_head $y2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge x0 with y2, c3, and c0 (--ff=path)' ' + git reset --hard x0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=path y2 c3 c0 && + verify_merge file result.1-5-13 && + verify_head $y2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with x0, c3, and c0 (--ff=path)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=path x0 c3 c0 && + verify_merge file result.1-5 && + verify_head $x0 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with y2, c3, and c0 (--ff=path)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=path y2 c3 c0 && + verify_merge file result.1-5-13 && + verify_head $y2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with x0, y3, and c0 (--ff=path)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=path x0 y3 c0 && + verify_merge file result.1-5-13 && + verify_head $y3 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with x0, c3, and y3 (--ff=path)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=path x0 c3 y3 && + verify_merge file result.1-5-13 && + verify_head $y3 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with y1, y2, and y3 (--ff=path)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=path y1 y2 y3 && + verify_merge file result.1-5-13 && + verify_head $y3 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with y1 (--ff=common)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=common y1 && + verify_merge file result.1-5-13 && + verify_head $y1 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with y1, y2, and y3 (--ff=common)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=common y1 y2 y3 && + verify_merge file result.1-5 && + verify_head $x0 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with c1 and c2 (--ff=common)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=common c1 c2 && + verify_merge file result.0 && + verify_head $c0; +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with y1 and y2 (--ff=common)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=common y1 y2 && + verify_merge file result.1-5 && + verify_head $x0; +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with y1 and y2 (--ff=common)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=common y1 y2 && + verify_merge file result.1-5 && + verify_head $x0; +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with y1 and y2 c0 (--ff=common)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + if git merge --ff=common y1 y2 c0 + then + false + else + verify_merge file result.1 && + verify_head $c1; + fi +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with x1 and c2 (--ff=common)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + if git merge --ff=common x1 c2 + then + false + else + verify_merge file result.1 && + verify_head $c1 + fi +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with x0 (--ff=fork)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=fork x0 && + verify_merge file result.1-5 && + verify_head $x0 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with y1, y2, and y3 (--ff=fork)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=fork y1 y2 y3 && + verify_merge file result.1-5-13 && + verify_head $y3 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with c1 and c2 (--ff=fork)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=fork c1 c2 && + verify_merge file result.0 && + verify_head $c0; +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with y1 and y2 (--ff=fork)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=fork y1 y2 && + verify_merge file result.1-5 && + verify_head $x0; +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with y1 and y2 (--ff=fork)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=fork y1 y2 && + verify_merge file result.1-5 && + verify_head $x0; +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with y1 and y2 c0 (--ff=fork)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=fork y1 y2 c0 && + verify_merge file result.1-5 && + verify_head $x0; +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with x1 and c2 (--ff=fork)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + if git merge --ff=fork x1 c2 + then + false + else + verify_merge file result.1 && + verify_head $c1 + fi +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with x1 (pull --ff=only)' ' + git reset --hard c1 && + test_tick && + git pull --ff=only clone refs/heads/master && + verify_merge file result.1-13 && + verify_head $x1 +' + +test_debug 'gitk --all' + +test_expect_success 'merge x2 with x1 (pull --ff=only)' ' + git reset --hard x2 && + test_tick && + if git pull --ff=only clone refs/heads/master + then + false + else + verify_merge file result.5-13 && + verify_head $x2 + fi +' + +test_debug 'gitk --all' + + + +test_expect_success 'merge c1 with new repository (pull --ff=only)' ' + git reset --hard c1 && + test_tick && + if git pull --ff=only new refs/heads/master + then + false + else + verify_merge file result.1 && + verify_head $c1 + fi +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with new repository (pull --ff=common)' ' + git reset --hard c1 && + test_tick && + if git pull --ff=common new refs/heads/master + then + false + else + verify_merge file result.1 && + verify_head $c1 + fi +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with new repository (pull --ff=fork)' ' + git reset --hard c1 && + test_tick && + if git pull --ff=fork new refs/heads/master + then + false + else + verify_merge file result.1 && + verify_head $c1 + fi +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with new repository (pull --ff=path)' ' + git reset --hard c1 && + test_tick && + git pull --ff=path new refs/heads/master && + verify_merge file result.1 && + verify_head $c1 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with y1 and y1 (--ff=same)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=same y1 y1 && + verify_merge file result.1-5-13 && + verify_head $y1 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c0 and c0 (--ff=same)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + git merge --ff=same c0 c0 && + verify_merge file result.1 && + verify_head $c1 +' + +test_debug 'gitk --all' + +test_expect_success 'merge x1 with c2 and c2 (--ff=same)' ' + git reset --hard x1 && + git config branch.master.mergeoptions "" && + test_tick && + if git merge --ff=same c2 c2 + then + false + else + verify_merge file result.1-13 && + verify_head $x1 + fi +' + +test_debug 'gitk --all' + +test_expect_success 'merge y1 with c1 and x1 (--ff=same)' ' + git reset --hard y1 && + git config branch.master.mergeoptions "" && + test_tick && + if git merge --ff=same c1 x1 + then + false + else + verify_merge file result.1-5-13 && + verify_head $y1 + fi +' + +test_debug 'gitk --all' + test_done -- 1.5.3.3 -- Sverre Hvammen Johansen -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html