Skip to content

Instantly share code, notes, and snippets.

@fritschy
Created August 20, 2014 10:39
Show Gist options
  • Save fritschy/b73fb2231505e283b062 to your computer and use it in GitHub Desktop.
Save fritschy/b73fb2231505e283b062 to your computer and use it in GitHub Desktop.
Submit patches using git send-email/format-patch that are compatible with an msft exchange/outlook receiver
# Marcus Fritzsch, July 2014
import os.path
import sys
import glob
from tempfile import mkdtemp
from email.parser import Parser
from email.generator import Generator
from subprocess import check_output
try:
from cStringIO import StringIO
except:
from io import StringIO
# Ready patches from git for submission in a outlook-on-receiver-side
# friendly format. That is; attach the patches including all it's headers,
# just like if you manually created a lot of email in outlook and
# attached a patch to each of them.
parser = Parser()
commit = sys.argv[-1]
patchdir = "patches"
# where the emails will be
tmpdir = mkdtemp()
# where the patches will be
patchdir = os.path.join(tmpdir, patchdir)
os.mkdir(patchdir)
def spawn(cmd):
return check_output(cmd.replace(' ', ' ').split(' '))
def rewrite_email(name):
basename = os.path.basename(name)
# do not rewrite cover letter
if basename == "0000-cover-letter.patch":
return
with open(name, "r") as mf:
m = parser.parse(mf)
parts = list(m.walk())
# assert the files are in our expected layout!
assert len(parts) == 3
# set the email payload to be just the email body of the original mail
m.set_payload([parts[1]])
# read patch and attach
attach = os.path.join(patchdir, basename)
with open(attach, "r") as f:
fs = f.read()
# make attachment
amail = None
with open(attach, "r") as ff:
amail = parser.parse(ff)
amail.add_header('Content-Disposition', 'attachment', filename=basename)
amail.add_header('Content-Type', 'text/x-patch')
# also, set the attachment to be it whole payload, this is needed
# as we need the headers to be present in the attachment too, this
# this is the actual cause we had to hack up this piece of near
# future technology!
amail.set_payload(fs)
m.attach(amail)
# Actually rewrite the email.
with open(name, "w") as f:
io = StringIO()
g = Generator(io, mangle_from_=False, maxheaderlen=60)
g.flatten(m)
f.write(io.getvalue())
def check_patches(patches):
# assert that we have a cover-letter for submission!
assert patches[0].endswith("0000-cover-letter.patch")
# assert that we only have .patch files in the list!
assert all(x.endswith(".patch") for x in patches)
# assert that no filename contains a ' '
assert all(not (' ' in x or '\t' in x) for x in patches)
def main():
# extra args for format-patch, also be aware that this is passed to system()
# unchecked
xargs = " ".join(sys.argv[1:-1])
# XXX: allow cover-letter to be specified on command line,
# take care of cover-letter (or all xargs?) to the second
# format-patch call.
# Create emails for submission
patches = spawn("git format-patch --thread --signoff --attach --cover-letter -o %s %s %s" % (tmpdir, xargs, commit))
patches = list(filter(lambda x: len(x) > 0, bytes(patches).decode().split("\n")))
check_patches(patches)
# Create patches for attachment
spawn("git format-patch --signoff --cover-letter -o %s %s" % (patchdir, commit))
for name in patches:
rewrite_email(name)
os.system("git send-email --annotate --suppress-cc=all %s %s" % (xargs, " ".join(patches)))
for x in patches:
os.unlink(os.path.join(patchdir, os.path.basename(x)))
os.unlink(x)
os.rmdir(patchdir)
os.rmdir(tmpdir)
__name__ == '__main__' and main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment