Subject: [PATCH] git-merge: add option --no-ff

From: Lars Hjemli <[email-masked]>
Date: Mon, 17 Sep 2007 18:23:04 +0200

This option forces fast-forward merges to create a "true" merge commit,
i.e. a commit with multiple parents.

Although a fast-forward merge would normally be the right thing to do with
git branches, it is suboptimal when operating on git-svn branches since it
makes 'git-svn dcommit' fail to recognize the correct upstream subversion
branch. But performing such a merge with --no-ff specified will both make
git-svn dcommit recognize the correct upstream and create the logically
correct history in subversion (the merge performed in git will be recorded
as a single revision in subversion, not as a series of revisions seemingly
cherry-picked from the merged branch).

Signed-off-by: Lars Hjemli <[email-masked]>
---

When updating git-svn.txt, I noticed that we might want to update the 
section "DESIGN PHILOSOPHY". Eric?

 Documentation/git-svn.txt       |   13 +++++++++++++
 Documentation/merge-options.txt |    5 +++++
 git-merge.sh                    |   13 +++++++++++--
 t/t6028-merge-up-to-date.sh     |   25 +++++++++++++++++++++++++
 4 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index be2e34e..c510c21 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -475,6 +475,19 @@ use 'git-svn rebase' to update your work branch instead of 'git pull' or
 when committing into SVN, which can lead to merge commits reversing
 previous commits in SVN.
 
+If you use 'git-svn dcommit' to commit your local work to the upstream
+subversion branch, merge commits are usually handled correctly, i.e.
+git-svn will only follow the first parent of each merge commit and create
+a single subversion revision for each of them. An exception is when two
+subversion branches has been merged locally and the merge ended up as a
+fast-forward operation. This will make git-svn belive that there are no
+local changes to dcommit. To work around this issue, one can redo the
+merge using the --no-ff option:
+
+       $ git reset --hard HEAD@{1}   ## undo the fast-forward merge
+       $ git merge --no-ff <branch>
+
+
 DESIGN PHILOSOPHY
 -----------------
 Merge tracking in Subversion is lacking and doing branched development
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index d64c259..b34b888 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -25,3 +25,8 @@
 	If there is no `-s` option, a built-in list of strategies
 	is used instead (`git-merge-recursive` when merging a single
 	head, `git-merge-octopus` otherwise).
+
+--no-ff::
+	Force the creation of a merge commit even when the merge would
+	have resolved as a fast-forward operation. See gitlink:git-svn[1]
+	for a use-case for this option.
diff --git a/git-merge.sh b/git-merge.sh
index 3a01db0..13b98e6 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 <strategy>] [-m=<merge-message>] <commit>+'
+USAGE='[-n] [--summary] [--no-commit] [--no-ff] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+'
 
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
@@ -165,6 +165,10 @@ do
 		merge_msg="$1"
 		have_message=t
 		;;
+	--no-ff)
+		no_ff=t
+		no_fast_forward_strategies=$all_strategies
+		;;
 	-*)	usage ;;
 	*)	break ;;
 	esac
@@ -444,7 +448,12 @@ done
 # auto resolved the merge cleanly.
 if test '' != "$result_tree"
 then
-    parents=$(git show-branch --independent "$head" "$@" | sed -e 's/^/-p /')
+    if test $no_ff = 't'
+    then
+        parents=$(git rev-parse "$head" "$@" | sed -e 's/^/-p /')
+    else
+        parents=$(git show-branch --independent "$head" "$@" | sed -e 's/^/-p /')
+    fi
     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/t6028-merge-up-to-date.sh b/t/t6028-merge-up-to-date.sh
index f8f3e3f..afd74e2 100755
--- a/t/t6028-merge-up-to-date.sh
+++ b/t/t6028-merge-up-to-date.sh
@@ -10,12 +10,14 @@ test_expect_success setup '
 	test_tick &&
 	git commit -m initial &&
 	git tag c0 &&
+	c0=$(git rev-parse c0)
 
 	echo second >file &&
 	git add file &&
 	test_tick &&
 	git commit -m second &&
 	git tag c1 &&
+	c1=$(git rev-parse c1)
 	git branch test
 '
 
@@ -41,6 +43,16 @@ test_expect_success 'merge -s recursive fast-forward' '
 
 '
 
+test_expect_success 'merge -s recursive --no-ff' '
+
+	git reset --hard c0 &&
+	test_tick &&
+	git merge -s recursive --no-ff c1 &&
+	test $c0 = $(git rev-parse HEAD^1) &&
+	test $c1 = $(git rev-parse HEAD^2)
+
+'
+
 test_expect_success 'merge -s ours up-to-date' '
 
 	git reset --hard c1 &&
@@ -63,6 +75,19 @@ test_expect_success 'merge -s ours fast-forward' '
 
 '
 
+test_expect_success 'merge -s ours --no-ff' '
+
+	git reset --hard c0 &&
+	test_tick &&
+	git merge -s ours --no-ff c1 &&
+	expect=$(git rev-parse c0^{tree}) &&
+	current=$(git rev-parse HEAD^{tree}) &&
+	test "$expect" = "$current" &&
+	test $c0 = $(git rev-parse HEAD^1) &&
+	test $c1 = $(git rev-parse HEAD^2)
+
+'
+
 test_expect_success 'merge -s subtree up-to-date' '
 
 	git reset --hard c1 &&
-- 
1.5.3.1.92.g2f5e