tags:

views:

323

answers:

2

When I use git format-patch, it doesn't seem to include merges. How can I perform a merge and then e-mail it to someone as a set of patches?

For example, let's say that I merge two branches and perform another commit on top of the merge:

git init

echo "initial file" > test.txt
git add test.txt
git commit -m "Commit A"

git checkout -b foo master
echo "foo" > test.txt
git commit -a -m "Commit B"

git checkout -b bar master
echo "bar" > test.txt
git commit -a -m "Commit C"

git merge foo
echo "foobar" > test.txt
git commit -a -m "Commit M"

echo "2nd line" >> test.txt
git commit -a -m "Commit D"

This creates the following tree:

    B
  /   \
A       M - D 
  \   /
    C

Now I try to checkout the initial commit and replay the above changes:

git checkout -b replay master
git format-patch --stdout master..bar | git am -3

This produces a merge conflict. In this scenario, git format-patch master..bar only produces 3 patches, omitting "Commit M". How do I deal with this?

-Geoffrey Lee

+1  A: 

Note that a bare git log -p won't show any patch content for the merge commit "M", but using git log -p -c does coax it out. However, git format-patch doesn't accept any arguments analogous to the -c (or --combined, -cc) accepted by git log.

I too remain stumped.

seh
+1  A: 

If you examine the content of the first two patches you'll see the issue:

diff --git a/test.txt b/test.txt
--- a/test.txt
+++ b/test.txt
@@ -1 +1 @@
-initial file
+foo

diff --git a/test.txt b/test.txt
index 7c21ad4..5716ca5 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1 @@
-initial file
+bar

from the perspective of the branch you were working on at the time (foo and bar) both of these commits have removed the "initial file" line and replaced it with something else entirely. AFAIK, there's no way to avoid this kind of conflict when you generate a patch of a non-linear progression with overlapping changes (your branch commits B and C in this case).

People normally use patches to add a single feature or bug fix off a known good prior work state -- the patch protocol is simply not sophisticated enough to handle merge history like Git does natively. If you want someone to see your merge then you need to push/pull between branches not drop back diff/patch.

omnisis
It's tough to prove a negative but like seh, I took a whack at this problem and I get the feeling you are right.
jhs
Yes, I understand the issues with the patch files. I was hoping there would be a workaround, because one would assume that the Linux or Git projects have encountered similar situations, and they rely entirely on submitting patches via e-mail rather than push/pull. I'll ping the Git mailing list and see if they have any additional feedback. Thanks.
geofflee
if you had instead replaced the merge line above with $ git merge --squash foo$ git commit -a -m"Commit M"your patch would have applied cleanly...
omnisis
Yes, I could have squashed the commits, but that would destroy history and generally isn't a good way to approach distributed version control, imho. Fortunately, someone on the Git mailing list pointed me to "git bundle", which allows you to package and transfer Git objects manually. This seems to be the best solution.
geofflee
The aforementioned suggestion by Jeff King regarding `git bundle`: http://thread.gmane.org/gmane.comp.version-control.git/140321/focus=140377
seh