ruffin
ruffin

Reputation: 17453

How can I `git format-patch SHAx..SHAy` to output to a single patch file, not one per commit?

Conventional patch

git format-patch OlderSHA..NewerSHA normally creates one patch file per commit between OlderSHA and NewerSHA, exclusive of OlderSHA (it's the base for the changes) and inclusive of NewerSHA.

For instance, in this "real world" example, there are five commits between SHAs and so git format-patch makes five patch files.

git format-patch 3caf9c2..0aa9968
0001-About-as-far-as-you-get-adding-new-module-to-RCP-wit.patch
0002-Add-a-trivial-service.patch
0003-Rename-files-and-add-lazy-module-class.patch
0004-Add-WCS-shim-and-add-to-testRouteMap.patch
0005-Add-module-to-routing-to-enable-lazy-loading.patch

Problem statement

I would like to produce a single file representing all of the changes between OlderSHA and NewerSHA that could be applied in a single command. I'm not looking to preserve state between the two SHAs; I only want the final differences stored in the patch file.

git format-patch --single-file-option? 3caf9c2..0aa9968
EVERYTHING_BETWEEN_SHAs.patch

I've looked over the docs on format-patch a bit and if this is an option, I've missed it.

The problem is a little hard to google, as "single file", even "output single patch file" seem to converge on "I want patches that related to a single file" results instead.

Note that I also don't simply want to concatenate five files into one; I want the difference between the two commits in a single file with a single entry or section in the resulting file for each file's changes. (If foo changes to bar in commit 2 and back to foo in commit 5, I don't want it to appear in SuperPatch.patch)

Does this option exist?

Upvotes: 0

Views: 176

Answers (2)

ElpieKay
ElpieKay

Reputation: 30858

I'll add some notes to @jthill answer.

"All of the changes between OlderSHA and NewerSHA" is ambiguous. Considering such a scenario,

git init foo
cd foo
touch a.txt
git add .
git commit -mroot
git branch new
echo hello > a.txt
git commit -am'hello'
git checkout new
echo world > a.txt
git commit -am'world'
git checkout master

The log graph would be like,

* d1f8ac1 (new) world
| * dd5329d (HEAD -> master) hello
|/  
* 8db55b4 root

git format-pach master new would create one patch for d1f8ac1 only, whose diff is between 8db55b4 and d1f8ac1, as if generated by git diff master...new (three dots). It's different from the diff between dd5329d and d1f8ac1, as if generated by git diff master..new (two dots).

When OlderSHA is an ancestor of NewerSHA, the 2 diffs are the same. When OlderSHA is diverged from NewerSHA, as described in the above scenario, the 2 diffs are different.

There is also a special case, in which OlderSHA and NewerSHA are unrelated. They don't share any common ancestor. git format-patch OlderSHA NewerSHA generates patches for all commits reachable from both OlderSHA and NewerSHA. git diff OlderSHA..NewerSHA and git diff OlderSHA...NewerSHA generate the same diff.

When you need the ... diff, use

git format-patch OlderSHA..$(git commit-tree -m - \
                       -p `git merge-base OlderSHA NewerSHA` NewerSHA^{tree})

When you need the .. diff, use

git format-patch OlderSHA..$(git commit-tree -m - -p OlderSHA NewerSHA^{tree})

Here git format-patch OlderSHA..$() could also be git format-patch -1 $(). And for the special case, use the .. diff way, or use the ... diff way without -p $(git merge-base OlderSHA NewerSHA)

git commit-tree creates a commit, whose message log is specified by -m, whose parent is specified by -p and whose tree is specified by NewerSHA^{tree}. It's like to make a new commit based on OlderSHA or the merge-base of OlderSHA and NewerSHA, and the commit has the exactly same code with NewerSHA.

Upvotes: 0

jthill
jthill

Reputation: 60235

Probably the most compact way to get this in general would be

git format-patch OlderSHA..$(git commit-tree -m - \
                           -p `git merge-base OlderSHA NewerSHA` NewerSHA^{tree})

with the merge-base needed in case OlderSHA isn't strictly an ancestor. If it is, you can lose it and do

git format-patch OlderSHA..$(git commit-tree -m - -p OlderSHA NewerSHA^{tree})

and if you want a nicer commit message in the patch you can use multiple -m's or lose the -m and feed the full message in through commit-tree's stdin.

Upvotes: 1

Related Questions