Skip to content

Instantly share code, notes, and snippets.

@phil-blain
Last active November 11, 2020 18:57
Show Gist options
  • Select an option

  • Save phil-blain/d350e91959efa6e7afce60e74bf7e4a8 to your computer and use it in GitHub Desktop.

Select an option

Save phil-blain/d350e91959efa6e7afce60e74bf7e4a8 to your computer and use it in GitHub Desktop.

Revisions

  1. phil-blain revised this gist Nov 11, 2020. 1 changed file with 14 additions and 0 deletions.
    14 changes: 14 additions & 0 deletions git-imap-send-all-the-emails.md
    Original file line number Diff line number Diff line change
    @@ -95,3 +95,17 @@ To upload an email or a collection of emails in a gzip'ed mbox to the IMAP inbox
    git in https://lore.kernel.org/git/<message-id>/raw | git imap-send # single email
    git in https://lore.kernel.org/git/<message-id>/t.mbox.gz | git imap-send # whole thread
    ```

    # Working with public-inbox instances with `b4`
    If the mailing list you are interested in is hosted on a public-inbox instance (like lore.kernel.org), there is a tool called [`b4`](https://pypi.org/project/b4/),
    developed by contributors from the Linux kernel project, that can be used to do what the `git in` script above demonstrates, and much more.
    To download a whole thread, you can use the Message ID of any message in the thread:
    ```bash
    # Import the whole thread to the git-imap-send—configured IMAP folder
    b4 mbox -o - <message-id> | git imap-send
    ```
    `b4` also has a lot more useful features, like detecting and downloading only patches in a specific thread, which is very useful for local code review:
    ```bash
    # Apply the most recent version of a patch series to the current branch
    b4 am -o - <message-id> | git am
    ```
  2. phil-blain revised this gist Nov 11, 2020. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions git-imap-send-all-the-emails.md
    Original file line number Diff line number Diff line change
    @@ -13,6 +13,7 @@ mail client (maybe after tweaking the [cover letter](https://git-scm.com/docs/My
    Even if you want to keep up-to-date with the development mailing list of your favorite project, you might not want to
    subscribe to the mailing list, to avoid cluttering your inbox. After all, most development mailing list have a web interface
    which can be used to browse the list, such as https://lore.kernel.org/git/, the archive for the Git mailing list.
    Some mailing list software (like [public-inbox](https://public-inbox.org/README.html), which powers lore.kernel.org) also provide bridges to other technologies like feed syndication (Atom) or Usenet (NNTP).

    But if you're not subscribed to the mailing list, and see an interesting thread that you'd like to answer to, how can you do it easily ?
    If the web archive provides a way to download messages, you can import them into your favorite mail client, but that can be hard to automate
  3. phil-blain revised this gist Oct 3, 2020. 2 changed files with 17 additions and 22 deletions.
    19 changes: 8 additions & 11 deletions git-imap-send-all-the-emails.md
    Original file line number Diff line number Diff line change
    @@ -38,8 +38,7 @@ import sys
    import os
    import mailbox
    import urllib.request
    import string
    import random
    import tempfile
    import argparse
    import gzip

    @@ -51,27 +50,26 @@ parser = argparse.ArgumentParser(
    parser.add_argument("url", help="URL to download mbox from")
    args = parser.parse_args()

    # Construct file name
    ext = '.mbox'
    mbox = os.environ.get('TMPDIR') + 'git-in-' + ''.join(random.choices(string.ascii_lowercase, k=8)) + ext

    # Check if the mbox is compressed with gzip
    must_decompress = False
    gz_ext = args.url[-3:]
    if gz_ext == '.gz':
    must_decompress = True

    # Download the mbox file from `url` and save it locally under `mbox`:
    # Create and open a temporary file
    (mbox_file, mbox_path) = tempfile.mkstemp('.mbox', 'git-in-')

    # Download the mbox file from `url` and save it to the temporary file
    # https://stackoverflow.com/a/7244263/
    with urllib.request.urlopen(args.url) as response, open(mbox, 'wb') as out_file:
    with urllib.request.urlopen(args.url) as response, open(mbox_path, 'wb') as out_file:
    data = response.read() # a `bytes` object
    if must_decompress:
    out_file.write(gzip.decompress(data))
    else:
    out_file.write(data)

    # Print messages with ordered headers
    for message in mailbox.mbox(mbox):
    for message in mailbox.mbox(mbox_path):
    # Get 'From:', 'Date:' and 'Subject:' header values
    from_header = message['from']
    date = message['date']
    @@ -87,9 +85,8 @@ for message in mailbox.mbox(mbox):
    # Print unixfrom and message
    print('From ' + message.get_from())
    print(message)

    os.remove(mbox)

    os.remove(mbox_path)
    ```

    To upload an email or a collection of emails in a gzip'ed mbox to the IMAP inbox you configured for `git imap-send`, simply do :
    20 changes: 9 additions & 11 deletions git-in
    Original file line number Diff line number Diff line change
    @@ -4,8 +4,7 @@ import sys
    import os
    import mailbox
    import urllib.request
    import string
    import random
    import tempfile
    import argparse
    import gzip

    @@ -17,27 +16,26 @@ parser = argparse.ArgumentParser(
    parser.add_argument("url", help="URL to download mbox from")
    args = parser.parse_args()

    # Construct file name
    ext = '.mbox'
    mbox = os.environ.get('TMPDIR') + 'git-in-' + ''.join(random.choices(string.ascii_lowercase, k=8)) + ext

    # Check if the mbox is compressed with gzip
    must_decompress = False
    gz_ext = args.url[-3:]
    if gz_ext == '.gz':
    must_decompress = True

    # Download the mbox file from `url` and save it locally under `mbox`:
    # Create and open a temporary file
    (mbox_file, mbox_path) = tempfile.mkstemp('.mbox', 'git-in-')

    # Download the mbox file from `url` and save it to the temporary file
    # https://stackoverflow.com/a/7244263/
    with urllib.request.urlopen(args.url) as response, open(mbox, 'wb') as out_file:
    with urllib.request.urlopen(args.url) as response, open(mbox_path, 'wb') as out_file:
    data = response.read() # a `bytes` object
    if must_decompress:
    out_file.write(gzip.decompress(data))
    else:
    out_file.write(data)

    # Print messages with ordered headers
    for message in mailbox.mbox(mbox):
    for message in mailbox.mbox(mbox_path):
    # Get 'From:', 'Date:' and 'Subject:' header values
    from_header = message['from']
    date = message['date']
    @@ -53,5 +51,5 @@ for message in mailbox.mbox(mbox):
    # Print unixfrom and message
    print('From ' + message.get_from())
    print(message)
    os.remove(mbox)

    os.remove(mbox_path)
  4. phil-blain revised this gist May 28, 2020. 2 changed files with 32 additions and 15 deletions.
    27 changes: 18 additions & 9 deletions git-imap-send-all-the-emails.md
    Original file line number Diff line number Diff line change
    @@ -41,23 +41,34 @@ import urllib.request
    import string
    import random
    import argparse
    import gzip

    # Parse input
    parser = argparse.ArgumentParser(
    prog='git in',
    description='download mbox from given URL and print messages with headers reordered for `git imap-send`',
    description="download raw or gzip'ed mbox from given URL and print messages with headers reordered for `git imap-send`",
    epilog='example usage: git in <url> | git imap-send')
    parser.add_argument("url", help="URL to download mbox from")
    args = parser.parse_args()

    # Construct file name
    mbox = os.environ.get('TMPDIR') + 'git-in-' + ''.join(random.choices(string.ascii_lowercase, k=8)) + '.mbox'
    ext = '.mbox'
    mbox = os.environ.get('TMPDIR') + 'git-in-' + ''.join(random.choices(string.ascii_lowercase, k=8)) + ext

    # Check if the mbox is compressed with gzip
    must_decompress = False
    gz_ext = args.url[-3:]
    if gz_ext == '.gz':
    must_decompress = True

    # Download the mbox file from `url` and save it locally under `mbox`:
    # https://stackoverflow.com/a/7244263/
    with urllib.request.urlopen(args.url) as response, open(mbox, 'wb') as out_file:
    data = response.read() # a `bytes` object
    out_file.write(data)
    if must_decompress:
    out_file.write(gzip.decompress(data))
    else:
    out_file.write(data)

    # Print messages with ordered headers
    for message in mailbox.mbox(mbox):
    @@ -78,13 +89,11 @@ for message in mailbox.mbox(mbox):
    print(message)

    os.remove(mbox)

    ```

    To upload an email to the IMAP inbox you configured for `git imap-send`, simply do :
    To upload an email or a collection of emails in a gzip'ed mbox to the IMAP inbox you configured for `git imap-send`, simply do :
    ```bash
    git in https://lore.kernel.org/git/<message-id>/raw | git imap-send
    git in https://lore.kernel.org/git/<message-id>/raw | git imap-send # single email
    git in https://lore.kernel.org/git/<message-id>/t.mbox.gz | git imap-send # whole thread
    ```

    # Caveats
    The script expects a raw mbox, so it does not yet support a gzip'ed mbox.
    That should not be hard to add using the [gzip module](https://docs.python.org/3/library/gzip.html) of the Python standard library.
    20 changes: 14 additions & 6 deletions git-in
    100644 → 100755
    Original file line number Diff line number Diff line change
    @@ -7,23 +7,34 @@ import urllib.request
    import string
    import random
    import argparse
    import gzip

    # Parse input
    parser = argparse.ArgumentParser(
    prog='git in',
    description='download mbox from given URL and print messages with headers reordered for `git imap-send`',
    description="download raw or gzip'ed mbox from given URL and print messages with headers reordered for `git imap-send`",
    epilog='example usage: git in <url> | git imap-send')
    parser.add_argument("url", help="URL to download mbox from")
    args = parser.parse_args()

    # Construct file name
    mbox = os.environ.get('TMPDIR') + 'git-in-' + ''.join(random.choices(string.ascii_lowercase, k=8)) + '.mbox'
    ext = '.mbox'
    mbox = os.environ.get('TMPDIR') + 'git-in-' + ''.join(random.choices(string.ascii_lowercase, k=8)) + ext

    # Check if the mbox is compressed with gzip
    must_decompress = False
    gz_ext = args.url[-3:]
    if gz_ext == '.gz':
    must_decompress = True

    # Download the mbox file from `url` and save it locally under `mbox`:
    # https://stackoverflow.com/a/7244263/
    with urllib.request.urlopen(args.url) as response, open(mbox, 'wb') as out_file:
    data = response.read() # a `bytes` object
    out_file.write(data)
    if must_decompress:
    out_file.write(gzip.decompress(data))
    else:
    out_file.write(data)

    # Print messages with ordered headers
    for message in mailbox.mbox(mbox):
    @@ -44,6 +55,3 @@ for message in mailbox.mbox(mbox):
    print(message)

    os.remove(mbox)

    # TODO: support gzip'ed mbox, detect it and decompress it automatically
    # https://docs.python.org/3/library/gzip.html
  5. phil-blain created this gist May 23, 2020.
    90 changes: 90 additions & 0 deletions git-imap-send-all-the-emails.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,90 @@
    # Email workflows with Git
    Since Git was designed for the mailing-list heavy workflow of the Linux kernel development community,
    it has commands that make it easy to transform a commit (also known as a patch) into an email and vice-versa.
    For example, [`git format-patch`](https://git-scm.com/docs/git-format-patch), [`git am`](https://git-scm.com/docs/git-am),
    [`git send-email`](https://git-scm.com/docs/git-send-email) and [`git imap-send`](https://git-scm.com/docs/git-imap-send).

    While `git send-email` can be used to send a list of patches prepared using `git format-patch` to a development mailing list via an SMTP server,
    `git imap-send` is designed to upload `git format-patch`-prepared patches to an IMAP folder, so that they can then be sent using a regular
    mail client (maybe after tweaking the [cover letter](https://git-scm.com/docs/MyFirstContribution#cover-letter) or the
    [in-patch commentaries](https://git-scm.com/docs/git-format-patch#Documentation/git-format-patch.txt---notesltrefgt)).

    # (Ab)using `git imap-send` to upload any email
    Even if you want to keep up-to-date with the development mailing list of your favorite project, you might not want to
    subscribe to the mailing list, to avoid cluttering your inbox. After all, most development mailing list have a web interface
    which can be used to browse the list, such as https://lore.kernel.org/git/, the archive for the Git mailing list.

    But if you're not subscribed to the mailing list, and see an interesting thread that you'd like to answer to, how can you do it easily ?
    If the web archive provides a way to download messages, you can import them into your favorite mail client, but that can be hard to automate
    when you are using a desktop email client (and not old-school `mutt` or `alpine`).

    So why not use `git imap-send` ? That's the plan, but since it's designed to work hand-in-hand with `git format-patch`, it expects a certain
    ordering of email headers ("From: ", "Date :" and "Subject :" must appear in that order). So a simple

    ```bash
    curl https://lore.kernel.org/git/<message-id>/raw | git imap-send
    ```

    might work if the email corresponding to `<message-id>` is a patch created by `git format-patch`, but it probably won't work if it's a regular email
    (say, a bug report or user question that you wish to answer to).

    To make `git imap-send` happy, we simply have to shuffle the email headers in the expected order before piping in the mbox.
    I use this little Python script that I name `git-in` to conveniently place it in the `git` namespace :

    ```python
    #!/usr/bin/env python3

    import sys
    import os
    import mailbox
    import urllib.request
    import string
    import random
    import argparse

    # Parse input
    parser = argparse.ArgumentParser(
    prog='git in',
    description='download mbox from given URL and print messages with headers reordered for `git imap-send`',
    epilog='example usage: git in <url> | git imap-send')
    parser.add_argument("url", help="URL to download mbox from")
    args = parser.parse_args()

    # Construct file name
    mbox = os.environ.get('TMPDIR') + 'git-in-' + ''.join(random.choices(string.ascii_lowercase, k=8)) + '.mbox'

    # Download the mbox file from `url` and save it locally under `mbox`:
    # https://stackoverflow.com/a/7244263/
    with urllib.request.urlopen(args.url) as response, open(mbox, 'wb') as out_file:
    data = response.read() # a `bytes` object
    out_file.write(data)

    # Print messages with ordered headers
    for message in mailbox.mbox(mbox):
    # Get 'From:', 'Date:' and 'Subject:' header values
    from_header = message['from']
    date = message['date']
    subject = message['subject']
    # Delete the 3 headers
    del message['from']
    del message['date']
    del message['subject']
    # Write the 3 headers in the order `git imap-send` expects
    message['From'] = from_header
    message['Date'] = date
    message['Subject'] = subject
    # Print unixfrom and message
    print('From ' + message.get_from())
    print(message)

    os.remove(mbox)
    ```

    To upload an email to the IMAP inbox you configured for `git imap-send`, simply do :
    ```bash
    git in https://lore.kernel.org/git/<message-id>/raw | git imap-send
    ```

    # Caveats
    The script expects a raw mbox, so it does not yet support a gzip'ed mbox.
    That should not be hard to add using the [gzip module](https://docs.python.org/3/library/gzip.html) of the Python standard library.
    49 changes: 49 additions & 0 deletions git-in
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,49 @@
    #!/usr/bin/env python3

    import sys
    import os
    import mailbox
    import urllib.request
    import string
    import random
    import argparse

    # Parse input
    parser = argparse.ArgumentParser(
    prog='git in',
    description='download mbox from given URL and print messages with headers reordered for `git imap-send`',
    epilog='example usage: git in <url> | git imap-send')
    parser.add_argument("url", help="URL to download mbox from")
    args = parser.parse_args()

    # Construct file name
    mbox = os.environ.get('TMPDIR') + 'git-in-' + ''.join(random.choices(string.ascii_lowercase, k=8)) + '.mbox'

    # Download the mbox file from `url` and save it locally under `mbox`:
    # https://stackoverflow.com/a/7244263/
    with urllib.request.urlopen(args.url) as response, open(mbox, 'wb') as out_file:
    data = response.read() # a `bytes` object
    out_file.write(data)

    # Print messages with ordered headers
    for message in mailbox.mbox(mbox):
    # Get 'From:', 'Date:' and 'Subject:' header values
    from_header = message['from']
    date = message['date']
    subject = message['subject']
    # Delete the 3 headers
    del message['from']
    del message['date']
    del message['subject']
    # Write the 3 headers in the order `git imap-send` expects
    message['From'] = from_header
    message['Date'] = date
    message['Subject'] = subject
    # Print unixfrom and message
    print('From ' + message.get_from())
    print(message)

    os.remove(mbox)

    # TODO: support gzip'ed mbox, detect it and decompress it automatically
    # https://docs.python.org/3/library/gzip.html