TriJB
TriJB

Reputation: 29

Create "git format-patch" output from existing "diff" file

The Linux kernel maintainers accept patches formatted using the git format-patch output but should you want to use something like splitpatch or patchutils to split out hunks from an existing patch, is there a what to reconstitute these into the email format you get from git format-patch?

I have taken a look at the formats and it seems the metadata at the start of the git format-patch format is simple text (email address etc, commit hash (from the local commit)) and is probably stripped out anyway by the maintainers, so you could probably write it manually, and include some random data for the commit hash?

Am I correct? I ask because sometimes maintainers want patches to be split up into series, and I can't see an easy way to do this without using git reset to reset the commit with the original changes, and then commit them separately (to get a patch series), which is a tedious procedure.

It would be much easier to just split up the existing "big" patch in my opinion.

I guess one workaround is to create a branch, apply the diffs and then use git format-patch to create the patches, but can we do this directly, without having to create branches and commits?

Upvotes: 1

Views: 220

Answers (1)

Guildenstern
Guildenstern

Reputation: 3841

Here’s what a “format-patch” looks like:

From 5b34bc4e22816f7f19bd26c15a08fe4c749b72f8 Mon Sep 17 00:00:00 2001
Message-ID: <5b34bc4e22816f7f19bd26c15a08fe4c749b72f8.1709316230.git.me@example.com>
In-Reply-To: [cover letter message-id]
From: Me <[email protected]>
Date: Fri, 1 Mar 2024 18:14:35 +0100
Subject: [PATCH] =?UTF-8?q?gitcli:=20drop=20mention=20of=20=E2=80=9Cnon-da?=
 =?UTF-8?q?shed=20form=E2=80=9D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
To: [email protected]
Cc: someone <[email protected]>

<commit message>
---
<free form part>
<diff/patch>

We’ll also assume that you have a cover letter (see the --cover-letter option) which is a separate format-patch file without any patch part (it tells you what changes with this series but it’s not something that will be applied).

Now you may copy this file into three parts and manually edit the patch on each one. Or maybe you extract the patch from this format and edit it with one of the tools you mentioned and send it back in. So with the latter approach you:

  1. extract the patch into three patch files;
  2. Remove everything below the --- or everything after the free form part if you are using the free form part to write something
  3. Copy that edited original three times
  4. For each of the copies you need to change the Message-ID to something unique. I guess plenty of languages have utilities to parse email headers and to generate some globally unique string that can be identified as a message id
  5. For each of them you need to change the subject:
    • Change [PATCH] to [PATCH n/3] where n is the patch number (1–3)
    • Change the subject to the commit message. Keep in mind to encode UTF-8 if you are using that, but I guess that is unlikely in this context.
  6. Write the commit message for each
  7. Use cat(1) to combine each format-patch copy with the new patch
  8. Since we assume that you have a cover letter format-patch file you don’t need to worry about the In-Reply-To headers. For each new format-patch copy they will point to this cover letter. Assuming you are using a “flat” in-reply-to scheme where all patches point back to the cover letter.
  9. The cover letter tells you what patches are in this series. Now the cover letter will say that there is one patch, which is wrong. That should be changed. It’s just a git-shortlog(1) output it seems.
  10. Optional: check out the branch you are basing this in detached HEAD mode (git checkout --detach main) and try to apply your patches with git-am(1). This will make sure that your patches are okay.

Now assuming that all of these files are in something like ./patches directory you can use git-send-email(1) on that directory.

You should now apply these patches with git-am(1) for your own use (see next paragraph). Maybe label this version with git tag my-series/<branch-name>/v1.

You send out this series. Now you get some feedback: patch 1 should be split into two patches and patches 2–3 should be squashed. You also get some feedback on typos in one of the commit messages and a formatting mistake in patch 2.

Now you need to check out git tag my-series/<branch-name>/v1 and repeat the above process.

An alternative approach is to use tools like git-rebase(1) and git-reset(1) to do all the changes you need to do with git(1). Then when you use git format-patch you only need to review what you are sending out and edit the cover letter file. But I would recommend doing as much preparation as possible with git(1) as you can;[1] you can for example write the cover letter beforehand and use --description-file[2] to feed it to the command together with --cover-from-description=subject.[3]

So if you are doing everything with git(1):

  1. Prepare your changes
  2. trash patches (or rm -r patches if you don’t have or don’t want to use a trash utility)
    • This is useful in case you have patches from previous runs that won’t get clobbered because the filenames are not the same. So you don’t send out more than you intend.
  3. Use git-format-patch
  4. Review what you are about to send out with cat patches/* | less
  5. Send it out with git-send-email(1)

Discussion

I haven’t contributed to the Linux Kernel but projects which use mailing lists can have pretty high standards when it comes to the commit history. A few typos in the commit message could lead to a new version of the series. One too many unrelated formatting changes could lead to a new version. Not splitting or squashing things appropriately (like you mention) is very likely to lead to a new version. It’s in my opinion best to just get comfortable with history rewriting using git(1) if you plan to contribute to a mailing list-based project.

Notes

  1. Doing the “last minute” edits on the cover letter can get really tedious as you always spot little mistakes (in my experience). Better to be able to recreate everything from one command.
  2. You need Git 2.43 for this
  3. The first line of the file becomes the subject, the rest is put in the body. Like a commit.

Upvotes: 1

Related Questions