|
|
#!/usr/bin/env python
|
|
|
"""
|
|
|
Backport pull requests to a particular branch.
|
|
|
|
|
|
Usage: backport_pr.py branch PR
|
|
|
|
|
|
e.g.:
|
|
|
|
|
|
backport_pr.py 0.13.1 123
|
|
|
|
|
|
to backport PR #123 onto branch 0.13.1
|
|
|
|
|
|
"""
|
|
|
|
|
|
from __future__ import print_function
|
|
|
|
|
|
import os
|
|
|
import sys
|
|
|
from subprocess import Popen, PIPE, check_call, check_output
|
|
|
from urllib import urlopen
|
|
|
|
|
|
from gh_api import get_pull_request
|
|
|
|
|
|
def find_rejects(root='.'):
|
|
|
for dirname, dirs, files in os.walk(root):
|
|
|
for fname in files:
|
|
|
if fname.endswith('.rej'):
|
|
|
yield os.path.join(dirname, fname)
|
|
|
|
|
|
def get_current_branch():
|
|
|
branches = check_output(['git', 'branch'])
|
|
|
for branch in branches.splitlines():
|
|
|
if branch.startswith('*'):
|
|
|
return branch[1:].strip()
|
|
|
|
|
|
def backport_pr(branch, num, project='ipython/ipython'):
|
|
|
current_branch = get_current_branch()
|
|
|
if branch != current_branch:
|
|
|
check_call(['git', 'checkout', branch])
|
|
|
pr = get_pull_request(project, num)
|
|
|
patch_url = pr['patch_url']
|
|
|
title = pr['title']
|
|
|
description = pr['body']
|
|
|
fname = "PR%i.patch" % num
|
|
|
if os.path.exists(fname):
|
|
|
print("using patch from {fname}".format(**locals()))
|
|
|
with open(fname) as f:
|
|
|
patch = f.read()
|
|
|
else:
|
|
|
req = urlopen(patch_url)
|
|
|
patch = req.read()
|
|
|
|
|
|
msg = "Backport PR #%i: %s" % (num, title) + '\n\n' + description
|
|
|
check = Popen(['git', 'apply', '--check', '--verbose'], stdin=PIPE)
|
|
|
a,b = check.communicate(patch)
|
|
|
|
|
|
if check.returncode:
|
|
|
print("patch did not apply, saving to {fname}".format(**locals()))
|
|
|
print("edit {fname} until `cat {fname} | git apply --check` succeeds".format(**locals()))
|
|
|
print("then run tools/backport_pr.py {num} again".format(**locals()))
|
|
|
if not os.path.exists(fname):
|
|
|
with open(fname, 'wb') as f:
|
|
|
f.write(patch)
|
|
|
return 1
|
|
|
|
|
|
p = Popen(['git', 'apply'], stdin=PIPE)
|
|
|
a,b = p.communicate(patch)
|
|
|
|
|
|
commit = Popen(['git', 'commit', '-a', '-m', msg])
|
|
|
commit.communicate()
|
|
|
if commit.returncode:
|
|
|
print("commit failed!")
|
|
|
return 1
|
|
|
else:
|
|
|
print("PR #%i applied, with msg:" % num)
|
|
|
print()
|
|
|
print(msg)
|
|
|
print()
|
|
|
|
|
|
if branch != current_branch:
|
|
|
check_call(['git', 'checkout', current_branch])
|
|
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
if len(sys.argv) < 3:
|
|
|
print(__doc__)
|
|
|
sys.exit(1)
|
|
|
|
|
|
sys.exit(backport_pr(sys.argv[1], int(sys.argv[2])))
|
|
|
|