git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RFC/PATCH] Additional fast forward strategies.

From
Sverre Hvammen Johansen <hvammen@gmail.com>
Date
Mar 11, 2008, 03:04 UTC
Message-ID
<402c10cd0803102004x7892f01cledfef6f277fba26a@mail.gmail.com>
>From 779142c6b22f032c877f550733e1bccc99aca761 Mon Sep 17 00:00:00 2001
From: Sverre Hvammen Johansen <hvammen@gmail.com>
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@black.local>
---
 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
Next: Junio C Hamano
Message 1 of 4 in “Additional fast forward strategies.”
  1. Additional fast forward strategies.Sverre Hvammen Johansen, Mar 11, 2008
  2. Junio C HamanoMar 11, 2008
  3. Jakub NarebskiMar 11, 2008
  4. Sverre Hvammen JohansenMar 12, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.