Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(55)

Side by Side Diff: third_party/crashpad/update.py

Issue 1513843002: Add update.py to easily update the imported in-tree copy of Crashpad (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: --fetch-ref, ancestor check, prompt for assisted cherry-pick Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # coding: utf-8
3
4 # Copyright 2015 The Chromium Authors. All rights reserved.
5 # Use of this source code is governed by a BSD-style license that can be
6 # found in the LICENSE file.
7
8 import argparse
9 import os
10 import re
11 import subprocess
12 import sys
13 import textwrap
14
15
16 def SubprocessCheckCall0Or1(args):
17 """Like subprocss.check_call(), but allows a return code of 1.
18
19 Returns True if the subprocess exits with code 0, False if it exits with
20 code 1, and re-raises the subprocess.check_call() exception otherwise.
21 """
22 try:
23 subprocess.check_call(args)
24 except subprocess.CalledProcessError, e:
25 if e.returncode != 1:
26 raise
27 return False
28
29 return True
30
31
32 def GitMergeBaseIsAncestor(ancestor, descendant):
33 """Determines whether |ancestor| is an ancestor of |descendant|.
34 """
35 return SubprocessCheckCall0Or1(
36 ['git', 'merge-base', '--is-ancestor', ancestor, descendant])
37
38
39 def main(args):
40 parser = argparse.ArgumentParser(
41 description='Update the in-tree copy of an imported project')
42 parser.add_argument(
43 '--repository',
44 default='https://chromium.googlesource.com/crashpad/crashpad',
45 help='The imported project\'s remote fetch URL',
46 metavar='URL')
47 parser.add_argument(
48 '--subtree',
49 default='third_party/crashpad/crashpad',
50 help='The imported project\'s location in this project\'s tree',
51 metavar='PATH')
52 parser.add_argument(
53 '--update-to',
54 default='FETCH_HEAD',
55 help='What to update the imported project to',
56 metavar='COMMITISH')
57 parser.add_argument(
58 '--fetch-ref',
59 default='HEAD',
60 help='The remote ref to fetch',
61 metavar='REF')
62 parser.add_argument(
63 '--readme',
64 help='The README.chromium file describing the imported project',
65 metavar='FILE',
66 dest='readme_path')
67 parsed = parser.parse_args(args)
68
69 original_head = (
70 subprocess.check_output(['git', 'rev-parse', 'HEAD']).rstrip())
71
72 # Read the README, because that’s what it’s for. Extract some things from
73 # it, and save it to be able to update it later.
74 readme_path = (parsed.readme_path or
75 os.path.join(os.path.dirname(__file__ or '.'),
76 'README.chromium'))
77 readme_content_old = open(readme_path).read()
78
79 project_name_match = re.search(
80 r'^Name:\s+(.*)$', readme_content_old, re.MULTILINE)
81 project_name = project_name_match.group(1)
82
83 # Extract the original commit hash from the README.
84 revision_match = re.search(r'^Revision:\s+([0-9a-fA-F]{40})($|\s)',
85 readme_content_old,
86 re.MULTILINE)
87 revision_old = revision_match.group(1)
88
89 subprocess.check_call(['git', 'fetch', parsed.repository, parsed.fetch_ref])
90
91 # Make sure that parsed.update_to is an ancestor of FETCH_HEAD, and
92 # revision_old is an ancestor of parsed.update_to. This prevents the use of
93 # hashes that are known to git but that don’t make sense in the context of
94 # the update operation.
95 if not GitMergeBaseIsAncestor(parsed.update_to, 'FETCH_HEAD'):
96 raise Exception('update_to is not an ancestor of FETCH_HEAD',
97 parsed.update_to,
98 'FETCH_HEAD')
99 if not GitMergeBaseIsAncestor(revision_old, parsed.update_to):
100 raise Exception('revision_old is not an ancestor of update_to',
101 revision_old,
102 parsed.update_to)
103
104 update_range = revision_old + '..' + parsed.update_to
105
106 # This cherry-picks each change in the window from the upstream project into
107 # the current branch.
108 assisted_cherry_pick = False
109 try:
110 if not SubprocessCheckCall0Or1(['git',
111 'cherry-pick',
112 '--keep-redundant-commits',
113 '--strategy=subtree',
114 '-Xsubtree=' + parsed.subtree,
115 '-x',
116 update_range]):
117 assisted_cherry_pick = True
118 print >>sys.stderr, ("""
119 Please fix the errors above and run "git cherry-pick --continue".
120 Press Enter when "git cherry-pick" completes.
121 You may use a new shell for this, or ^Z if job control is available.
122 Press ^C to abort.
123 """)
124 raw_input()
125 except:
126 # ^C, signal, or something else.
127 print >>sys.stderr, 'Aborting...'
128 subprocess.call(['git', 'cherry-pick', '--abort'])
129 raise
130
131 # Get an abbreviated hash and subject line for each commit in the window,
132 # sorted in chronological order.
133 log_lines = subprocess.check_output(['git',
134 '-c',
135 'core.abbrev=12',
136 'log',
137 '--abbrev-commit',
138 '--pretty=oneline',
139 '--reverse',
140 update_range]).splitlines(False)
141
142 if assisted_cherry_pick:
143 # If the user had to help, count the number of cherry-picked commits,
144 # expecting it to match.
145 cherry_picked_commits = int(subprocess.check_output(
146 ['git', 'rev-list', '--count', original_head + '..HEAD']))
147 if cherry_picked_commits != len(log_lines):
148 print >>sys.stderr, 'Something smells fishy, aborting anyway...'
149 subprocess.call(['git', 'cherry-pick', '--abort'])
150 raise Exception('not all commits were cherry-picked',
151 len(log_lines),
152 cherry_picked_commits)
153
154 # Make a nice commit message. Start with the full commit hash.
155 revision_new = subprocess.check_output(
156 ['git', 'rev-parse', parsed.update_to]).rstrip()
157 new_message = 'Update ' + project_name + ' to ' + revision_new + '\n\n'
158
159 # Wrap everything to 72 characters, with a hanging indent.
160 wrapper = textwrap.TextWrapper(width=72, subsequent_indent = ' ' * 13)
161 for line in log_lines:
162 # Strip trailing periods from subjects.
163 if line.endswith('.'):
164 line = line[:-1]
165
166 # If any subjects have what look like commit hashes in them, truncate
167 # them to 12 characters.
168 line = re.sub(r'(\s)([0-9a-fA-F]{12})([0-9a-fA-F]{28})($|\s)',
169 r'\1\2\4',
170 line)
171
172 new_message += '\n'.join(wrapper.wrap(line)) + '\n'
173
174 # Update the README with the new hash.
175 readme_content_new = re.sub(
176 r'^(Revision:\s+)([0-9a-fA-F]{40})($|\s.*?$)',
177 r'\g<1>' + revision_new,
178 readme_content_old,
179 1,
180 re.MULTILINE)
181
182 # If the in-tree copy has no changes relative to the upstream, clear the
183 # “Local Modifications” section of the README.
184 has_local_modifications = True
185 if SubprocessCheckCall0Or1(['git',
186 'diff-tree',
187 '--quiet',
188 parsed.update_to,
189 'HEAD:' + parsed.subtree]):
190 has_local_modifications = False
191
192 readme_content_new = re.sub(r'\nLocal Modifications:\n.*$',
193 '\nLocal Modifications:\nNone.',
194 readme_content_new,
195 1)
196
197 # This soft-reset causes all of the cherry-picks to show up as staged,
198 # which will have the effect of squashing them along with the README update
199 # when committed below.
200 subprocess.check_call(['git', 'reset', '--soft', original_head])
201
202 # Write the new README.
203 open(readme_path, 'w').write(readme_content_new)
204
205 # Commit everything.
206 subprocess.check_call(['git', 'add', readme_path])
207 subprocess.check_call(['git', 'commit', '--message=' + new_message])
208
209 if has_local_modifications:
210 print >>sys.stderr, (
211 'Remember to check the Local Modifications section in ' +
212 readme_path)
213
214 return 0
215
216
217 if __name__ == '__main__':
218 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698