summaryrefslogtreecommitdiffstatshomepage
path: root/content/8/index.md
blob: 608cc9bf4ebdc409a75663ceb6b40fc9dc54f6c8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
+++
date = 2021-07-23T22:37:17+02:00
title = "Applying patches with mutt(1)"

[taxonomies]
tags = ["git", "TIL"]

[extra]
mentions = [9]
+++

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` (but see {{ ref(id=9) }}).

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.