90

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"?

8 Answers 8

131

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.

Sign up to request clarification or add additional context in comments.

8 Comments

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.
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.
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
@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.
For the curious, this is the official git docs for selecting ranges of commits based on .. and ^ syntax
|
39

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?

Comments

5

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

1 Comment

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.
3

You can use git diff:

git diff 0f3063094850 > ./test.patch

1 Comment

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.
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?

Comments

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.

Comments

-3

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

1 Comment

That generates one .patch file per commit. The asker wants a single .patch file that contains all commits in the given range.
-3

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.

1 Comment

This will generate one patch per commit

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.