Git: How to create patches for a merge?

Viewed 29322

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

6 Answers

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.

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.

With Git 2.32 (Q2 2021), the "format-patch" documentation is clearer: it skips merges.

See commit 8e0601f (01 May 2021) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit 270f8bf, 11 May 2021)

docs/format-patch: mention handling of merges

Signed-off-by: Jeff King

Format-patch doesn't have a way to format merges in a way that can be applied by git-am (or any other tool), and so it just omits them.
However, this may be a surprising implication for users who are not well versed in how the tool works.
Let's add a note to the documentation making this more clear.

git format-patch now includes in its man page:

Prepare each non-merge commit with its "patch" in

git format-patch now includes in its man page:

CAVEATS

Note that format-patch will omit merge commits from the output, even if they are part of the requested range.
A simple "patch" does not include enough information for the receiving end to reproduce the same merge commit.

Related