threads / patch / 43412

patchRe: [PATCH] git-merge: preserve and merge local changes when doing fast forward

Subject: Re: [PATCH] git-merge: preserve and merge local changes when doing fast forward

## tl;dr

3 messages between Nov 30, 2006 and Nov 30, 2006. Diffs are folded; open one to read it.

replies: 2people: 3as markdown or json

Junio C Hamano· Nov 30, 2006, 03:02 UTC · lore

[PATCH] git-merge: preserve and merge local changes when doing fast forward

The idea and the logic are identical to what "checkout -m" does when switching the branches. Instead of refusing the two-way merge, perform the three-way merge between the old head, the working tree and the new head, and leave the (potentially conflicted) merge result in the working tree.

If this turns out to be a sane thing to do, we probably should make the common logic between "checkout -m" and this into a built-in command.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 * I am not sure if this is worth doing in general; it can leave
   a huge mess if the conflict with the merge and the local
   change is too extensive and does not give a good way to
   recover from it, and that is why we require an explicit "-m"
   to "git checkout" for this behaviour.  Perhaps we would want
   a new option to git-merge to allow preserving the local
   changes, but the obvious candidate -m is taken for something
   else per recent popular request X-<...
 git-merge.sh |   42 +++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 41 insertions(+), 1 deletions(-)
Show changes to git-merge.sh +41 −1
diff --git a/git-merge.sh b/git-merge.sh
index 75af10d..324991f 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -91,6 +91,46 @@ finish () {
 	esac
 }
 
+merge_local_changes () {
+	merge_error=$(git-read-tree -m -u $1 $2 2>&1) || (
+		# Match the index to the working tree, and do a three-way.
+		git diff-files --name-only |
+		git update-index --remove --stdin &&
+		work=`git write-tree` &&
+		git read-tree --reset -u $2 &&
+		git read-tree -m -u --aggressive $1 $2 $work || exit
+
+		echo >&2 "Carrying local changes forward."
+		if result=`git write-tree 2>/dev/null`
+		then
+			echo >&2 "Trivially automerged."
+		else
+			git merge-index -o git-merge-one-file -a
+		fi
+
+		# Do not register the cleanly merged paths in the index
+		# yet; this is not a real merge before committing, but
+		# just carrying the working tree changes along.
+		unmerged=`git ls-files -u`
+		git read-tree --reset $2
+		case "$unmerged" in
+		'')	;;
+		*)
+			(
+				z40=0000000000000000000000000000000000000000
+				echo "$unmerged" |
+				sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
+				echo "$unmerged"
+			) | git update-index --index-info
+
+			echo >&2 "Conflicts in locally modified files:"
+			git diff --name-only --diff-filter=U >&2
+			;;
+		esac
+		exit 0
+	)
+}
+
 case "$#" in 0) usage ;; esac
 
 rloga= have_message=
@@ -264,7 +304,7 @@ f,*)
 	echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
 	git-update-index --refresh 2>/dev/null
 	new_head=$(git-rev-parse --verify "$1^0") &&
-	git-read-tree -u -v -m $head "$new_head" &&
+	merge_local_changes $head $new_head &&
 	finish "$new_head" "Fast forward"
 	dropsave
 	exit 0
-- 
1.4.4.1.gc419
Carl Worth· Nov 30, 2006, 07:32 UTC · re: Junio C Hamano · lore
On Wed, 29 Nov 2006 19:02:33 -0800, Junio C Hamano wrote:
Show 5 quoted lines
> The idea and the logic are identical to what "checkout -m" does
> when switching the branches.  Instead of refusing the two-way
> merge, perform the three-way merge between the old head, the
> working tree and the new head, and leave the (potentially
> conflicted) merge result in the working tree.

This looks very appealing to me. I know I've often been frustrated when git refuses to fast-forward just because I have dirty state, (and stashing the diff manually into a patch file, then re-applying it after fast forward is really annoying---getting work done in spite of the tool and not because of it).

>  * I am not sure if this is worth doing in general; it can leave
>    a huge mess if the conflict with the merge and the local
>    change is too extensive and does not give a good way to
>    recover from it,

As I said above it seems very reasonable to me. As for the problem you mention here, isn't "no good way to recover from it" the real problem, and not this merge possibility? And is there enough information in the index file such that one could implement a way to recover from this?

-Carl
Johannes Schindelin· Nov 30, 2006, 10:32 UTC · re: Junio C Hamano · lore
Hi,
On Wed, 29 Nov 2006, Junio C Hamano wrote:
> Perhaps we would want a new option to git-merge to allow preserving the 
> local changes, but the obvious candidate -m is taken for something else 
> per recent popular request X-<...

How about "-k|--keep-changes" or "-l|--local-changes", or "-p|--preserve-changes"?

BTW from a user's perspective, this operation _is_ a merge, not a checkout. So, IMHO the compatibility with that rarely used option of git-checkout is neglectable.

Ciao,

← back to recent threads