diff options
Diffstat (limited to '')
-rw-r--r-- | content/8/index.md | 60 |
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. |