47

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
  • 330,190
  • 66
  • 504
  • 555
SimpleCoder
  • 851
  • 2
  • 8
  • 18

6 Answers6

77

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
  • 17,077
  • 12
  • 61
  • 83
  • 22
    **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
  • 3
    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
  • 2
    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
  • 1
    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
  • 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
2

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
  • 330,190
  • 66
  • 504
  • 555
  • 1
    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
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,222
  • 10
  • 35
  • 50
0

You can use git diff:

git diff 0f3063094850 > ./test.patch

Dillion Wang
  • 302
  • 3
  • 17
0

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
  • 17,128
  • 4
  • 67
  • 86
-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
  • 27,337
  • 11
  • 91
  • 127
  • 5
    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