Skip to content

Instantly share code, notes, and snippets.

@jezdez
Created August 25, 2011 10:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jezdez/1170386 to your computer and use it in GitHub Desktop.
Save jezdez/1170386 to your computer and use it in GitHub Desktop.
A helper script to apply patches from Django's trac
#!/usr/bin/env python
"""
Obviously this is only useful if you have to deal with Django's
Trac a lot.
Mostly stolen from Jacob Kaplan-Moss, but improved by Jannis Leidel
and Aymeric Augustin.
Reads a config file at ~/.djpatchrc, e.g.:
[djpatch]
username = django
password = guitar
"""
import os
import sys
import argparse
import xmlrpclib
import subprocess
import ConfigParser
import urlparse
# Turn off help, so we print all options in response to -h
conf_parser = argparse.ArgumentParser(add_help=False)
conf_parser.add_argument("-c", "--conf",
metavar="FILE", help="Specify config file",
default=os.path.expanduser('~/.djpatchrc'))
args, remaining_argv = conf_parser.parse_known_args()
defaults = {
"patchlevel": -1, # -1 = autodetect
"download_only": False,
"url": "https://code.djangoproject.com/login/xmlrpc",
}
if args.conf:
if not os.path.exists(args.conf):
print "Couldn't find config file %s" % args.conf
raise
config = ConfigParser.SafeConfigParser()
config.read([args.conf])
try:
items = config.items("djpatch")
defaults.update(dict(items))
except ConfigParser.NoSectionError, e:
print "Error while loading config file %s: %s" % (args.conf, e)
parser = argparse.ArgumentParser(
# Inherit options from config_parser
parents=[conf_parser],
# Don't mess with format of description
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.set_defaults(**defaults)
parser.add_argument("-d",
dest="download_only", action="store_true",
help="Only download the patch file")
parser.add_argument("-p",
dest="patchlevel", type=int, metavar="NUM",
help="Strip NUM leading components from file names")
parser.add_argument("-U", "--username",
dest="username", type=str, help="Trac username")
parser.add_argument("-P", "--password",
dest="password", nargs=1, type=str, help="Trac password")
parser.add_argument("--url",
dest="url", type=str, help="Trac XMLRPC URL")
parser.add_argument("ticket",
nargs=1, type=int, help="Ticket ID", metavar="TICKET")
args = parser.parse_args(remaining_argv)
if not args.username or not args.password:
raise SystemExit('No username or password given.')
credentials = ("%s:%s@" % (args.username, args.password))
url = list(urlparse.urlsplit(args.url))
url[1] = credentials + url[1]
url_with_credentials = urlparse.urlunsplit(url)
s = xmlrpclib.Server(url_with_credentials)
attachments = s.ticket.listAttachments(args.ticket[0])
attachments.sort(key=lambda rec: rec[3])
name = attachments[-1][0]
patch = s.ticket.getAttachment(args.ticket[0], name)
if args.patchlevel == -1:
if "\n--- a/" in patch.data and "\n+++ b/" in patch.data:
args.patchlevel = 1
else:
args.patchlevel = 0
if args.download_only:
with open(name, 'w') as p:
p.write(patch.data)
print u"Saved '%s'" % name
else:
cmd = ["git", "apply", "-p%s" % str(args.patchlevel), "-"]
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=sys.stdout)
proc.communicate(patch.data)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment