blob: c9d551013bc859a39668577c5542247fcf7f8a46 [file] [log] [blame]
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -08001#!/usr/bin/env python2
Mike Frysingerf80ca212018-07-13 15:02:52 -04002# -*- coding: utf-8 -*-
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -08003# Copyright 2017 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
Mike Frysingerf80ca212018-07-13 15:02:52 -04006
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -08007"""This is a tool for picking patches from upstream and applying them."""
8
9from __future__ import print_function
10
Brian Norris9f8a2be2018-06-01 11:14:08 -070011import ConfigParser
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080012import argparse
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +080013import functools
Brian Norrisc3421042018-08-15 14:17:26 -070014import mailbox
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080015import os
Tzung-Bi Shih5100c742019-09-02 10:28:32 +080016import pprint
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080017import re
18import signal
19import subprocess
20import sys
Harry Cuttsae372f32019-02-12 18:01:14 -080021import textwrap
Brian Norris2d4e9762018-08-15 13:11:47 -070022import urllib
Stephen Boydb68c17a2019-09-26 15:08:02 -070023import xmlrpclib
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080024
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +080025errprint = functools.partial(print, file=sys.stderr)
26
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080027LINUX_URLS = (
28 'git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git',
29 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git',
30 'https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux.git',
31)
32
Stephen Boydb68c17a2019-09-26 15:08:02 -070033PATCHWORK_URLS = (
34 'https://lore.kernel.org/patchwork',
35 'https://patchwork.kernel.org',
36 'https://patchwork.ozlabs.org',
37 'https://patchwork.freedesktop.org',
38 'https://patchwork.linux-mips.org',
39)
40
Harry Cuttsae372f32019-02-12 18:01:14 -080041COMMIT_MESSAGE_WIDTH = 75
42
Brian Norris9f8a2be2018-06-01 11:14:08 -070043_PWCLIENTRC = os.path.expanduser('~/.pwclientrc')
44
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -070045def _get_conflicts():
46 """Report conflicting files."""
47 resolutions = ('DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU')
48 conflicts = []
Douglas Anderson46287f92018-04-30 09:58:24 -070049 lines = subprocess.check_output(['git', 'status', '--porcelain',
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -070050 '--untracked-files=no']).split('\n')
Douglas Anderson46287f92018-04-30 09:58:24 -070051 for line in lines:
52 if not line:
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -070053 continue
Douglas Anderson46287f92018-04-30 09:58:24 -070054 resolution, name = line.split(None, 1)
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -070055 if resolution in resolutions:
56 conflicts.append(' ' + name)
57 if not conflicts:
Douglas Andersonb6a10fe2019-08-12 13:53:30 -070058 return ''
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -070059 return '\nConflicts:\n%s\n' % '\n'.join(conflicts)
60
Guenter Roeckd66daa72018-04-19 10:31:25 -070061def _find_linux_remote():
62 """Find a remote pointing to a Linux upstream repository."""
63 git_remote = subprocess.Popen(['git', 'remote'], stdout=subprocess.PIPE)
64 remotes = git_remote.communicate()[0].strip()
65 for remote in remotes.splitlines():
66 rurl = subprocess.Popen(['git', 'remote', 'get-url', remote],
67 stdout=subprocess.PIPE)
68 url = rurl.communicate()[0].strip()
69 if not rurl.returncode and url in LINUX_URLS:
70 return remote
71 return None
72
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -070073def _pause_for_merge(conflicts):
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080074 """Pause and go in the background till user resolves the conflicts."""
75
76 git_root = subprocess.check_output(['git', 'rev-parse',
77 '--show-toplevel']).strip('\n')
78
79 paths = (
80 os.path.join(git_root, '.git', 'rebase-apply'),
81 os.path.join(git_root, '.git', 'CHERRY_PICK_HEAD'),
82 )
83 for path in paths:
84 if os.path.exists(path):
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +080085 errprint('Found "%s".' % path)
86 errprint(conflicts)
87 errprint('Please resolve the conflicts and restart the '
88 'shell job when done. Kill this job if you '
89 'aborted the conflict.')
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -080090 os.kill(os.getpid(), signal.SIGTSTP)
91 # TODO: figure out what the state is after the merging, and go based on
92 # that (should we abort? skip? continue?)
93 # Perhaps check last commit message to see if it's the one we were using.
94
Brian Norris9f8a2be2018-06-01 11:14:08 -070095def _get_pw_url(project):
96 """Retrieve the patchwork server URL from .pwclientrc.
97
Mike Frysingerf80ca212018-07-13 15:02:52 -040098 Args:
99 project: patchwork project name; if None, we retrieve the default
100 from pwclientrc
Brian Norris9f8a2be2018-06-01 11:14:08 -0700101 """
102 config = ConfigParser.ConfigParser()
103 config.read([_PWCLIENTRC])
104
105 if project is None:
106 try:
107 project = config.get('options', 'default')
108 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800109 errprint('Error: no default patchwork project found in %s.'
110 % _PWCLIENTRC)
Brian Norris9f8a2be2018-06-01 11:14:08 -0700111 sys.exit(1)
112
113 if not config.has_option(project, 'url'):
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800114 errprint("Error: patchwork URL not found for project '%s'" % project)
Brian Norris9f8a2be2018-06-01 11:14:08 -0700115 sys.exit(1)
116
117 url = config.get(project, 'url')
Brian Norris2d4e9762018-08-15 13:11:47 -0700118 # Strip trailing 'xmlrpc' and/or trailing slash.
119 return re.sub('/(xmlrpc/)?$', '', url)
Brian Norris9f8a2be2018-06-01 11:14:08 -0700120
Harry Cuttsae372f32019-02-12 18:01:14 -0800121def _wrap_commit_line(prefix, content):
122 line = prefix + '=' + content
123 indent = ' ' * (len(prefix) + 1)
124 return textwrap.fill(line, COMMIT_MESSAGE_WIDTH, subsequent_indent=indent)
125
Stephen Boydb68c17a2019-09-26 15:08:02 -0700126def _pick_patchwork(url, patch_id, args):
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800127 if args['tag'] is None:
128 args['tag'] = 'FROMLIST: '
129
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800130 opener = urllib.urlopen('%s/patch/%d/mbox' % (url, patch_id))
131 if opener.getcode() != 200:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800132 errprint('Error: could not download patch - error code %d'
133 % opener.getcode())
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800134 sys.exit(1)
135 patch_contents = opener.read()
136
137 if not patch_contents:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800138 errprint('Error: No patch content found')
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800139 sys.exit(1)
140
Douglas Andersonbecd4e62019-09-25 13:40:55 -0700141 message_id = mailbox.Message(patch_contents)['Message-Id']
142 message_id = re.sub('^<|>$', '', message_id.strip())
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800143 if args['source_line'] is None:
144 args['source_line'] = '(am from %s/patch/%d/)' % (url, patch_id)
Tzung-Bi Shiha8f310d2019-09-04 19:10:10 +0800145 args['source_line'] += (
146 '\n(also found at https://lkml.kernel.org/r/%s)' % message_id)
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800147
Douglas Andersonbecd4e62019-09-25 13:40:55 -0700148 # Auto-snarf the Change-Id if it was encoded into the Message-Id.
149 mo = re.match(r'.*(I[a-f0-9]{40})@changeid$', message_id)
150 if mo and args['changeid'] is None:
151 args['changeid'] = mo.group(1)
152
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800153 if args['replace']:
154 subprocess.call(['git', 'reset', '--hard', 'HEAD~1'])
155
156 git_am = subprocess.Popen(['git', 'am', '-3'], stdin=subprocess.PIPE)
157 git_am.communicate(patch_contents)
158 return git_am.returncode
159
Stephen Boydb68c17a2019-09-26 15:08:02 -0700160def _match_patchwork(match, args):
161 """Match location: pw://### or pw://PROJECT/###."""
162 pw_project = match.group(2)
163 patch_id = int(match.group(3))
164
165 if args['debug']:
166 print('_match_patchwork: pw_project=%s, patch_id=%d' %
167 (pw_project, patch_id))
168
169 url = _get_pw_url(pw_project)
170 return _pick_patchwork(url, patch_id, args)
171
172def _match_msgid(match, args):
173 """Match location: msgid://MSGID."""
174 msgid = match.group(1)
175
176 if args['debug']:
177 print('_match_msgid: message_id=%s' % (msgid))
178
179 # Patchwork requires the brackets so force it
180 msgid = '<' + msgid + '>'
181 url = None
182 for url in PATCHWORK_URLS:
183 rpc = xmlrpclib.ServerProxy(url + '/xmlrpc/')
184 res = rpc.patch_list({'msgid': msgid})
185 if res:
186 patch_id = res[0]['id']
187 break
188 else:
189 errprint('Error: could not find patch based on message id')
190 sys.exit(1)
191
192 return _pick_patchwork(url, patch_id, args)
193
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800194def _match_linux(match, args):
195 """Match location: linux://HASH."""
196 commit = match.group(1)
197
198 if args['debug']:
199 print('_match_linux: commit=%s' % commit)
200
201 # Confirm a 'linux' remote is setup.
202 linux_remote = _find_linux_remote()
203 if not linux_remote:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800204 errprint('Error: need a valid upstream remote')
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800205 sys.exit(1)
206
207 linux_master = '%s/master' % linux_remote
208 ret = subprocess.call(['git', 'merge-base', '--is-ancestor',
209 commit, linux_master])
210 if ret:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800211 errprint('Error: Commit not in %s' % linux_master)
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800212 sys.exit(1)
213
214 if args['source_line'] is None:
215 git_pipe = subprocess.Popen(['git', 'rev-parse', commit],
216 stdout=subprocess.PIPE)
217 commit = git_pipe.communicate()[0].strip()
218
219 args['source_line'] = ('(cherry picked from commit %s)' %
220 (commit))
221 if args['tag'] is None:
222 args['tag'] = 'UPSTREAM: '
223
224 if args['replace']:
225 subprocess.call(['git', 'reset', '--hard', 'HEAD~1'])
226
227 return subprocess.call(['git', 'cherry-pick', commit])
228
229def _match_fromgit(match, args):
230 """Match location: git://remote/branch/HASH."""
231 remote = match.group(2)
232 branch = match.group(3)
233 commit = match.group(4)
234
235 if args['debug']:
236 print('_match_fromgit: remote=%s branch=%s commit=%s' %
237 (remote, branch, commit))
238
239 ret = subprocess.call(['git', 'merge-base', '--is-ancestor',
240 commit, '%s/%s' % (remote, branch)])
241 if ret:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800242 errprint('Error: Commit not in %s/%s' % (remote, branch))
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800243 sys.exit(1)
244
245 git_pipe = subprocess.Popen(['git', 'remote', 'get-url', remote],
246 stdout=subprocess.PIPE)
247 url = git_pipe.communicate()[0].strip()
248
249 if args['source_line'] is None:
250 git_pipe = subprocess.Popen(['git', 'rev-parse', commit],
251 stdout=subprocess.PIPE)
252 commit = git_pipe.communicate()[0].strip()
253
Tzung-Bi Shiha8f310d2019-09-04 19:10:10 +0800254 args['source_line'] = (
255 '(cherry picked from commit %s\n %s %s)' % (commit, url, branch))
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800256 if args['tag'] is None:
257 args['tag'] = 'FROMGIT: '
258
259 if args['replace']:
260 subprocess.call(['git', 'reset', '--hard', 'HEAD~1'])
261
262 return subprocess.call(['git', 'cherry-pick', commit])
263
264def _match_gitfetch(match, args):
265 """Match location: (git|https)://repoURL#branch/HASH."""
266 remote = match.group(1)
267 branch = match.group(3)
268 commit = match.group(4)
269
270 if args['debug']:
271 print('_match_gitfetch: remote=%s branch=%s commit=%s' %
272 (remote, branch, commit))
273
274 ret = subprocess.call(['git', 'fetch', remote, branch])
275 if ret:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800276 errprint('Error: Branch not in %s' % remote)
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800277 sys.exit(1)
278
279 url = remote
280
281 if args['source_line'] is None:
282 git_pipe = subprocess.Popen(['git', 'rev-parse', commit],
283 stdout=subprocess.PIPE)
284 commit = git_pipe.communicate()[0].strip()
285
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 return subprocess.call(['git', 'cherry-pick', commit])
292
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800293def main(args):
294 """This is the main entrypoint for fromupstream.
295
296 Args:
297 args: sys.argv[1:]
298
299 Returns:
300 An int return code.
301 """
302 parser = argparse.ArgumentParser()
303
304 parser.add_argument('--bug', '-b',
Guenter Roeckf47a50c2018-07-25 12:41:36 -0700305 type=str, help='BUG= line')
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800306 parser.add_argument('--test', '-t',
Guenter Roeckf47a50c2018-07-25 12:41:36 -0700307 type=str, help='TEST= line')
Stephen Boyd24b309b2018-11-06 22:11:00 -0800308 parser.add_argument('--crbug', action='append',
309 type=int, help='BUG=chromium: line')
310 parser.add_argument('--buganizer', action='append',
311 type=int, help='BUG=b: line')
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800312 parser.add_argument('--changeid', '-c',
313 help='Overrides the gerrit generated Change-Id line')
314
Tzung-Bi Shihf5d25a82019-09-02 11:40:09 +0800315 parser.add_argument('--replace', '-r',
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800316 action='store_true',
Tzung-Bi Shih196d31e2019-09-01 18:16:28 +0800317 help='Replaces the HEAD commit with this one, taking '
318 'its properties(BUG, TEST, Change-Id). Useful for '
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800319 'updating commits.')
320 parser.add_argument('--nosignoff',
321 dest='signoff', action='store_false')
Tzung-Bi Shih5100c742019-09-02 10:28:32 +0800322 parser.add_argument('--debug', '-d', action='store_true',
323 help='Prints more verbose logs.')
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800324
325 parser.add_argument('--tag',
326 help='Overrides the tag from the title')
327 parser.add_argument('--source', '-s',
328 dest='source_line', type=str,
Tzung-Bi Shih196d31e2019-09-01 18:16:28 +0800329 help='Overrides the source line, last line, ex: '
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800330 '(am from http://....)')
331 parser.add_argument('locations',
Douglas Andersonc77a8b82018-05-04 17:02:03 -0700332 nargs='+',
Tzung-Bi Shih196d31e2019-09-01 18:16:28 +0800333 help='Patchwork ID (pw://### or pw://PROJECT/###, '
334 'where PROJECT is defined in ~/.pwclientrc; if no '
335 'PROJECT is specified, the default is retrieved from '
336 '~/.pwclientrc), '
Stephen Boydb68c17a2019-09-26 15:08:02 -0700337 'Message-ID (msgid://MSGID), '
Tzung-Bi Shih196d31e2019-09-01 18:16:28 +0800338 'linux commit like linux://HASH, or '
339 'git reference like git://remote/branch/HASH or '
340 'git://repoURL#branch/HASH or '
Stephen Boyd66ea1912018-10-30 11:26:54 -0700341 'https://repoURL#branch/HASH')
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800342
343 args = vars(parser.parse_args(args))
344
Stephen Boyd24b309b2018-11-06 22:11:00 -0800345 buglist = [args['bug']] if args['bug'] else []
346 if args['buganizer']:
347 buglist += ['b:{0}'.format(x) for x in args['buganizer']]
348 if args['crbug']:
349 buglist += ['chromium:{0}'.format(x) for x in args['crbug']]
Brian Norris667a0cb2018-12-07 09:28:46 -0800350 if buglist:
351 args['bug'] = ', '.join(buglist)
Stephen Boyd24b309b2018-11-06 22:11:00 -0800352
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800353 if args['replace']:
354 old_commit_message = subprocess.check_output(
355 ['git', 'show', '-s', '--format=%B', 'HEAD']
356 ).strip('\n')
Tzung-Bi Shih231fada2019-09-02 00:54:59 +0800357
358 # It is possible that multiple Change-Ids are in the commit message
359 # (due to cherry picking). We only want to pull out the first one.
360 changeid_match = re.search('^Change-Id: (.*)$',
361 old_commit_message, re.MULTILINE)
362 if changeid_match:
363 args['changeid'] = changeid_match.group(1)
364
Tzung-Bi Shihe1e0e7d2019-09-02 15:04:38 +0800365 bugs = re.findall('^BUG=(.*)$', old_commit_message, re.MULTILINE)
Tzung-Bi Shih04345302019-09-02 12:04:01 +0800366 if args['bug'] is None and bugs:
367 args['bug'] = '\nBUG='.join(bugs)
368
Tzung-Bi Shihe1e0e7d2019-09-02 15:04:38 +0800369 tests = re.findall('^TEST=(.*)$', old_commit_message, re.MULTILINE)
Tzung-Bi Shih04345302019-09-02 12:04:01 +0800370 if args['test'] is None and tests:
371 args['test'] = '\nTEST='.join(tests)
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800372 # TODO: deal with multiline BUG/TEST better
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800373
Guenter Roeckf47a50c2018-07-25 12:41:36 -0700374 if args['bug'] is None or args['test'] is None:
Tzung-Bi Shih196d31e2019-09-01 18:16:28 +0800375 parser.error('BUG=/TEST= lines are required; --replace can help '
Stephen Boyde6fdf912018-11-09 10:30:57 -0800376 'automate, or set via --bug/--test')
Guenter Roeckf47a50c2018-07-25 12:41:36 -0700377
Tzung-Bi Shih5100c742019-09-02 10:28:32 +0800378 if args['debug']:
379 pprint.pprint(args)
380
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800381 re_matches = (
Tzung-Bi Shihe1e0e7d2019-09-02 15:04:38 +0800382 (re.compile(r'^pw://(([^/]+)/)?(\d+)'), _match_patchwork),
Stephen Boydb68c17a2019-09-26 15:08:02 -0700383 (re.compile(r'^msgid://<?([^>]*)>?'), _match_msgid),
Tzung-Bi Shihe1e0e7d2019-09-02 15:04:38 +0800384 (re.compile(r'^linux://([0-9a-f]+)'), _match_linux),
385 (re.compile(r'^(from)?git://([^/\#]+)/([^#]+)/([0-9a-f]+)$'),
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800386 _match_fromgit),
Tzung-Bi Shihe1e0e7d2019-09-02 15:04:38 +0800387 (re.compile(r'^((git|https)://.+)#(.+)/([0-9a-f]+)$'), _match_gitfetch),
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800388 )
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800389
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800390 for location in args['locations']:
Tzung-Bi Shih5100c742019-09-02 10:28:32 +0800391 if args['debug']:
392 print('location=%s' % location)
393
Tzung-Bi Shih886c9092019-09-02 12:46:16 +0800394 for reg, handler in re_matches:
395 match = reg.match(location)
396 if match:
397 ret = handler(match, args)
398 break
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800399 else:
Tzung-Bi Shih436fdba2019-09-04 19:05:00 +0800400 errprint('Don\'t know what "%s" means.' % location)
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800401 sys.exit(1)
402
403 if ret != 0:
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -0700404 conflicts = _get_conflicts()
Douglas Anderson2108e532018-04-30 09:50:42 -0700405 if args['tag'] == 'UPSTREAM: ':
406 args['tag'] = 'BACKPORT: '
407 else:
408 args['tag'] = 'BACKPORT: ' + args['tag']
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -0700409 _pause_for_merge(conflicts)
410 else:
Douglas Andersonb6a10fe2019-08-12 13:53:30 -0700411 conflicts = ''
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800412
413 # extract commit message
414 commit_message = subprocess.check_output(
415 ['git', 'show', '-s', '--format=%B', 'HEAD']
416 ).strip('\n')
417
Guenter Roeck2e4f2512018-04-24 09:20:51 -0700418 # Remove stray Change-Id, most likely from merge resolution
419 commit_message = re.sub(r'Change-Id:.*\n?', '', commit_message)
420
Brian Norris7a41b982018-06-01 10:28:29 -0700421 # Note the source location before tagging anything else
422 commit_message += '\n' + args['source_line']
423
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800424 # add automatic Change ID, BUG, and TEST (and maybe signoff too) so
425 # next commands know where to work on
426 commit_message += '\n'
Guenter Roeckbdbb9cc2018-04-19 10:05:08 -0700427 commit_message += conflicts
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800428 commit_message += '\n' + 'BUG=' + args['bug']
Harry Cuttsae372f32019-02-12 18:01:14 -0800429 commit_message += '\n' + _wrap_commit_line('TEST', args['test'])
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800430 if args['signoff']:
431 extra = ['-s']
432 else:
433 extra = []
Stephen Boydaa4e7e02018-11-09 08:48:46 -0800434 subprocess.Popen(
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800435 ['git', 'commit'] + extra + ['--amend', '-F', '-'],
436 stdin=subprocess.PIPE
437 ).communicate(commit_message)
438
439 # re-extract commit message
440 commit_message = subprocess.check_output(
441 ['git', 'show', '-s', '--format=%B', 'HEAD']
442 ).strip('\n')
443
Douglas Andersonbecd4e62019-09-25 13:40:55 -0700444 # If we see a "Link: " that seems to point to a Message-Id with an
445 # automatic Change-Id we'll snarf it out.
446 mo = re.search(r'^Link:.*(I[a-f0-9]{40})@changeid', commit_message,
447 re.MULTILINE)
448 if mo and args['changeid'] is None:
449 args['changeid'] = mo.group(1)
450
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800451 # replace changeid if needed
452 if args['changeid'] is not None:
453 commit_message = re.sub(r'(Change-Id: )(\w+)', r'\1%s' %
454 args['changeid'], commit_message)
455 args['changeid'] = None
456
457 # decorate it that it's from outside
458 commit_message = args['tag'] + commit_message
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800459
460 # commit everything
Stephen Boydaa4e7e02018-11-09 08:48:46 -0800461 subprocess.Popen(
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800462 ['git', 'commit', '--amend', '-F', '-'], stdin=subprocess.PIPE
463 ).communicate(commit_message)
464
Chirantan Ekbote4b08e712019-06-12 15:35:41 +0900465 return 0
Alexandru M Stanfb5b5ee2014-12-04 13:32:55 -0800466
467if __name__ == '__main__':
468 sys.exit(main(sys.argv[1:]))