67

This is the situation:

  1. We created a "private" repo (say our-repo) based off an existing open-source git repo (say source-repo).

  2. We have been developing code and have around 20 merges into our repo. So, the repo moved from "State_Initial" to "State_Current".

Now, for business reasons, we want to hand-over all our development to a third party. Based on some legal issues, the only option is we give them a "single" patch file with all our changes. That is a squashed patch between "State_Initial" and "State_Current".

I looked around, and found

git format-patch -X

But, it generates "n" .patch files.

Is there a way to create a single patch file, so that if we create a repo based off "source-repo" and apply the patch, it takes us to "State_Current"?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
SimpleCoder
  • 1,101
  • 2
  • 10
  • 21

8 Answers8

99

The following command creates a single .patch file that contains multiple commits.

git format-patch cc1dde0dd^..6de6d4b06 --stdout > foo.patch

You can then apply it like so:

git am foo.patch

Note: Be sure to use ^.. instead of .. if you want the first commit SHA to be included.

aleclarson
  • 18,087
  • 14
  • 64
  • 91
  • 35
    **Warning:** This produces one file containing multiple separate patches. It doesn't squash the history down as requested in the question. If there are sensitive commits in the middle they'll be exposed. – John Kugelman Nov 17 '19 at 19:42
  • 4
    If you want to squash multiple commits into a patch then you can squash them by rebasing and then can make the squashed commit as a patch. – Priyanshu May 13 '21 at 15:27
  • 6
    Just to clarify (maybe for Windows users only?). This command resulted in an error for me when doing the "git am foo.patch" part. I got a "Patch: format detection failed" error. What worked was to create the patch using the command: git format-patch cc1dde0dd^..6de6d4b06 --output=foo.patch – ThermoX May 21 '21 at 13:14
  • @ThermoX Were you using PowerShell? From my testing it seems that If you run the --stdout version of the command from PowerShell you'll get a UTF-16 LE BOM-encoded file with Windows line endings instead of a UTF-8–encoded file with UNIX line endings, and that's probably why the patch doesn't apply cleanly. So use Git Bash or cmd.exe for best results. – Jordan Bradford Aug 03 '21 at 18:41
  • 1
    @Jordan Bradford Yes, I was using PowerShell with the posh-git extension. I probably should have mentioned that in my original comment. Looking at the encoding, I *am* getting UTF-8 for the --output version, while as I am getting UCS-2 LE BOM for the --stdout. When running this under a cmd shell, you are correct. They both come out at UTF-8. However, I then need to use ^^ instead of ^. So I just keep using PowerShell and the --output version, which works fine. But glad to know the reason --stdout wasn't working there. – ThermoX Sep 17 '21 at 12:13
  • 3
    For the curious, this is the official git [docs](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges) for selecting ranges of commits based on `..` and `^` syntax – Addison Klinke Dec 08 '21 at 17:07
  • 1
    As explained in this [answer](https://stackoverflow.com/a/4624204/7446465), `git format-patch` may be preferred over `git diff` since it includes metadata for the changes (i.e. committer identity, timestamps, commit messages, etc) – Addison Klinke Dec 08 '21 at 17:12
  • @AddisonKlinke: Note that precisely this can be what is desired, when the goal is to see how the code is different. – O. R. Mapper Nov 29 '22 at 13:52
17

You can generate a single-file patch between <COMMIT1> and <COMMIT2> like this:

With commit history

Generate patch:

git format-patch <COMMIT1>^..<COMMIT2> --stdout > commits.patch

Apply patch:

git am commits.patch

Without commit history

Generate patch:

git diff <COMMIT1>^..<COMMIT2> > diff.patch

Apply patch:

git apply diff.patch

Notes

  • On Windows, you'd better run the above command in Git Bash. If you use PowerShell and get error when apply the patch, you can try to convert the patch file to UTF-8 encoding.

  • If you for some reason want to include the first commit into the patch. You can do:

    git format-patch --root --stdout > all.patch
    

    or

    git diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD > diff.patch
    

    The 4b825dc642cb6eb9a060e54bf8d69288fbee4904 represents the "empty tree", see How to get Git diff of the first commit?

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Le0jc
  • 171
  • 1
  • 5
3

Create a new branch named squashed that has a single squashed commit. This branch will have the exact same contents as your normal branch but none of the history.

Look it over and if you're satisfied, use format-patch to create a patch file.

$ git checkout -b squashed $(git commit-tree HEAD^{tree} -m 'Squashed history')
$ git format-patch --root HEAD

This is a non-destructive operation and you can switch right back to your normal development branch afterwards. You can tag the squashed branch to save a reference to what you e-mailed them, or use branch -D to delete it if you no longer need it.

$ git branch -D squashed
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • 3
    How do you specify the start of the commit range, though? It looks like this captures _all_ of the files of the branch in a single commit, going back to the initial commit. – GuyPaddock Dec 10 '19 at 18:42
3

You can use git diff:

git diff 0f3063094850 > ./test.patch

Dillion Wang
  • 362
  • 3
  • 18
  • Useful, but a bit laconic. I think this means, "_To get a patch of the diff between your current status and a specific commit, execute `git diff [otherCommitSHA] > test.patch`_"-- as that's what this seems to do. That command doesn't appear to create the same patch format as the patch command, but you can learn about exactly [what patch formats can be used here](https://git-scm.com/docs/git-diff#_generating_patch_text_with_p). – ruffin Oct 03 '22 at 15:12
2

"The last 10 patches from head in a single patch files:"

git format-patch -10 HEAD --stdout > 0001-last-10-commits.patch

Source: https://stackoverflow.com/a/16172120/9478470 an answer to similar question: How can I generate a Git patch for a specific commit?

Piotr S.
  • 21
  • 1
0

If for whatever reason you do not wish to create a throwaway branch and squash all your commits between state_initial and state_current and then use git format-patch, there is an alternative. If you branched out from state_initial and your branch is rebased on top of the source branch:

git format-patch source_branch <patch_file_name>

When you do git am <patch_file_name>, it will not reconstruct your entire commit chain. But this patch file will be a sequential list of changes. If you have changed and changed back things across multiple commits, it can still be visible if someone examines the patch file.

Subhamoy S.
  • 6,566
  • 10
  • 37
  • 53
-1

use this command:

$ git format-patch -n <commit string>

-n means how many commits you want to generate for this patch, and means which commit you want to generate from.

Siwei
  • 19,858
  • 7
  • 75
  • 95
-2

git format-patch can take a revision range as an argument. See git help format-patch:

SYNOPSIS

git format-patch [-k] [(-o|--output-directory) <dir> | --stdout]
                   ... (many flags omitted)
                   [--progress]
                   [<common diff options>]
                   [ <since> | <revision range> ] 

DESCRIPTION

There are two ways to specify which commits to operate on.

  1. A single commit, <since>, specifies that the commits leading to the tip of the current branch that are not in the history that leads to the <since> to be output.

  2. Generic <revision range> expression (see "SPECIFYING REVISIONS" section in gitrevisions(7)) means the commits in the specified range.

For example, the following command generates a patch for the last three commits on the current branch:

git format-patch HEAD~3..HEAD
Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
  • 6
    That generates one `.patch` file per commit. The asker wants a single `.patch` file that contains all commits in the given range. – aleclarson Jan 28 '19 at 23:35