ChatGPT解决这个技术问题 Extra ChatGPT

How can I generate a Git patch for a specific commit?

I need to write a script that creates patches for a list of SHA-1 commit numbers.

I tried using git format-patch <the SHA1>, but that generated a patch for each commit since that SHA-1 value. After a few hundred patches were generated, I had to kill the process.

Is there a way to generate a patch only for the specific SHA-1 value?


P
Peter Mortensen

Try:

git format-patch -1 <sha>

or

git format-patch -1 HEAD

According to the documentation link above, the -1 flag tells Git how many commits should be included in the patch;

- Prepare patches from the topmost commits.

Apply the patch with the command:

git am < file.patch

Applying the patch: git apply --stat file.patch # show stats. git apply --check file.patch # check for error before applying. git am < file.patch # apply the patch finally.
It does not seem to work if the last commit is a merge from another branch.
Use git am -3 < file.patch to apply using a three-way merge which will let you resolve conflicts using git mergetool afterward (or editing manually) found here.
This command also works for just a specific file(s) from the commit: git format-patch -1 <sha> path/to/file.js This will create a patch only containing the diffs for the file.js
It would be very helpful if you explained the purpose of the -1
F
Farid

For generating the patches from the topmost commits from a specific SHA-1 hash:

git format-patch -<n> <SHA-1> --stdout > <name_of_patch_file>.patch

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

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

can you please be kind enough to provide an example for the first command
git format-patch -1 HEAD will generate patch for the most recent commit
pardon me for asking this, so when it is -2 it generate patches for most recent 2 commits is it, and one more thing to for clarification is the command got format-patch -2 HEAD is same as the line git format-patch HEAD~2
P
Peter Mortensen

Say you have commit id 2 after commit 1 you would be able to run:

git diff 2 1 > mypatch.diff

where 2 and 1 are SHA-1 hashes.


Thank you dookehster for the reply. That means I need the script to find the commits that preceded those I am interested in. I was hoping that I could avoid that.
@elle, no, you don't -- git diff hash^ hash . the "hash^" give the preceded commit. (but, of course, manojlds's answer is better)
git show HEAD > mypatch.diff while you're on the commit should do the same.
@dookehester is it correct or is it the other way, git diff 1 2
This will fail to include any binary files in the diff.
3
3 revs, 3 users 78%

This command (as suggested already by @Naftuli Tzvi Kay),

git format-patch -1 HEAD

Replace HEAD with a specific hash or range.

will generate the patch file for the latest commit formatted to resemble the Unix mailbox format.

- - Prepare patches from the topmost commits.

Then you can reapply the patch file in a mailbox format by:

git am -3k 001*.patch

See: man git-format-patch.


Thanks! I think it's worth noting that applying the patch will create a commit with a commit message prefixed by [PATCH]. That's easy to fix though
Phenomenal. OP, you haven't accepted this, because...? @MikeS No, it doesn't, anymore than any other git-formatted patch does, at least if the user applies it in the correct way.
@MikeS I didn't really investigate why, but leaving out the -k flag (git am -3) fixed this form me (no PATCH[0/10] commit messages). Git version 2.20.1.windows.1
i
iamantony
git format-patch commit_Id~1..commit_Id  
git apply patch-file-name

Fast and simple solution.


Also do not forget to call git apply --check patch-file-name before applying a patch. This will help to avoid problems.
V
VonC

If you want to be sure the (single commit) patch will be applied on top of a specific commit, you can use the new git 2.9 (June 2016) option git format-patch --base

git format-patch --base=COMMIT_VALUE~ -M -C COMMIT_VALUE~..COMMIT_VALUE

# or
git format-patch --base=auto -M -C COMMIT_VALUE~..COMMIT_VALUE

# or
git config format.useAutoBase true
git format-patch -M -C COMMIT_VALUE~..COMMIT_VALUE

See commit bb52995, commit 3de6651, commit fa2ab86, commit ded2c09 (26 Apr 2016) by Xiaolong Ye (``).
(Merged by Junio C Hamano -- gitster -- in commit 72ce3ff, 23 May 2016)

format-patch: add '--base' option to record base tree info

Maintainers or third party testers may want to know the exact base tree the patch series applies to. Teach git format-patch a '--base' option to record the base tree info and append it at the end of the first message (either the cover letter or the first patch in the series). The base tree info consists of the "base commit", which is a well-known commit that is part of the stable part of the project history everybody else works off of, and zero or more "prerequisite patches", which are well-known patches in flight that is not yet part of the "base commit" that need to be applied on top of "base commit" in topological order before the patches can be applied. The "base commit" is shown as "base-commit: " followed by the 40-hex of the commit object name. A "prerequisite patch" is shown as "prerequisite-patch-id: " followed by the 40-hex "patch id", which can be obtained by passing the patch through the "git patch-id --stable" command.

Git 2.23 (Q3 2019) will improve that, because the "--base" option of "format-patch" computed the patch-ids for prerequisite patches in an unstable way, which has been updated to compute in a way that is compatible with "git patch-id --stable".

See commit a8f6855, commit 6f93d26 (26 Apr 2019) by Stephen Boyd (akshayka).
(Merged by Junio C Hamano -- gitster -- in commit 8202d12, 13 Jun 2019)

format-patch: make --base patch-id output stable

We weren't flushing the context each time we processed a hunk in the patch-id generation code in diff.c, but we were doing that when we generated "stable" patch-ids with the 'patch-id' tool. Let's port that similar logic over from patch-id.c into diff.c so we can get the same hash when we're generating patch-ids for 'format-patch --base=' types of command invocations.

Before Git 2.24 (Q4 2019), "git format-patch -o <outdir>" did an equivalent of "mkdir <outdir>" not "mkdir -p <outdir>", which is being corrected.

See commit edefc31 (11 Oct 2019) by Bert Wesarg (bertwesarg).
(Merged by Junio C Hamano -- gitster -- in commit f1afbb0, 18 Oct 2019)

format-patch: create leading components of output directory Signed-off-by: Bert Wesarg

'git format-patch -o ' did an equivalent of 'mkdir ' not 'mkdir -p ', which is being corrected.

Avoid the usage of 'adjust_shared_perm' on the leading directories which may have security implications. Achieved by temporarily disabling of 'config.sharedRepository' like 'git init' does.

With Git 2.25 (Q1 2020), "git rebase" did not work well when format.useAutoBase configuration variable is set, which has been corrected.

See commit cae0bc0, commit 945dc55, commit 700e006, commit a749d01, commit 0c47e06 (04 Dec 2019) by Denton Liu (Denton-L).
(Merged by Junio C Hamano -- gitster -- in commit 71a7de7, 16 Dec 2019)

rebase: fix format.useAutoBase breakage Reported-by: Christian Biesinger Signed-off-by: Denton Liu With format.useAutoBase = true, running rebase resulted in an error: fatal: failed to get upstream, if you want to record base commit automatically, please use git branch --set-upstream-to to track a remote branch. Or you could specify base commit by --base= manually error: git encountered an error while preparing the patches to replay these revisions: ede2467cdedc63784887b587a61c36b7850ebfac..d8f581194799ae29bf5fa72a98cbae98a1198b12 As a result, git cannot rebase them. Fix this by always passing --no-base to format-patch from rebase so that the effect of format.useAutoBase is negated.

With Git 2.29 (Q4 2020), "git format-patch"(man) learns to take "whenAble" as a possible value for the format.useAutoBase configuration variable to become no-op when the automatically computed base does not make sense.

See commit 7efba5f (01 Oct 2020) by Jacob Keller (jacob-keller).
(Merged by Junio C Hamano -- gitster -- in commit 5f8c70a, 05 Oct 2020)

format-patch: teach format.useAutoBase "whenAble" option Signed-off-by: Jacob Keller

The format.useAutoBase configuration option exists to allow users to enable '--base=auto' for format-patch by default. This can sometimes lead to poor workflow, due to unexpected failures when attempting to format an ancient patch: $ git format-patch -1 fatal: base commit shouldn't be in revision list This can be very confusing, as it is not necessarily immediately obvious that the user requested a --base (since this was in the configuration, not on the command line). We do want --base=auto to fail when it cannot provide a suitable base, as it would be equally confusing if a formatted patch did not include the base information when it was requested. Teach format.useAutoBase a new mode, "whenAble". This mode will cause format-patch to attempt to include a base commit when it can. However, if no valid base commit can be found, then format-patch will continue formatting the patch without a base commit. In order to avoid making yet another branch name unusable with --base, do not teach --base=whenAble or --base=whenable. Instead, refactor the base_commit option to use a callback, and rely on the global configuration variable auto_base. This does mean that a user cannot request this optional base commit generation from the command line. However, this is likely not too valuable. If the user requests base information manually, they will be immediately informed of the failure to acquire a suitable base commit. This allows the user to make an informed choice about whether to continue the format. Add tests to cover the new mode of operation for --base.

git config now includes in its man page:

format-patch by default. Can also be set to "whenAble" to allow enabling --base=auto if a suitable base is available, but to skip adding base info otherwise without the format dying.

With Git 2.30 (Q1 2021), "git format-patch --output=there"(man) did not work as expected and instead crashed.

The option is now supported.

See commit dc1672d, commit 1e1693b, commit 4c6f781 (04 Nov 2020) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit 5edc8bd, 18 Nov 2020)

format-patch: support --output option Reported-by: Johannes Postler Signed-off-by: Jeff King

We've never intended to support diff's --output option in format-patch. And until baa4adc66a (parse-options: disable option abbreviation with PARSE_OPT_KEEP_UNKNOWN, 2019-01-27, Git v2.22.0-rc0), it was impossible to trigger. We first parse the format-patch options before handing the remainder off to setup_revisions(). Before that commit, we'd accept "--output=foo" as an abbreviation for "--output-directory=foo". But afterwards, we don't check abbreviations, and --output gets passed to the diff code. This results in nonsense behavior and bugs. The diff code will have opened a filehandle at rev.diffopt.file, but we'll overwrite that with our own handles that we open for each individual patch file. So the --output file will always just be empty. But worse, the diff code also sets rev.diffopt.close_file, so log_tree_commit() will close the filehandle itself. And then the main loop in cmd_format_patch() will try to close it again, resulting in a double-free. The simplest solution would be to just disallow --output with format-patch, as nobody ever intended it to work. However, we have accidentally documented it (because format-patch includes diff-options). And it does work with "git log"(man) , which writes the whole output to the specified file. It's easy enough to make that work for format-patch, too: it's really the same as --stdout, but pointed at a specific file. We can detect the use of the --output option by the "close_file" flag (note that we can't use rev.diffopt.file, since the diff setup will otherwise set it to stdout). So we just need to unset that flag, but don't have to do anything else. Our situation is otherwise exactly like --stdout (note that we don't fclose() the file, but nor does the stdout case; exiting the program takes care of that for us).


M
Maik Lowrey

Create a git patch using commit-id

$ git format-patch -1 commit-id

This command create patch with following file name

0001-commit-message.patch

To apply the patch:

$ git am 0001-commit-message.patch

Could you clarify the -1 argument? I was unable to find a reference to it in the docs or online.
@brainbag I found it in the manpage: -<n> Prepare patches from the topmost <n> commits
If you change -1 to any >1 number you will still get patch per commit. How does this even answer the question?
P
Peter Mortensen

To generate a patch from a specific commit (not the last commit):

git format-patch -M -C COMMIT_VALUE~1..COMMIT_VALUE

P
Peter Mortensen

If you just want diff the specified file, you can use:

git diff master 766eceb -- connections/ > 000-mysql-connector.patch


P
Peter Mortensen

With my Mercurial background I was going to use:

git log --patch -1 $ID > $file

But I am considering using git format-patch -1 $ID now.


P
Peter Mortensen

What is the way to generate a patch only for the specific SHA-1 value?

It's quite simple:

Option 1. git show commitID > myFile.patch

Option 2. git commitID~1..commitID > myFile.patch

Note: Replace commitID with the actual commit id (SHA-1 commit code).


Option 1 is downright wrong and is unrelated to the question.
Option 2 is also an invalid command. You will get error like: git a5f4bcaeb7fa7de27ae79d9522332e872889bbf0~1..a5f4bcaeb7fa7de27ae79d9522332e872889bbf0 git: 'a5f4bcaeb7fa7de27ae79d9522332e872889bbf0~1..a5f4bcaeb7fa7de27ae79d9522332e872889bbf0' is not a git command. See 'git --help'. Pleas check before posting answers
Option 1 is actually what I was looking for when I went searching how to do this. +1 from me!
@AnshumanManral I don't quite get why git show is wrong and unrelated; maybe explain in addition to claiming.