OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import argparse | 6 import argparse |
7 import json | 7 import json |
8 import re | 8 import re |
9 import sys | 9 import sys |
10 | 10 |
11 from collections import defaultdict | 11 from collections import defaultdict |
12 | 12 |
13 import git_common as git | 13 import git_common as git |
14 | 14 |
15 | 15 |
16 FOOTER_PATTERN = re.compile(r'^\s*([\w-]+): (.*)$') | 16 FOOTER_PATTERN = re.compile(r'^\s*([\w-]+): (.*)$') |
17 CHROME_COMMIT_POSITION_PATTERN = re.compile(r'^([\w/\-\.]+)@{#(\d+)}$') | 17 CHROME_COMMIT_POSITION_PATTERN = re.compile(r'^([\w/\-\.]+)@{#(\d+)}$') |
18 GIT_SVN_ID_PATTERN = re.compile('^([^\s@]+)@(\d+)') | |
19 | 18 |
20 | 19 |
21 def normalize_name(header): | 20 def normalize_name(header): |
22 return '-'.join([ word.title() for word in header.strip().split('-') ]) | 21 return '-'.join([ word.title() for word in header.strip().split('-') ]) |
23 | 22 |
24 | 23 |
25 def parse_footer(line): | 24 def parse_footer(line): |
26 """Returns footer's (key, value) if footer is valid, else None.""" | 25 """Returns footer's (key, value) if footer is valid, else None.""" |
27 match = FOOTER_PATTERN.match(line) | 26 match = FOOTER_PATTERN.match(line) |
28 if match: | 27 if match: |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
61 # which means those aren't footers. | 60 # which means those aren't footers. |
62 footer_lines = [] | 61 footer_lines = [] |
63 | 62 |
64 footer_lines.reverse() | 63 footer_lines.reverse() |
65 footers = map(parse_footer, footer_lines) | 64 footers = map(parse_footer, footer_lines) |
66 if not footer_lines or not all(footers): | 65 if not footer_lines or not all(footers): |
67 return message_lines, [], [] | 66 return message_lines, [], [] |
68 return message_lines[:-len(footer_lines)], footer_lines, footers | 67 return message_lines[:-len(footer_lines)], footer_lines, footers |
69 | 68 |
70 | 69 |
71 def get_footer_svn_id(branch=None): | |
72 if not branch: | |
73 branch = git.root() | |
74 svn_id = None | |
75 message = git.run('log', '-1', '--format=%B', branch) | |
76 footers = parse_footers(message) | |
77 git_svn_id = get_unique(footers, 'git-svn-id') | |
78 if git_svn_id: | |
79 match = GIT_SVN_ID_PATTERN.match(git_svn_id) | |
80 if match: | |
81 svn_id = match.group(1) | |
82 return svn_id | |
83 | |
84 | |
85 def get_footer_change_id(message): | 70 def get_footer_change_id(message): |
86 """Returns a list of Gerrit's ChangeId from given commit message.""" | 71 """Returns a list of Gerrit's ChangeId from given commit message.""" |
87 return parse_footers(message).get(normalize_name('Change-Id'), []) | 72 return parse_footers(message).get(normalize_name('Change-Id'), []) |
88 | 73 |
89 | 74 |
90 def add_footer_change_id(message, change_id): | 75 def add_footer_change_id(message, change_id): |
91 """Returns message with Change-ID footer in it. | 76 """Returns message with Change-ID footer in it. |
92 | 77 |
93 Assumes that Change-Id is not yet in footers, which is then inserted at | 78 Assumes that Change-Id is not yet in footers, which is then inserted at |
94 earliest footer line which is after all of these footers: | 79 earliest footer line which is after all of these footers: |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 | 128 |
144 | 129 |
145 def get_position(footers): | 130 def get_position(footers): |
146 """Get the commit position from the footers multimap using a heuristic. | 131 """Get the commit position from the footers multimap using a heuristic. |
147 | 132 |
148 Returns: | 133 Returns: |
149 A tuple of the branch and the position on that branch. For example, | 134 A tuple of the branch and the position on that branch. For example, |
150 | 135 |
151 Cr-Commit-Position: refs/heads/master@{#292272} | 136 Cr-Commit-Position: refs/heads/master@{#292272} |
152 | 137 |
153 would give the return value ('refs/heads/master', 292272). If | 138 would give the return value ('refs/heads/master', 292272). |
154 Cr-Commit-Position is not defined, we try to infer the ref and position | |
155 from git-svn-id. The position number can be None if it was not inferrable. | |
156 """ | 139 """ |
157 | 140 |
158 position = get_unique(footers, 'Cr-Commit-Position') | 141 position = get_unique(footers, 'Cr-Commit-Position') |
159 if position: | 142 if position: |
160 match = CHROME_COMMIT_POSITION_PATTERN.match(position) | 143 match = CHROME_COMMIT_POSITION_PATTERN.match(position) |
161 assert match, 'Invalid Cr-Commit-Position value: %s' % position | 144 assert match, 'Invalid Cr-Commit-Position value: %s' % position |
162 return (match.group(1), match.group(2)) | 145 return (match.group(1), match.group(2)) |
163 | 146 |
164 svn_commit = get_unique(footers, 'git-svn-id') | |
165 if svn_commit: | |
166 match = GIT_SVN_ID_PATTERN.match(svn_commit) | |
167 assert match, 'Invalid git-svn-id value: %s' % svn_commit | |
168 # V8 has different semantics than Chromium. | |
169 if re.match(r'.*https?://v8\.googlecode\.com/svn/trunk', | |
170 match.group(1)): | |
171 return ('refs/heads/candidates', match.group(2)) | |
172 if re.match(r'.*https?://v8\.googlecode\.com/svn/branches/bleeding_edge', | |
173 match.group(1)): | |
174 return ('refs/heads/master', match.group(2)) | |
175 | |
176 # Assume that any trunk svn revision will match the commit-position | |
177 # semantics. | |
178 if re.match('.*/trunk.*$', match.group(1)): | |
179 return ('refs/heads/master', match.group(2)) | |
180 | |
181 # But for now only support faking branch-heads for chrome. | |
182 branch_match = re.match('.*/chrome/branches/([\w/-]+)/src$', match.group(1)) | |
183 if branch_match: | |
184 # svn commit numbers do not map to branches. | |
185 return ('refs/branch-heads/%s' % branch_match.group(1), None) | |
186 | |
187 raise ValueError('Unable to infer commit position from footers') | 147 raise ValueError('Unable to infer commit position from footers') |
188 | 148 |
189 | 149 |
190 def main(args): | 150 def main(args): |
191 parser = argparse.ArgumentParser( | 151 parser = argparse.ArgumentParser( |
192 formatter_class=argparse.ArgumentDefaultsHelpFormatter | 152 formatter_class=argparse.ArgumentDefaultsHelpFormatter |
193 ) | 153 ) |
194 parser.add_argument('ref', nargs='?', help="Git ref to retrieve footers from." | 154 parser.add_argument('ref', nargs='?', help="Git ref to retrieve footers from." |
195 " Omit to parse stdin.") | 155 " Omit to parse stdin.") |
196 | 156 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
234 print '%s: %s' % (k, v) | 194 print '%s: %s' % (k, v) |
235 return 0 | 195 return 0 |
236 | 196 |
237 | 197 |
238 if __name__ == '__main__': | 198 if __name__ == '__main__': |
239 try: | 199 try: |
240 sys.exit(main(sys.argv[1:])) | 200 sys.exit(main(sys.argv[1:])) |
241 except KeyboardInterrupt: | 201 except KeyboardInterrupt: |
242 sys.stderr.write('interrupted\n') | 202 sys.stderr.write('interrupted\n') |
243 sys.exit(1) | 203 sys.exit(1) |
OLD | NEW |