summaryrefslogtreecommitdiffstatshomepage
path: root/content
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--content/8/index.md60
1 files changed, 60 insertions, 0 deletions
diff --git a/content/8/index.md b/content/8/index.md
new file mode 100644
index 0000000..ea5c65f
--- /dev/null
+++ b/content/8/index.md
@@ -0,0 +1,60 @@
++++
+date = 2021-07-23T22:37:17+02:00
+title = "Applying patches with mutt(1)"
+
+[taxonomies]
+tags = ["git", "TIL"]
++++
+
+When maintaining a project sooner or later there comes the time when you need to
+apply patches that have been submitted to you. Assuming a patch-based workflow
+those are going to be one patch per mail, possibly connected in a thread.
+There's lots of different ways of getting those patches to their final
+destination, [`git-am(1)`](https://git-scm.com/docs/git-am), and in this post I
+want to take a look at ones that work well with [`mutt(1)`](http://mutt.org/) or
+[`neomutt(1)`](https://neomutt.org/) since that is what I use.
+
+_I want to rely on the default bindings as much as possible. All mentioned
+bindings should work out of the box._
+
+Applying a single patch is very straightforward: Select the mail in the pager
+and hit `|` to pipe it to an external command. This command will be some
+variation of `git-am(1)`, perhaps `git am -s` to also add a `Signed-off-by`
+trailer.
+
+What if you want to apply a patch series, however? An obvious solution would be
+to pipe each message in the thread to `git-am(1)`. The `<pipe-message>` command
+we invoked earlier with `|` only applies to the currently selected message, so
+we can't use that on the whole thread. Instead we can tag the whole thread using
+`<Esc>t`, then use the `<tag-prefix>` command `;` followed by `|` to send all
+tagged messages to `git-am(1)`.
+
+There's two problems with this, though. The first is that depending on the setting
+of [`pipe_split`](https://neomutt.org/guide/reference#3-280-%C2%A0pipe_split),
+`git-am(1)` might only apply the first patch in the series. This is the case if
+`pipe_split` is set to the default of `no`; `mutt(1)` will then concatenate the
+messages before sending them to the external command. Sadly this concatenated
+format is slightly different from the `mbox` format that `git-am(1)` expects,
+making it not see anything past the first patch.
+
+With `pipe_split` set to `yes`, `mutt(1)` spawns one process per tagged mail
+instead, applying all patches correctly. Now that `git-am(1)` is spawned once
+per mail, however, you lose its atomicity: Neither `ORIG_HEAD` will be set
+correctly, nor will `--abort` go back to the original branch.
+
+This might not be a big issue, but I am not a fan. Thankfully `git-am(1)`
+supports reading patches from `mbox` files or `Maildir` structures. So instead
+of piping mails to `git-am(1)` via `<pipe-message>`, let's save them to the
+current directory: With the thread still tagged, `;C` (`<tag-prefix>` followed
+by `<copy-message>`) will save a copy of it under a given path. Now you can
+apply the series by hitting `!` and running `git am -s <path>`. This works with
+[`mbox_type`](https://neomutt.org/guide/reference#3-188-%C2%A0mbox_type) set to
+either `mbox` or `Maildir`.
+
+Of course there is no need to rely on the default bindings, especially if you
+need to do this kind of thing very often. `mutt(1)` is easily customizable,
+making it possible to bind the entire chain of actions to just one keystroke.
+If you're interested in a more detailed examination of a patch-based workflow
+with `mutt(1)`, check out this
+[post](http://kroah.com/log/blog/2019/08/14/patch-workflow-with-mutt-2019/)
+by Greg Kroah-Hartman.