blob: b5b89f1af0a4765c7dd864d41004f25585c50116 [file] [log] [blame]
Alexandru M Stan725c71f2019-12-11 16:53:33 -08001#!/usr/bin/env python3
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -08002# Copyright 2017 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
Mike Frysingerf80ca212018-07-13 15:02:52 -04005
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -08006"""This is a tool for picking patches from upstream and applying them."""
7
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -08008import argparse
Alexandru M Stan725c71f2019-12-11 16:53:33 -08009import configparser
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +080010import functools
Brian Norrisc3421042018-08-15 14:17:26 -070011import mailbox
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080012import os
Tzung-Bi Shih5100c742019-09-02 10:28:32 +080013import pprint
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080014import re
15import signal
16import subprocess
17import sys
Harry Cuttsae372f32019-02-12 18:01:14 -080018import textwrap
Alexandru M Stan725c71f2019-12-11 16:53:33 -080019import urllib.request
20import xmlrpc.client
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080021
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +080022errprint = functools.partial(print, file=sys.stderr)
23
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080024LINUX_URLS = (
25 'git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git',
26 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git',
27 'https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux.git',
28)
29
Stephen Boydb68c17a2019-09-26 15:08:02 -070030PATCHWORK_URLS = (
31 'https://lore.kernel.org/patchwork',
32 'https://patchwork.kernel.org',
33 'https://patchwork.ozlabs.org',
34 'https://patchwork.freedesktop.org',
35 'https://patchwork.linux-mips.org',
36)
37
Harry Cuttsae372f32019-02-12 18:01:14 -080038COMMIT_MESSAGE_WIDTH = 75
39
Brian Norris9f8a2be2018-06-01 11:14:08 -070040_PWCLIENTRC = os.path.expanduser('~/.pwclientrc')
41
Alexandru M Stan725c71f2019-12-11 16:53:33 -080042def _git(args, stdin=None, encoding='utf-8'):
43 """Calls a git subcommand.
44
45 Similar to subprocess.check_output.
46
47 Args:
48 stdin: a string or bytes (depending on encoding) that will be passed
49 to the git subcommand.
50 encoding: either 'utf-8' (default) or None. Override it to None if
51 you want both stdin and stdout to be raw bytes.
52
53 Returns:
54 the stdout of the git subcommand, same type as stdin. The output is
55 also run through strip to make sure there's no extra whitespace.
56
57 Raises:
58 subprocess.CalledProcessError: when return code is not zero.
59 The exception has a .returncode attribute.
60 """
61 return subprocess.run(
62 ['git'] + args,
63 encoding=encoding,
64 input=stdin,
65 stdout=subprocess.PIPE,
66 check=True,
67 ).stdout.strip()
68
69def _git_returncode(*args, **kwargs):
70 """Same as _git, but return returncode instead of stdout.
71
72 Similar to subprocess.call.
73
74 Never raises subprocess.CalledProcessError.
75 """
76 try:
77 _git(*args, **kwargs)
78 return 0
79 except subprocess.CalledProcessError as e:
80 return e.returncode
81
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -070082def _get_conflicts():
83 """Report conflicting files."""
84 resolutions = ('DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU')
85 conflicts = []
Alexandru M Stan725c71f2019-12-11 16:53:33 -080086 output = _git(['status', '--porcelain', '--untracked-files=no'])
87 for line in output.splitlines():
Douglas Anderson46287f92018-04-30 09:58:24 -070088 if not line:
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -070089 continue
Douglas Anderson46287f92018-04-30 09:58:24 -070090 resolution, name = line.split(None, 1)
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -070091 if resolution in resolutions:
92 conflicts.append(' ' + name)
93 if not conflicts:
Douglas Andersonb6a10fe2019-08-12 13:53:30 -070094 return ''
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -070095 return '\nConflicts:\n%s\n' % '\n'.join(conflicts)
96
Guenter Roeckd66daa72018-04-19 10:31:25 -070097def _find_linux_remote():
98 """Find a remote pointing to a Linux upstream repository."""
Alexandru M Stan725c71f2019-12-11 16:53:33 -080099 for remote in _git(['remote']).splitlines():
100 try:
101 if _git(['remote', 'get-url', remote]) in LINUX_URLS:
102 return remote
103 except subprocess.CalledProcessError:
104 # Kinda weird, get-url failing on an item that git just gave us.
105 continue
Guenter Roeckd66daa72018-04-19 10:31:25 -0700106 return None
107
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -0700108def _pause_for_merge(conflicts):
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800109 """Pause and go in the background till user resolves the conflicts."""
110
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800111 git_root = _git(['rev-parse', '--show-toplevel'])
Harry Cutts2bcd9af2020-02-20 16:27:50 -0800112 previous_head_hash = _git(['rev-parse', 'HEAD'])
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800113
114 paths = (
115 os.path.join(git_root, '.git', 'rebase-apply'),
116 os.path.join(git_root, '.git', 'CHERRY_PICK_HEAD'),
117 )
118 for path in paths:
119 if os.path.exists(path):
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800120 errprint('Found "%s".' % path)
121 errprint(conflicts)
122 errprint('Please resolve the conflicts and restart the '
123 'shell job when done. Kill this job if you '
124 'aborted the conflict.')
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800125 os.kill(os.getpid(), signal.SIGTSTP)
Harry Cutts2bcd9af2020-02-20 16:27:50 -0800126
127 # Check the conflicts actually got resolved. Otherwise we'll end up
128 # modifying the wrong commit message and probably confusing people.
129 while previous_head_hash == _git(['rev-parse', 'HEAD']):
130 errprint('Error: no new commit has been made. Did you forget to run '
131 '`git am --continue` or `git cherry-pick --continue`?')
132 errprint('Please create a new commit and restart the shell job (or kill'
133 ' it if you aborted the conflict).')
134 os.kill(os.getpid(), signal.SIGTSTP)
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800135
Brian Norris9f8a2be2018-06-01 11:14:08 -0700136def _get_pw_url(project):
137 """Retrieve the patchwork server URL from .pwclientrc.
138
Mike Frysingerf80ca212018-07-13 15:02:52 -0400139 Args:
140 project: patchwork project name; if None, we retrieve the default
141 from pwclientrc
Brian Norris9f8a2be2018-06-01 11:14:08 -0700142 """
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800143 config = configparser.ConfigParser()
Brian Norris9f8a2be2018-06-01 11:14:08 -0700144 config.read([_PWCLIENTRC])
145
146 if project is None:
147 try:
148 project = config.get('options', 'default')
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800149 except (configparser.NoSectionError, configparser.NoOptionError) as e:
150 errprint('Error: no default patchwork project found in %s. (%r)'
151 % (_PWCLIENTRC, e))
Brian Norris9f8a2be2018-06-01 11:14:08 -0700152 sys.exit(1)
153
154 if not config.has_option(project, 'url'):
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800155 errprint("Error: patchwork URL not found for project '%s'" % project)
Brian Norris9f8a2be2018-06-01 11:14:08 -0700156 sys.exit(1)
157
158 url = config.get(project, 'url')
Brian Norris2d4e9762018-08-15 13:11:47 -0700159 # Strip trailing 'xmlrpc' and/or trailing slash.
160 return re.sub('/(xmlrpc/)?$', '', url)
Brian Norris9f8a2be2018-06-01 11:14:08 -0700161
Harry Cuttsae372f32019-02-12 18:01:14 -0800162def _wrap_commit_line(prefix, content):
163 line = prefix + '=' + content
164 indent = ' ' * (len(prefix) + 1)
165 return textwrap.fill(line, COMMIT_MESSAGE_WIDTH, subsequent_indent=indent)
166
Stephen Boydb68c17a2019-09-26 15:08:02 -0700167def _pick_patchwork(url, patch_id, args):
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800168 if args['tag'] is None:
169 args['tag'] = 'FROMLIST: '
170
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800171 opener = urllib.request.urlopen('%s/patch/%d/mbox' % (url, patch_id))
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800172 if opener.getcode() != 200:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800173 errprint('Error: could not download patch - error code %d'
174 % opener.getcode())
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800175 sys.exit(1)
176 patch_contents = opener.read()
177
178 if not patch_contents:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800179 errprint('Error: No patch content found')
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800180 sys.exit(1)
181
Douglas Andersonbecd4e62019-09-25 13:40:55 -0700182 message_id = mailbox.Message(patch_contents)['Message-Id']
183 message_id = re.sub('^<|>$', '', message_id.strip())
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800184 if args['source_line'] is None:
185 args['source_line'] = '(am from %s/patch/%d/)' % (url, patch_id)
Tzung-Bi Shiha8f310d2019-09-04 19:10:10 +0800186 args['source_line'] += (
187 '\n(also found at https://lkml.kernel.org/r/%s)' % message_id)
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800188
Douglas Andersonbecd4e62019-09-25 13:40:55 -0700189 # Auto-snarf the Change-Id if it was encoded into the Message-Id.
190 mo = re.match(r'.*(I[a-f0-9]{40})@changeid$', message_id)
191 if mo and args['changeid'] is None:
192 args['changeid'] = mo.group(1)
193
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800194 if args['replace']:
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800195 _git(['reset', '--hard', 'HEAD~1'])
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800196
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800197 return _git_returncode(['am', '-3'], stdin=patch_contents, encoding=None)
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800198
Stephen Boydb68c17a2019-09-26 15:08:02 -0700199def _match_patchwork(match, args):
200 """Match location: pw://### or pw://PROJECT/###."""
201 pw_project = match.group(2)
202 patch_id = int(match.group(3))
203
204 if args['debug']:
205 print('_match_patchwork: pw_project=%s, patch_id=%d' %
206 (pw_project, patch_id))
207
208 url = _get_pw_url(pw_project)
209 return _pick_patchwork(url, patch_id, args)
210
211def _match_msgid(match, args):
212 """Match location: msgid://MSGID."""
213 msgid = match.group(1)
214
215 if args['debug']:
216 print('_match_msgid: message_id=%s' % (msgid))
217
218 # Patchwork requires the brackets so force it
219 msgid = '<' + msgid + '>'
220 url = None
221 for url in PATCHWORK_URLS:
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800222 rpc = xmlrpc.client.ServerProxy(url + '/xmlrpc/')
Stephen Boydb68c17a2019-09-26 15:08:02 -0700223 res = rpc.patch_list({'msgid': msgid})
224 if res:
225 patch_id = res[0]['id']
226 break
227 else:
228 errprint('Error: could not find patch based on message id')
229 sys.exit(1)
230
231 return _pick_patchwork(url, patch_id, args)
232
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800233def _match_linux(match, args):
234 """Match location: linux://HASH."""
235 commit = match.group(1)
236
237 if args['debug']:
238 print('_match_linux: commit=%s' % commit)
239
240 # Confirm a 'linux' remote is setup.
241 linux_remote = _find_linux_remote()
242 if not linux_remote:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800243 errprint('Error: need a valid upstream remote')
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800244 sys.exit(1)
245
246 linux_master = '%s/master' % linux_remote
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800247 try:
248 _git(['merge-base', '--is-ancestor', commit, linux_master])
249 except subprocess.CalledProcessError:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800250 errprint('Error: Commit not in %s' % linux_master)
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800251 sys.exit(1)
252
253 if args['source_line'] is None:
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800254 commit = _git(['rev-parse', commit])
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800255 args['source_line'] = ('(cherry picked from commit %s)' %
256 (commit))
257 if args['tag'] is None:
258 args['tag'] = 'UPSTREAM: '
259
260 if args['replace']:
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800261 _git(['reset', '--hard', 'HEAD~1'])
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800262
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800263 return _git_returncode(['cherry-pick', commit])
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800264
265def _match_fromgit(match, args):
266 """Match location: git://remote/branch/HASH."""
267 remote = match.group(2)
268 branch = match.group(3)
269 commit = match.group(4)
270
271 if args['debug']:
272 print('_match_fromgit: remote=%s branch=%s commit=%s' %
273 (remote, branch, commit))
274
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800275 try:
276 _git(['merge-base', '--is-ancestor', commit,
277 '%s/%s' % (remote, branch)])
278 except subprocess.CalledProcessError:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800279 errprint('Error: Commit not in %s/%s' % (remote, branch))
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800280 sys.exit(1)
281
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800282 url = _git(['remote', 'get-url', remote])
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800283
284 if args['source_line'] is None:
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800285 commit = _git(['rev-parse', commit])
Tzung-Bi Shiha8f310d2019-09-04 19:10:10 +0800286 args['source_line'] = (
287 '(cherry picked from commit %s\n %s %s)' % (commit, url, branch))
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800288 if args['tag'] is None:
289 args['tag'] = 'FROMGIT: '
290
291 if args['replace']:
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800292 _git(['reset', '--hard', 'HEAD~1'])
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800293
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800294 return _git_returncode(['cherry-pick', commit])
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800295
296def _match_gitfetch(match, args):
297 """Match location: (git|https)://repoURL#branch/HASH."""
298 remote = match.group(1)
299 branch = match.group(3)
300 commit = match.group(4)
301
302 if args['debug']:
303 print('_match_gitfetch: remote=%s branch=%s commit=%s' %
304 (remote, branch, commit))
305
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800306 try:
307 _git(['fetch', remote, branch])
308 except subprocess.CalledProcessError:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800309 errprint('Error: Branch not in %s' % remote)
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800310 sys.exit(1)
311
312 url = remote
313
314 if args['source_line'] is None:
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800315 commit = _git(['rev-parse', commit])
Tzung-Bi Shiha8f310d2019-09-04 19:10:10 +0800316 args['source_line'] = (
317 '(cherry picked from commit %s\n %s %s)' % (commit, url, branch))
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800318 if args['tag'] is None:
319 args['tag'] = 'FROMGIT: '
320
Stephen Boyd4b3869a2020-01-24 15:35:37 -0800321 if args['replace']:
322 _git(['reset', '--hard', 'HEAD~1'])
323
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800324 return _git_returncode(['cherry-pick', commit])
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800325
Stephen Boyd96396032020-02-25 10:12:59 -0800326def _match_gitweb(match, args):
327 """Match location: https://repoURL/commit/?h=branch&id=HASH."""
328 remote = match.group(1)
329 branch = match.group(2)
330 commit = match.group(3)
331
332 if args['debug']:
333 print('_match_gitweb: remote=%s branch=%s commit=%s' %
334 (remote, branch, commit))
335
336 try:
337 _git(['fetch', remote, branch])
338 except subprocess.CalledProcessError:
339 errprint('Error: Branch not in %s' % remote)
340 sys.exit(1)
341
342 url = remote
343
344 if args['source_line'] is None:
345 commit = _git(['rev-parse', commit])
346 args['source_line'] = (
347 '(cherry picked from commit %s\n %s %s)' % (commit, url, branch))
348 if args['tag'] is None:
349 args['tag'] = 'FROMGIT: '
350
351 if args['replace']:
352 _git(['reset', '--hard', 'HEAD~1'])
353
354 return _git_returncode(['cherry-pick', commit])
355
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800356def main(args):
357 """This is the main entrypoint for fromupstream.
358
359 Args:
360 args: sys.argv[1:]
361
362 Returns:
363 An int return code.
364 """
365 parser = argparse.ArgumentParser()
366
367 parser.add_argument('--bug', '-b',
Guenter Roeckf47a50c2018-07-25 12:41:36 -0700368 type=str, help='BUG= line')
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800369 parser.add_argument('--test', '-t',
Guenter Roeckf47a50c2018-07-25 12:41:36 -0700370 type=str, help='TEST= line')
Stephen Boyd24b309b2018-11-06 22:11:00 -0800371 parser.add_argument('--crbug', action='append',
372 type=int, help='BUG=chromium: line')
373 parser.add_argument('--buganizer', action='append',
374 type=int, help='BUG=b: line')
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800375 parser.add_argument('--changeid', '-c',
376 help='Overrides the gerrit generated Change-Id line')
377
Tzung-Bi Shihf5d25a82019-09-02 11:40:09 +0800378 parser.add_argument('--replace', '-r',
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800379 action='store_true',
Tzung-Bi Shih196d31e2019-09-01 18:16:28 +0800380 help='Replaces the HEAD commit with this one, taking '
381 'its properties(BUG, TEST, Change-Id). Useful for '
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800382 'updating commits.')
383 parser.add_argument('--nosignoff',
384 dest='signoff', action='store_false')
Tzung-Bi Shih5100c742019-09-02 10:28:32 +0800385 parser.add_argument('--debug', '-d', action='store_true',
386 help='Prints more verbose logs.')
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800387
388 parser.add_argument('--tag',
389 help='Overrides the tag from the title')
390 parser.add_argument('--source', '-s',
391 dest='source_line', type=str,
Tzung-Bi Shih196d31e2019-09-01 18:16:28 +0800392 help='Overrides the source line, last line, ex: '
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800393 '(am from http://....)')
394 parser.add_argument('locations',
Douglas Andersonc77a8b82018-05-04 17:02:03 -0700395 nargs='+',
Tzung-Bi Shih196d31e2019-09-01 18:16:28 +0800396 help='Patchwork ID (pw://### or pw://PROJECT/###, '
397 'where PROJECT is defined in ~/.pwclientrc; if no '
398 'PROJECT is specified, the default is retrieved from '
399 '~/.pwclientrc), '
Stephen Boydb68c17a2019-09-26 15:08:02 -0700400 'Message-ID (msgid://MSGID), '
Tzung-Bi Shih196d31e2019-09-01 18:16:28 +0800401 'linux commit like linux://HASH, or '
402 'git reference like git://remote/branch/HASH or '
403 'git://repoURL#branch/HASH or '
Stephen Boyd96396032020-02-25 10:12:59 -0800404 'https://repoURL#branch/HASH or '
405 'https://repoURL/commit/?h=branch&id=HASH')
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800406
407 args = vars(parser.parse_args(args))
408
Stephen Boyd24b309b2018-11-06 22:11:00 -0800409 buglist = [args['bug']] if args['bug'] else []
410 if args['buganizer']:
411 buglist += ['b:{0}'.format(x) for x in args['buganizer']]
412 if args['crbug']:
413 buglist += ['chromium:{0}'.format(x) for x in args['crbug']]
Brian Norris667a0cb2018-12-07 09:28:46 -0800414 if buglist:
415 args['bug'] = ', '.join(buglist)
Stephen Boyd24b309b2018-11-06 22:11:00 -0800416
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800417 if args['replace']:
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800418 old_commit_message = _git(['show', '-s', '--format=%B', 'HEAD'])
Tzung-Bi Shih231fada2019-09-02 00:54:59 +0800419
420 # It is possible that multiple Change-Ids are in the commit message
421 # (due to cherry picking). We only want to pull out the first one.
422 changeid_match = re.search('^Change-Id: (.*)$',
423 old_commit_message, re.MULTILINE)
424 if changeid_match:
425 args['changeid'] = changeid_match.group(1)
426
Tzung-Bi Shihe1e0e7d2019-09-02 15:04:38 +0800427 bugs = re.findall('^BUG=(.*)$', old_commit_message, re.MULTILINE)
Tzung-Bi Shih04345302019-09-02 12:04:01 +0800428 if args['bug'] is None and bugs:
429 args['bug'] = '\nBUG='.join(bugs)
430
Tzung-Bi Shihe1e0e7d2019-09-02 15:04:38 +0800431 tests = re.findall('^TEST=(.*)$', old_commit_message, re.MULTILINE)
Tzung-Bi Shih04345302019-09-02 12:04:01 +0800432 if args['test'] is None and tests:
433 args['test'] = '\nTEST='.join(tests)
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800434 # TODO: deal with multiline BUG/TEST better
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800435
Guenter Roeckf47a50c2018-07-25 12:41:36 -0700436 if args['bug'] is None or args['test'] is None:
Tzung-Bi Shih196d31e2019-09-01 18:16:28 +0800437 parser.error('BUG=/TEST= lines are required; --replace can help '
Stephen Boyde6fdf912018-11-09 10:30:57 -0800438 'automate, or set via --bug/--test')
Guenter Roeckf47a50c2018-07-25 12:41:36 -0700439
Tzung-Bi Shih5100c742019-09-02 10:28:32 +0800440 if args['debug']:
441 pprint.pprint(args)
442
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800443 re_matches = (
Tzung-Bi Shihe1e0e7d2019-09-02 15:04:38 +0800444 (re.compile(r'^pw://(([^/]+)/)?(\d+)'), _match_patchwork),
Stephen Boydb68c17a2019-09-26 15:08:02 -0700445 (re.compile(r'^msgid://<?([^>]*)>?'), _match_msgid),
Tzung-Bi Shihe1e0e7d2019-09-02 15:04:38 +0800446 (re.compile(r'^linux://([0-9a-f]+)'), _match_linux),
447 (re.compile(r'^(from)?git://([^/\#]+)/([^#]+)/([0-9a-f]+)$'),
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800448 _match_fromgit),
Tzung-Bi Shihe1e0e7d2019-09-02 15:04:38 +0800449 (re.compile(r'^((git|https)://.+)#(.+)/([0-9a-f]+)$'), _match_gitfetch),
Stephen Boyd96396032020-02-25 10:12:59 -0800450 (re.compile(r'^(https://.+)/commit/\?h=(.+)\&id=([0-9a-f]+)$'), _match_gitweb),
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800451 )
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800452
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800453 for location in args['locations']:
Tzung-Bi Shih5100c742019-09-02 10:28:32 +0800454 if args['debug']:
455 print('location=%s' % location)
456
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800457 for reg, handler in re_matches:
458 match = reg.match(location)
459 if match:
460 ret = handler(match, args)
461 break
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800462 else:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800463 errprint('Don\'t know what "%s" means.' % location)
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800464 sys.exit(1)
465
466 if ret != 0:
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -0700467 conflicts = _get_conflicts()
Douglas Anderson2108e532018-04-30 09:50:42 -0700468 if args['tag'] == 'UPSTREAM: ':
469 args['tag'] = 'BACKPORT: '
470 else:
471 args['tag'] = 'BACKPORT: ' + args['tag']
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -0700472 _pause_for_merge(conflicts)
473 else:
Douglas Andersonb6a10fe2019-08-12 13:53:30 -0700474 conflicts = ''
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800475
476 # extract commit message
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800477 commit_message = _git(['show', '-s', '--format=%B', 'HEAD'])
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800478
Guenter Roeck2e4f2512018-04-24 09:20:51 -0700479 # Remove stray Change-Id, most likely from merge resolution
480 commit_message = re.sub(r'Change-Id:.*\n?', '', commit_message)
481
Brian Norris7a41b982018-06-01 10:28:29 -0700482 # Note the source location before tagging anything else
483 commit_message += '\n' + args['source_line']
484
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800485 # add automatic Change ID, BUG, and TEST (and maybe signoff too) so
486 # next commands know where to work on
487 commit_message += '\n'
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -0700488 commit_message += conflicts
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800489 commit_message += '\n' + 'BUG=' + args['bug']
Harry Cuttsae372f32019-02-12 18:01:14 -0800490 commit_message += '\n' + _wrap_commit_line('TEST', args['test'])
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800491 if args['signoff']:
492 extra = ['-s']
493 else:
494 extra = []
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800495 _git(['commit'] + extra + ['--amend', '-F', '-'], stdin=commit_message)
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800496
497 # re-extract commit message
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800498 commit_message = _git(['show', '-s', '--format=%B', 'HEAD'])
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800499
Douglas Andersonbecd4e62019-09-25 13:40:55 -0700500 # If we see a "Link: " that seems to point to a Message-Id with an
501 # automatic Change-Id we'll snarf it out.
502 mo = re.search(r'^Link:.*(I[a-f0-9]{40})@changeid', commit_message,
503 re.MULTILINE)
504 if mo and args['changeid'] is None:
505 args['changeid'] = mo.group(1)
506
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800507 # replace changeid if needed
508 if args['changeid'] is not None:
509 commit_message = re.sub(r'(Change-Id: )(\w+)', r'\1%s' %
510 args['changeid'], commit_message)
511 args['changeid'] = None
512
513 # decorate it that it's from outside
514 commit_message = args['tag'] + commit_message
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800515
516 # commit everything
Alexandru M Stan725c71f2019-12-11 16:53:33 -0800517 _git(['commit', '--amend', '-F', '-'], stdin=commit_message)
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800518
Chirantan Ekbote4b08e712019-06-12 15:35:41 +0900519 return 0
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800520
521if __name__ == '__main__':
522 sys.exit(main(sys.argv[1:]))