Skip to content

Commit c8869af

Browse files
authored
[2.7] bpo-29656: Handle PR branches in 'make patchcheck' (#302) (#628)
Additional changes needed to backport: - dropped legacy SVN support from patchcheck - use subprocess.PIPE to silence expected error output - don't try to use subprocess.Popen as a context manager - don't try to pass a keyword argument to str.split() (cherry picked from commit 482f7a2)
1 parent 309fb90 commit c8869af

File tree

1 file changed

+74
-20
lines changed

1 file changed

+74
-20
lines changed

Tools/scripts/patchcheck.py

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
SRCDIR = sysconfig.get_config_var('srcdir')
1414

15-
1615
def n_files_str(count):
1716
"""Return 'N file(s)' with the proper plurality on 'file'."""
1817
return "{} file{}".format(count, "s" if count != 1 else "")
@@ -50,32 +49,86 @@ def mq_patches_applied():
5049
st.stderr.close()
5150

5251

52+
def get_git_branch():
53+
"""Get the symbolic name for the current git branch"""
54+
cmd = "git rev-parse --abbrev-ref HEAD".split()
55+
try:
56+
return subprocess.check_output(cmd, stderr=subprocess.PIPE)
57+
except subprocess.CalledProcessError:
58+
return None
59+
60+
61+
def get_git_upstream_remote():
62+
"""Get the remote name to use for upstream branches
63+
64+
Uses "upstream" if it exists, "origin" otherwise
65+
"""
66+
cmd = "git remote get-url upstream".split()
67+
try:
68+
subprocess.check_output(cmd, stderr=subprocess.PIPE)
69+
except subprocess.CalledProcessError:
70+
return "origin"
71+
return "upstream"
72+
73+
74+
@status("Getting base branch for PR",
75+
info=lambda x: x if x is not None else "not a PR branch")
76+
def get_base_branch():
77+
if not os.path.isdir(os.path.join(SRCDIR, '.git')):
78+
# Not a git checkout, so there's no base branch
79+
return None
80+
version = sys.version_info
81+
if version.releaselevel == 'alpha':
82+
base_branch = "master"
83+
else:
84+
base_branch = "{0.major}.{0.minor}".format(version)
85+
this_branch = get_git_branch()
86+
if this_branch is None or this_branch == base_branch:
87+
# Not on a git PR branch, so there's no base branch
88+
return None
89+
upstream_remote = get_git_upstream_remote()
90+
return upstream_remote + "/" + base_branch
91+
92+
5393
@status("Getting the list of files that have been added/changed",
5494
info=lambda x: n_files_str(len(x)))
55-
def changed_files():
56-
"""Get the list of changed or added files from the VCS."""
95+
def changed_files(base_branch=None):
96+
"""Get the list of changed or added files from Mercurial or git."""
5797
if os.path.isdir(os.path.join(SRCDIR, '.hg')):
58-
vcs = 'hg'
98+
if base_branch is not None:
99+
sys.exit('need a git checkout to check PR status')
59100
cmd = 'hg status --added --modified --no-status'
60101
if mq_patches_applied():
61102
cmd += ' --rev qparent'
62-
elif os.path.isdir('.svn'):
63-
vcs = 'svn'
64-
cmd = 'svn status --quiet --non-interactive --ignore-externals'
65-
else:
66-
sys.exit('need a checkout to get modified files')
67-
68-
st = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
69-
try:
70-
st.wait()
71-
if vcs == 'hg':
103+
st = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
104+
try:
72105
return [x.decode().rstrip() for x in st.stdout]
106+
finally:
107+
st.stdout.close()
108+
elif os.path.isdir(os.path.join(SRCDIR, '.git')):
109+
if base_branch:
110+
cmd = 'git diff --name-status ' + base_branch
73111
else:
74-
output = (x.decode().rstrip().rsplit(None, 1)[-1]
75-
for x in st.stdout if x[0] in 'AM')
76-
return set(path for path in output if os.path.isfile(path))
77-
finally:
78-
st.stdout.close()
112+
cmd = 'git status --porcelain'
113+
filenames = []
114+
st = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
115+
try:
116+
for line in st.stdout:
117+
line = line.decode().rstrip()
118+
status_text, filename = line.split(None, 1)
119+
status = set(status_text)
120+
# modified, added or unmerged files
121+
if not status.intersection('MAU'):
122+
continue
123+
if ' -> ' in filename:
124+
# file is renamed
125+
filename = filename.split(' -> ', 2)[1].strip()
126+
filenames.append(filename)
127+
finally:
128+
st.stdout.close()
129+
return filenames
130+
else:
131+
sys.exit('need a checkout to get modified files')
79132

80133

81134
def report_modified_files(file_paths):
@@ -154,7 +207,8 @@ def reported_news(file_paths):
154207

155208

156209
def main():
157-
file_paths = changed_files()
210+
base_branch = get_base_branch()
211+
file_paths = changed_files(base_branch)
158212
python_files = [fn for fn in file_paths if fn.endswith('.py')]
159213
c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))]
160214
doc_files = [fn for fn in file_paths if fn.startswith('Doc') and

0 commit comments

Comments
 (0)