[PATCH 3/6] Introduce -ff=<fast forward option>

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Sverre Hvammen Johansen <hvammen@xxxxxxxxx>

--ff now takes an argument allowing --ff to be written
as --ff=allow and -no-ff to be written as --ff=never.
This change allow other fast forward options to be
introduced later.

See the documentation for a further explanation of these options.

Signed-off-by: Sverre Hvammen Johansen <hvammen@xxxxxxxxx>
---
 Documentation/fast-forward-options.txt |   44 ++++++++
 Documentation/git-merge.txt            |    6 +-
 Documentation/git-pull.txt             |    2 +
 Documentation/merge-options.txt        |    9 +-
 git-merge.sh                           |   47 +++++---
 git-pull.sh                            |    4 +-
 t/t7601-merge-ff-options.sh            |  188 ++++++++++++++++++++++++++++++++
 7 files changed, 277 insertions(+), 23 deletions(-)
 create mode 100644 Documentation/fast-forward-options.txt

diff --git a/Documentation/fast-forward-options.txt b/Documentation/fast-forward-options.txt
new file mode 100644
index 0000000..9374aa9
--- /dev/null
+++ b/Documentation/fast-forward-options.txt
@@ -0,0 +1,44 @@
+FAST FORWARD OPTIONS
+--------------------
+
+allow::
+
+	Do not generate a merge commit if the merge resolves as a
+	fast-forward, only update the branch pointer.  This option is
+	equivalent of '--ff' without any argument.  This is the
+	default behavior.
+
+never::
+	Generate a merge commit even if the merge resolves as a
+	fast-forward.  This option is equivalent of '--no-ff'.
+
+If your workflow is always to branch from the special branch
+("master") when working on a topic and merge that back to "master", if
+you happen to have worked only on a single topic and the "master" was
+never advanced during the time you worked on that topic, merging the
+topic back to "master" will result in a fast-forward.  When you look
+back that history, you will not be able to tell where the topic
+started and ended by following the ancestry chain of the "master"
+branch.
+
+Using "never fast forward" policy on such a special branch will be a
+way to make sure that all commits on the first-parent ancestry of that
+special branch will be merges from something else.  From the history
+you can determine where the topic started and ended.
+
+The following shows two branches forked off from "master".  The branch
+"master" have merged in changes from branch "topicA" twice and
+"topicB" once:
+
+------------
+	 o---o---o---o---o  topicA
+	/     \           \
+    ---*-------*-------*---*  master
+      /         \     /
+		 o---o  topicB
+------------
+
+The first merge of topicA or the only merge of topicB would have
+resulted in a fast forward without '--ff=never'.  Topic A consist of
+those commits that can be reached from master^2 without passing
+through any of the first-parent ancestries of master.
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 5924c1a..9cf761d 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -9,7 +9,8 @@ git-merge - Join two or more development histories together
 SYNOPSIS
 --------
 [verse]
-'git-merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]...
+'git-merge' [-n] [--stat] [--no-commit] [--squash]
+	[-s <strategy>]... [--ff[=<fast forward option>]]
 	[-m <msg>] <remote> <remote>...
 'git-merge' <msg> HEAD <remote>...
 
@@ -37,6 +38,9 @@ include::merge-options.txt[]
 	least one <remote>.  Specifying more than one <remote>
 	obviously means you are trying an Octopus.
 
+
+include::fast-forward-options.txt[]
+
 include::merge-strategies.txt[]
 
 
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 66304f0..eb816bc 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -52,6 +52,8 @@ include::pull-fetch-param.txt[]
 
 include::urls-remotes.txt[]
 
+include::fast-forward-options.txt[]
+
 include::merge-strategies.txt[]
 
 DEFAULT BEHAVIOUR
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index f37a776..8f87758 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -42,12 +42,11 @@
 
 --no-ff::
 	Generate a merge commit even if the merge resolved as a
-	fast-forward.
+	fast-forward.  --no-ff is an alias for --ff=never.
 
---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.
+--ff[=<fast forward option>]::
+	Select fast forward option.  --ff without any argument
+	is an alias for --ff=allow which is the default behavior.
 
 -s <strategy>, \--strategy=<strategy>::
 	Use the given merge strategy; can be supplied more than
diff --git a/git-merge.sh b/git-merge.sh
index 69b35d8..91fada7 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -16,7 +16,7 @@ log                  add list of one-line log to merge commit message
 no-log               don't add list of one-line log to merge commit message
 squash               create a single commit instead of doing a merge
 commit               perform a commit if the merge sucesses (default)
-ff                   allow fast forward (default)
+ff?                  fast forward options
 s,strategy=          merge strategy to use
 m,message=           message to be used for the merge commit (if any)
 "
@@ -39,7 +39,7 @@ no_fast_forward_strategies='subtree ours'
 no_trivial_strategies='recursive recur subtree ours'
 use_strategies=
 
-allow_fast_forward=t
+fast_forward=allow
 allow_trivial_merge=t
 squash= no_commit= log_arg=
 
@@ -159,8 +159,6 @@ parse_config () {
 		--log|--no-log)
 			log_arg=$1 ;;
 		--squash)
-			test "$allow_fast_forward" = t ||
-				die "You cannot combine --squash with --no-ff."
 			squash=t no_commit=t ;;
 		--no-squash)
 			squash= no_commit= ;;
@@ -169,11 +167,26 @@ parse_config () {
 		--no-commit)
 			no_commit=t ;;
 		--ff)
-			allow_fast_forward=t ;;
+			case "$2" in
+			allow|never)
+				fast_forward=$2; shift ;;
+			-*)
+				fast_forward=allow ;;
+			*)
+				die "Available fast-forward options are: allow and newer" ;;
+			esac
+			;;
+		--ff=*)
+			fast_forward=${1#--ff=}
+			case "$fast_forward" in
+			allow|never)
+				;;
+			*)
+				die "Available fast-forward options are: allow and newer" ;;
+			esac
+			;;
 		--no-ff)
-			test "$squash" != t ||
-				die "You cannot combine --squash with --no-ff."
-			allow_fast_forward=f ;;
+			fast_forward=never ;;
 		-s|--strategy)
 			shift
 			case " $all_strategies " in
@@ -195,6 +208,8 @@ parse_config () {
 		esac
 		shift
 	done
+	test "$fast_forward" = allow -o "$squash" = "" ||
+		die "You cannot combine --squash with --ff=never"
 	args_left=$#
 }
 
@@ -315,7 +330,7 @@ do
 	do
 		case " $s " in
 		*" $ss "*)
-			allow_fast_forward=f
+			fast_forward=never
 			break
 			;;
 		esac
@@ -341,17 +356,17 @@ case "$#" in
 esac
 echo "$head" >"$GIT_DIR/ORIG_HEAD"
 
-case "$allow_fast_forward,$#,$common,$no_commit" in
-?,*,'',*)
+case "$fast_forward,$#,$common,$no_commit" in
+*,*,'',*)
 	# No common ancestors found. We need a real merge.
 	;;
-?,1,"$1",*)
+*,1,"$1",*)
 	# If head can reach all the merge then we are up to date.
 	# but first the most common case of merging one remote.
 	finish_up_to_date "Already up-to-date."
 	exit 0
 	;;
-t,1,"$head",*)
+allow,1,"$head",*)
 	# Again the most common case of merging one remote.
 	echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)"
 	git update-index --refresh 2>/dev/null
@@ -366,11 +381,11 @@ t,1,"$head",*)
 	dropsave
 	exit 0
 	;;
-?,1,?*"$LF"?*,*)
+*,1,?*"$LF"?*,*)
 	# We are not doing octopus and not fast forward.  Need a
 	# real merge.
 	;;
-?,1,*,)
+*,1,*,)
 	# We are not doing octopus, not fast forward, and have only
 	# one common.
 	git update-index --refresh 2>/dev/null
@@ -488,7 +503,7 @@ done
 # auto resolved the merge cleanly.
 if test '' != "$result_tree"
 then
-    if test "$allow_fast_forward" = "t"
+    if test $fast_forward = allow
     then
         parents=$(git show-branch --independent "$head" "$@")
     else
diff --git a/git-pull.sh b/git-pull.sh
index bf0c298..9e91e75 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -4,7 +4,7 @@
 #
 # Fetch one or more remote refs and merge it/them into the current HEAD.
 
-USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
+USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--ff=<ff-strategy>] [-s strategy]... [<fetch-options>] <repo> <head>...'
 LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
@@ -41,6 +41,8 @@ do
 		no_ff=--ff ;;
 	--no-ff)
 		no_ff=--no-ff ;;
+	--ff=allow|--ff=never)
+		no_ff=$1 ;;
 	-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
 		--strateg=*|--strategy=*|\
 	-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
diff --git a/t/t7601-merge-ff-options.sh b/t/t7601-merge-ff-options.sh
index e499bf5..c7c6d14 100755
--- a/t/t7601-merge-ff-options.sh
+++ b/t/t7601-merge-ff-options.sh
@@ -474,4 +474,192 @@ test_expect_success 'merge c1 with c2 and x1' '
 
 test_debug 'gitk --all'
 
+test_expect_success 'merge x0 with c1 (--squash combined with --ff=allow)' '
+	git reset --hard x0 &&
+	git config branch.master.mergeoptions "" &&
+	test_tick &&
+	git merge c1 --squash --ff=allow &&
+	verify_merge file result.1-5 &&
+	verify_head $x0
+'
+
+test_debug 'gitk --all'
+
+
+test_expect_success 'merge c1 with c2 (--squash combined with --ff=allow)' '
+	git reset --hard c1 &&
+	git config branch.master.mergeoptions "" &&
+	test_tick &&
+	git merge c2 --squash --ff=allow &&
+	verify_merge file result.1-5 &&
+	verify_head $c1 &&
+	git commit &&
+	verify_parents $c1
+'
+
+test_debug 'gitk --all'
+
+
+test_expect_success 'merge c1 with x0 (--no-commit combined with --ff=allow)' '
+	git reset --hard c1 &&
+	git config branch.master.mergeoptions "" &&
+	test_tick &&
+	git merge x0 --no-commit --ff=allow &&
+	verify_merge file result.1-5 &&
+	verify_parents $c1
+'
+
+test_debug 'gitk --all'
+
+
+test_expect_success 'merge x0 with c1 (--no-commit combined with --ff=allow)' '
+	git reset --hard x0 &&
+	git config branch.master.mergeoptions "" &&
+	test_tick &&
+	git merge c1 --no-commit --ff=allow &&
+	verify_merge file result.1-5 &&
+	verify_head $x0
+'
+
+test_debug 'gitk --all'
+
+
+test_expect_success 'merge c1 with c2 (--no-commit combined with --ff=allow)' '
+	git reset --hard c1 &&
+	git config branch.master.mergeoptions "" &&
+	test_tick &&
+	git merge c2 --no-commit --ff=allow &&
+	verify_merge file result.1-5 &&
+	verify_head $c1 &&
+	git commit &&
+	verify_parents $c1
+'
+
+test_debug 'gitk --all'
+
+
+test_expect_success 'merge c1 with x1 (pull --ff=allow)' '
+	git reset --hard c1 &&
+	test_tick &&
+	git pull --ff=allow 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=allow)' '
+	git reset --hard x2 &&
+	test_tick &&
+	git pull --ff=allow clone refs/heads/master &&
+	verify_merge file result.1-5-13 &&
+	verify_parents $x2 $x1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with new repository (pull --ff=allow)' '
+	git reset --hard c1 &&
+	test_tick &&
+	git pull --ff=allow new refs/heads/master &&
+	verify_merge file result.1 &&
+	verify_merge file2 result.9
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge x0 with c1 (--squash combined with --ff=never)' '
+	git reset --hard x0 &&
+	git config branch.master.mergeoptions "" &&
+	test_tick &&
+	test_must_fail git merge c1 --squash --ff=never &&
+	verify_merge file result.1-5 &&
+	verify_head $x0
+'
+
+test_debug 'gitk --all'
+
+
+test_expect_success 'merge c1 with c2 (--squash combined with --ff=never)' '
+	git reset --hard c1 &&
+	git config branch.master.mergeoptions "" &&
+	test_tick &&
+	test_must_fail git merge c2 --squash --ff=never &&
+	verify_merge file result.1 &&
+	verify_head $c1
+'
+
+test_debug 'gitk --all'
+
+
+test_expect_success 'merge c1 with x0 (--no-commit combined with --ff=never)' '
+	git reset --hard c1 &&
+	git config branch.master.mergeoptions "" &&
+	test_tick &&
+	git merge x0 --no-commit --ff=never &&
+	verify_merge file result.1-5 &&
+	verify_head $c1 &&
+	git commit &&
+	verify_parents $c1
+'
+
+test_debug 'gitk --all'
+
+
+test_expect_success 'merge x0 with c1 (--no-commit combined with --ff=never)' '
+	git reset --hard x0 &&
+	git config branch.master.mergeoptions "" &&
+	test_tick &&
+	git merge c1 --no-commit --ff=never &&
+	verify_merge file result.1-5 &&
+	verify_head $x0
+'
+
+test_debug 'gitk --all'
+
+
+test_expect_success 'merge c1 with c2 (--no-commit combined with --ff=never)' '
+	git reset --hard c1 &&
+	git config branch.master.mergeoptions "" &&
+	test_tick &&
+	git merge c2 --no-commit --ff=never &&
+	verify_merge file result.1-5 &&
+	verify_head $c1 &&
+	git commit &&
+	verify_parents $c1
+'
+
+test_debug 'gitk --all'
+
+
+test_expect_success 'merge c1 with x1 (pull --ff=never)' '
+	git reset --hard c1 &&
+	test_tick &&
+	git pull --ff=never clone refs/heads/master &&
+	verify_merge file result.1-13 &&
+	verify_parents $c1 $x1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge x2 with x1 (pull --ff=never)' '
+	git reset --hard x2 &&
+	test_tick &&
+	git pull --ff=never clone refs/heads/master &&
+	verify_merge file result.1-5-13 &&
+	verify_parents $x2 $x1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with new repository (pull --ff=never)' '
+	git reset --hard c1 &&
+	test_tick &&
+	git pull --ff=never new refs/heads/master &&
+	verify_merge file result.1 &&
+	verify_merge file2 result.9
+'
+
+test_debug 'gitk --all'
+
 test_done
-- 
1.5.5.1.499.g878b8

--
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

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux