blob: 7f19fccf2934cb1597095373dc1ca114f4a051e1 [file] [log] [blame]
Michael Bestas3952f6c2016-08-26 01:12:08 +03001#!/usr/bin/env python
2#
3# Copyright (C) 2013-15 The CyanogenMod Project
Dan Pasanen0fdc0852016-12-27 10:32:16 -06004# (C) 2017 The LineageOS Project
Michael Bestas3952f6c2016-08-26 01:12:08 +03005#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19#
20# Run repopick.py -h for a description of this utility.
21#
22
23from __future__ import print_function
24
25import sys
26import json
27import os
28import subprocess
29import re
30import argparse
31import textwrap
Gabriele M5b610ae2018-03-31 14:26:59 +020032from functools import cmp_to_key
Michael Bestas3952f6c2016-08-26 01:12:08 +030033from xml.etree import ElementTree
34
35try:
Dan Pasanen1cdd3802017-01-23 15:08:52 -060036 import requests
Michael Bestas3952f6c2016-08-26 01:12:08 +030037except ImportError:
Dan Pasanen1cdd3802017-01-23 15:08:52 -060038 try:
39 # For python3
40 import urllib.error
41 import urllib.request
42 except ImportError:
43 # For python2
44 import imp
45 import urllib2
46 urllib = imp.new_module('urllib')
47 urllib.error = urllib2
48 urllib.request = urllib2
Michael Bestas3952f6c2016-08-26 01:12:08 +030049
50
Luca Weiss5ee35ea2018-11-25 14:07:12 +010051# cmp() is not available in Python 3, define it manually
52# See https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons
53def cmp(a, b):
54 return (a > b) - (a < b)
55
56
Michael Bestas3952f6c2016-08-26 01:12:08 +030057# Verifies whether pathA is a subdirectory (or the same) as pathB
58def is_subdir(a, b):
59 a = os.path.realpath(a) + '/'
60 b = os.path.realpath(b) + '/'
61 return b == a[:len(b)]
62
63
64def fetch_query_via_ssh(remote_url, query):
65 """Given a remote_url and a query, return the list of changes that fit it
66 This function is slightly messy - the ssh api does not return data in the same structure as the HTTP REST API
67 We have to get the data, then transform it to match what we're expecting from the HTTP RESET API"""
68 if remote_url.count(':') == 2:
69 (uri, userhost, port) = remote_url.split(':')
70 userhost = userhost[2:]
71 elif remote_url.count(':') == 1:
72 (uri, userhost) = remote_url.split(':')
73 userhost = userhost[2:]
74 port = 29418
75 else:
76 raise Exception('Malformed URI: Expecting ssh://[user@]host[:port]')
77
Michael Bestas3952f6c2016-08-26 01:12:08 +030078 out = subprocess.check_output(['ssh', '-x', '-p{0}'.format(port), userhost, 'gerrit', 'query', '--format=JSON --patch-sets --current-patch-set', query])
79 if not hasattr(out, 'encode'):
80 out = out.decode()
81 reviews = []
82 for line in out.split('\n'):
83 try:
84 data = json.loads(line)
85 # make our data look like the http rest api data
86 review = {
87 'branch': data['branch'],
88 'change_id': data['id'],
89 'current_revision': data['currentPatchSet']['revision'],
90 'number': int(data['number']),
91 'revisions': {patch_set['revision']: {
Gabriele M0fcc1222018-04-10 18:35:12 +020092 '_number': int(patch_set['number']),
Michael Bestas3952f6c2016-08-26 01:12:08 +030093 'fetch': {
94 'ssh': {
95 'ref': patch_set['ref'],
96 'url': 'ssh://{0}:{1}/{2}'.format(userhost, port, data['project'])
97 }
Gabriele M0fcc1222018-04-10 18:35:12 +020098 },
99 'commit': {
Aayush Guptad395aa22020-07-26 07:19:19 +0000100 'parents': [{'commit': parent} for parent in patch_set['parents']]
Gabriele M0fcc1222018-04-10 18:35:12 +0200101 },
Michael Bestas3952f6c2016-08-26 01:12:08 +0300102 } for patch_set in data['patchSets']},
103 'subject': data['subject'],
104 'project': data['project'],
105 'status': data['status']
106 }
107 reviews.append(review)
108 except:
109 pass
110 args.quiet or print('Found {0} reviews'.format(len(reviews)))
111 return reviews
112
113
114def fetch_query_via_http(remote_url, query):
Dan Pasanen1cdd3802017-01-23 15:08:52 -0600115 if "requests" in sys.modules:
116 auth = None
117 if os.path.isfile(os.getenv("HOME") + "/.gerritrc"):
118 f = open(os.getenv("HOME") + "/.gerritrc", "r")
119 for line in f:
120 parts = line.rstrip().split("|")
121 if parts[0] in remote_url:
122 auth = requests.auth.HTTPBasicAuth(username=parts[1], password=parts[2])
Aayush Guptad395aa22020-07-26 07:19:19 +0000123 status_code = '-1'
Dan Pasanen1cdd3802017-01-23 15:08:52 -0600124 if auth:
Gabriele M5b610ae2018-03-31 14:26:59 +0200125 url = '{0}/a/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS'.format(remote_url, query)
Dan Pasanen1cdd3802017-01-23 15:08:52 -0600126 data = requests.get(url, auth=auth)
Aayush Guptad395aa22020-07-26 07:19:19 +0000127 status_code = str(data.status_code)
128 if status_code != '200':
Dan Pasanen1cdd3802017-01-23 15:08:52 -0600129 #They didn't get good authorization or data, Let's try the old way
Gabriele M5b610ae2018-03-31 14:26:59 +0200130 url = '{0}/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS'.format(remote_url, query)
Dan Pasanen1cdd3802017-01-23 15:08:52 -0600131 data = requests.get(url)
132 reviews = json.loads(data.text[5:])
133 else:
134 """Given a query, fetch the change numbers via http"""
Gabriele M5b610ae2018-03-31 14:26:59 +0200135 url = '{0}/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS'.format(remote_url, query)
Dan Pasanen1cdd3802017-01-23 15:08:52 -0600136 data = urllib.request.urlopen(url).read().decode('utf-8')
137 reviews = json.loads(data[5:])
Michael Bestas3952f6c2016-08-26 01:12:08 +0300138
139 for review in reviews:
140 review['number'] = review.pop('_number')
141
142 return reviews
143
144
145def fetch_query(remote_url, query):
146 """Wrapper for fetch_query_via_proto functions"""
147 if remote_url[0:3] == 'ssh':
148 return fetch_query_via_ssh(remote_url, query)
149 elif remote_url[0:4] == 'http':
150 return fetch_query_via_http(remote_url, query.replace(' ', '+'))
151 else:
152 raise Exception('Gerrit URL should be in the form http[s]://hostname/ or ssh://[user@]host[:port]')
153
Aayush Guptad395aa22020-07-26 07:19:19 +0000154
Michael Bestas3952f6c2016-08-26 01:12:08 +0300155if __name__ == '__main__':
Jackeagled6811aa2019-09-24 08:26:40 +0200156 # Default to BlissRoms Gerrit
Jackeaglea3249322021-07-15 05:43:47 +0200157 default_gerrit = 'https://review.blissroms.org'
Michael Bestas3952f6c2016-08-26 01:12:08 +0300158
159 parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent('''\
160 repopick.py is a utility to simplify the process of cherry picking
Jackeaglea3249322021-07-15 05:43:47 +0200161 patches from BlissRoms Gerrit instance (or any gerrit instance of your choosing)
Michael Bestas3952f6c2016-08-26 01:12:08 +0300162
163 Given a list of change numbers, repopick will cd into the project path
164 and cherry pick the latest patch available.
165
166 With the --start-branch argument, the user can specify that a branch
167 should be created before cherry picking. This is useful for
168 cherry-picking many patches into a common branch which can be easily
169 abandoned later (good for testing other's changes.)
170
171 The --abandon-first argument, when used in conjunction with the
172 --start-branch option, will cause repopick to abandon the specified
173 branch in all repos first before performing any cherry picks.'''))
Aayush Gupta5fd8e2b2020-07-26 06:47:01 +0000174 parser.add_argument('change_number', nargs='*',
175 help='change number to cherry pick. Use {change number}/{patchset number} to get a specific revision.')
176 parser.add_argument('-i', '--ignore-missing', action='store_true',
177 help='do not error out if a patch applies to a missing directory')
178 parser.add_argument('-s', '--start-branch', nargs=1,
179 metavar='', help='start the specified branch before cherry picking')
180 parser.add_argument('-r', '--reset', action='store_true',
181 help='reset to initial state (abort cherry-pick) if there is a conflict')
182 parser.add_argument('-a', '--abandon-first', action='store_true',
183 help='before cherry picking, abandon the branch specified in --start-branch')
184 parser.add_argument('-b', '--auto-branch', action='store_true',
185 help='shortcut to "--start-branch auto --abandon-first --ignore-missing"')
Michael Bestas3952f6c2016-08-26 01:12:08 +0300186 parser.add_argument('-q', '--quiet', action='store_true', help='print as little as possible')
187 parser.add_argument('-v', '--verbose', action='store_true', help='print extra information to aid in debug')
188 parser.add_argument('-f', '--force', action='store_true', help='force cherry pick even if change is closed')
189 parser.add_argument('-p', '--pull', action='store_true', help='execute pull instead of cherry-pick')
Aayush Gupta5fd8e2b2020-07-26 06:47:01 +0000190 parser.add_argument('-P', '--path', metavar='', help='use the specified path for the change')
191 parser.add_argument('-t', '--topic', metavar='', help='pick all commits from a specified topic')
192 parser.add_argument('-Q', '--query', metavar='', help='pick all commits using the specified query')
193 parser.add_argument('-g', '--gerrit', default=default_gerrit,
194 metavar='', help='Gerrit Instance to use. Form proto://[user@]host[:port]')
195 parser.add_argument('-e', '--exclude', nargs=1,
196 metavar='', help='exclude a list of commit numbers separated by a ,')
197 parser.add_argument('-c', '--check-picked', type=int, default=10,
198 metavar='', help='pass the amount of commits to check for already picked changes')
Michael Bestas3952f6c2016-08-26 01:12:08 +0300199 args = parser.parse_args()
200 if not args.start_branch and args.abandon_first:
201 parser.error('if --abandon-first is set, you must also give the branch name with --start-branch')
202 if args.auto_branch:
203 args.abandon_first = True
204 args.ignore_missing = True
205 if not args.start_branch:
206 args.start_branch = ['auto']
207 if args.quiet and args.verbose:
208 parser.error('--quiet and --verbose cannot be specified together')
209
210 if (1 << bool(args.change_number) << bool(args.topic) << bool(args.query)) != 2:
211 parser.error('One (and only one) of change_number, topic, and query are allowed')
212
213 # Change current directory to the top of the tree
214 if 'ANDROID_BUILD_TOP' in os.environ:
215 top = os.environ['ANDROID_BUILD_TOP']
216
217 if not is_subdir(os.getcwd(), top):
218 sys.stderr.write('ERROR: You must run this tool from within $ANDROID_BUILD_TOP!\n')
219 sys.exit(1)
220 os.chdir(os.environ['ANDROID_BUILD_TOP'])
221
222 # Sanity check that we are being run from the top level of the tree
223 if not os.path.isdir('.repo'):
224 sys.stderr.write('ERROR: No .repo directory found. Please run this from the top of your tree.\n')
225 sys.exit(1)
226
227 # If --abandon-first is given, abandon the branch before starting
228 if args.abandon_first:
229 # Determine if the branch already exists; skip the abandon if it does not
230 plist = subprocess.check_output(['repo', 'info'])
231 if not hasattr(plist, 'encode'):
232 plist = plist.decode()
233 needs_abandon = False
234 for pline in plist.splitlines():
235 matchObj = re.match(r'Local Branches.*\[(.*)\]', pline)
236 if matchObj:
237 local_branches = re.split('\s*,\s*', matchObj.group(1))
238 if any(args.start_branch[0] in s for s in local_branches):
239 needs_abandon = True
240
241 if needs_abandon:
242 # Perform the abandon only if the branch already exists
243 if not args.quiet:
244 print('Abandoning branch: %s' % args.start_branch[0])
245 subprocess.check_output(['repo', 'abandon', args.start_branch[0]])
246 if not args.quiet:
247 print('')
248
249 # Get the master manifest from repo
250 # - convert project name and revision to a path
251 project_name_to_data = {}
252 manifest = subprocess.check_output(['repo', 'manifest'])
253 xml_root = ElementTree.fromstring(manifest)
254 projects = xml_root.findall('project')
255 remotes = xml_root.findall('remote')
256 default_revision = xml_root.findall('default')[0].get('revision')
257
Aayush Guptad395aa22020-07-26 07:19:19 +0000258 # dump project data into the a list of dicts with the following data:
259 # {project: {path, revision}}
Michael Bestas3952f6c2016-08-26 01:12:08 +0300260
261 for project in projects:
262 name = project.get('name')
Aaron Klingd97bfec2020-06-19 18:43:16 -0500263 # when name and path are equal, "repo manifest" doesn't return a path at all, so fall back to name
264 path = project.get('path', name)
Aaron Kling7d6601c2020-06-30 14:22:55 -0500265 revision = project.get('upstream')
Michael Bestas3952f6c2016-08-26 01:12:08 +0300266 if revision is None:
267 for remote in remotes:
268 if remote.get('name') == project.get('remote'):
269 revision = remote.get('revision')
270 if revision is None:
LuK13379098b942020-11-23 10:55:24 +0100271 revision = project.get('revision', default_revision)
Michael Bestas3952f6c2016-08-26 01:12:08 +0300272
Aayush Guptad395aa22020-07-26 07:19:19 +0000273 if name not in project_name_to_data:
Michael Bestas3952f6c2016-08-26 01:12:08 +0300274 project_name_to_data[name] = {}
275 revision = revision.split('refs/heads/')[-1]
276 project_name_to_data[name][revision] = path
277
278 # get data on requested changes
279 reviews = []
280 change_numbers = []
Gabriele M5b610ae2018-03-31 14:26:59 +0200281
282 def cmp_reviews(review_a, review_b):
283 current_a = review_a['current_revision']
284 parents_a = [r['commit'] for r in review_a['revisions'][current_a]['commit']['parents']]
285 current_b = review_b['current_revision']
286 parents_b = [r['commit'] for r in review_b['revisions'][current_b]['commit']['parents']]
287 if current_a in parents_b:
288 return -1
289 elif current_b in parents_a:
290 return 1
291 else:
292 return cmp(review_a['number'], review_b['number'])
293
Michael Bestas3952f6c2016-08-26 01:12:08 +0300294 if args.topic:
295 reviews = fetch_query(args.gerrit, 'topic:{0}'.format(args.topic))
Gabriele M5b610ae2018-03-31 14:26:59 +0200296 change_numbers = [str(r['number']) for r in sorted(reviews, key=cmp_to_key(cmp_reviews))]
Michael Bestas3952f6c2016-08-26 01:12:08 +0300297 if args.query:
298 reviews = fetch_query(args.gerrit, args.query)
Gabriele M5b610ae2018-03-31 14:26:59 +0200299 change_numbers = [str(r['number']) for r in sorted(reviews, key=cmp_to_key(cmp_reviews))]
Michael Bestas3952f6c2016-08-26 01:12:08 +0300300 if args.change_number:
Gabriele Maf970b62018-04-01 17:50:58 +0200301 change_url_re = re.compile('https?://.+?/([0-9]+(?:/[0-9]+)?)/?')
Michael Bestas3952f6c2016-08-26 01:12:08 +0300302 for c in args.change_number:
Gabriele Maf970b62018-04-01 17:50:58 +0200303 change_number = change_url_re.findall(c)
304 if change_number:
305 change_numbers.extend(change_number)
306 elif '-' in c:
Michael Bestas3952f6c2016-08-26 01:12:08 +0300307 templist = c.split('-')
308 for i in range(int(templist[0]), int(templist[1]) + 1):
309 change_numbers.append(str(i))
310 else:
311 change_numbers.append(c)
312 reviews = fetch_query(args.gerrit, ' OR '.join('change:{0}'.format(x.split('/')[0]) for x in change_numbers))
313
314 # make list of things to actually merge
315 mergables = []
316
317 # If --exclude is given, create the list of commits to ignore
318 exclude = []
319 if args.exclude:
320 exclude = args.exclude[0].split(',')
321
322 for change in change_numbers:
323 patchset = None
324 if '/' in change:
325 (change, patchset) = change.split('/')
326
327 if change in exclude:
328 continue
329
330 change = int(change)
LuK1337ad5d9a02017-03-24 23:25:13 +0100331
Gabriele Mde9e0ae2018-04-01 17:50:55 +0200332 if patchset:
LuK1337ad5d9a02017-03-24 23:25:13 +0100333 patchset = int(patchset)
334
Michael Bestas3952f6c2016-08-26 01:12:08 +0300335 review = next((x for x in reviews if x['number'] == change), None)
336 if review is None:
337 print('Change %d not found, skipping' % change)
338 continue
339
340 mergables.append({
341 'subject': review['subject'],
342 'project': review['project'],
343 'branch': review['branch'],
344 'change_id': review['change_id'],
345 'change_number': review['number'],
346 'status': review['status'],
Gabriele M88c0e5d2018-04-01 17:50:57 +0200347 'fetch': None,
348 'patchset': review['revisions'][review['current_revision']]['_number'],
Michael Bestas3952f6c2016-08-26 01:12:08 +0300349 })
Gabriele M88c0e5d2018-04-01 17:50:57 +0200350
Michael Bestas3952f6c2016-08-26 01:12:08 +0300351 mergables[-1]['fetch'] = review['revisions'][review['current_revision']]['fetch']
352 mergables[-1]['id'] = change
353 if patchset:
354 try:
LuK133727564182017-03-24 20:00:48 +0100355 mergables[-1]['fetch'] = [review['revisions'][x]['fetch'] for x in review['revisions'] if review['revisions'][x]['_number'] == patchset][0]
Michael Bestas3952f6c2016-08-26 01:12:08 +0300356 mergables[-1]['id'] = '{0}/{1}'.format(change, patchset)
Gabriele M88c0e5d2018-04-01 17:50:57 +0200357 mergables[-1]['patchset'] = patchset
Michael Bestas3952f6c2016-08-26 01:12:08 +0300358 except (IndexError, ValueError):
359 args.quiet or print('ERROR: The patch set {0}/{1} could not be found, using CURRENT_REVISION instead.'.format(change, patchset))
360
361 for item in mergables:
362 args.quiet or print('Applying change number {0}...'.format(item['id']))
363 # Check if change is open and exit if it's not, unless -f is specified
Dan Pasanen63f767e2017-07-09 09:41:33 -0500364 if (item['status'] != 'OPEN' and item['status'] != 'NEW' and item['status'] != 'DRAFT') and not args.query:
Michael Bestas3952f6c2016-08-26 01:12:08 +0300365 if args.force:
366 print('!! Force-picking a closed change !!\n')
367 else:
368 print('Change status is ' + item['status'] + '. Skipping the cherry pick.\nUse -f to force this pick.')
369 continue
370
371 # Convert the project name to a project path
372 # - check that the project path exists
373 project_path = None
374
375 if item['project'] in project_name_to_data and item['branch'] in project_name_to_data[item['project']]:
376 project_path = project_name_to_data[item['project']][item['branch']]
377 elif args.path:
378 project_path = args.path
Adrian DC045f6642019-10-13 12:34:06 +0200379 elif item['project'] in project_name_to_data and len(project_name_to_data[item['project']]) == 1:
380 local_branch = list(project_name_to_data[item['project']])[0]
381 project_path = project_name_to_data[item['project']][local_branch]
382 print('WARNING: Project {0} has a different branch ("{1}" != "{2}")'.format(project_path, local_branch, item['branch']))
Michael Bestas3952f6c2016-08-26 01:12:08 +0300383 elif args.ignore_missing:
384 print('WARNING: Skipping {0} since there is no project directory for: {1}\n'.format(item['id'], item['project']))
385 continue
386 else:
387 sys.stderr.write('ERROR: For {0}, could not determine the project path for project {1}\n'.format(item['id'], item['project']))
388 sys.exit(1)
389
390 # If --start-branch is given, create the branch (more than once per path is okay; repo ignores gracefully)
391 if args.start_branch:
392 subprocess.check_output(['repo', 'start', args.start_branch[0], project_path])
393
394 # Determine the maximum commits to check already picked changes
Adrian DCdf290222017-09-07 22:59:54 +0200395 check_picked_count = args.check_picked
Michael Bestas3952f6c2016-08-26 01:12:08 +0300396 branch_commits_count = int(subprocess.check_output(['git', 'rev-list', '--count', 'HEAD'], cwd=project_path))
397 if branch_commits_count <= check_picked_count:
398 check_picked_count = branch_commits_count - 1
399
400 # Check if change is already picked to HEAD...HEAD~check_picked_count
401 found_change = False
402 for i in range(0, check_picked_count):
Adrian DC13b02ff2016-12-04 12:30:26 +0100403 if subprocess.call(['git', 'cat-file', '-e', 'HEAD~{0}'.format(i)], cwd=project_path, stderr=open(os.devnull, 'wb')):
404 continue
Simon Shieldsd5e35c92019-11-18 23:56:08 +1100405 output = subprocess.check_output(['git', 'show', '-q', 'HEAD~{0}'.format(i)], cwd=project_path)
406 # make sure we have a string on Python 3
407 if isinstance(output, bytes):
408 output = output.decode('utf-8')
409 output = output.split()
Michael Bestas3952f6c2016-08-26 01:12:08 +0300410 if 'Change-Id:' in output:
411 head_change_id = ''
Aayush Guptad395aa22020-07-26 07:19:19 +0000412 for j, t in enumerate(reversed(output)):
Michael Bestas3952f6c2016-08-26 01:12:08 +0300413 if t == 'Change-Id:':
414 head_change_id = output[len(output) - j]
415 break
416 if head_change_id.strip() == item['change_id']:
417 print('Skipping {0} - already picked in {1} as HEAD~{2}'.format(item['id'], project_path, i))
418 found_change = True
419 break
420 if found_change:
421 continue
422
423 # Print out some useful info
424 if not args.quiet:
LuK1337c62a9fb2019-09-21 11:47:33 +0200425 print(u'--> Subject: "{0}"'.format(item['subject']))
Michael Bestas3952f6c2016-08-26 01:12:08 +0300426 print('--> Project path: {0}'.format(project_path))
Gabriele M88c0e5d2018-04-01 17:50:57 +0200427 print('--> Change number: {0} (Patch Set {1})'.format(item['id'], item['patchset']))
Michael Bestas3952f6c2016-08-26 01:12:08 +0300428
429 if 'anonymous http' in item['fetch']:
430 method = 'anonymous http'
431 else:
432 method = 'ssh'
433
434 # Try fetching from GitHub first if using default gerrit
435 if args.gerrit == default_gerrit:
436 if args.verbose:
437 print('Trying to fetch the change from GitHub')
438
439 if args.pull:
Jackeaglea3249322021-07-15 05:43:47 +0200440 cmd = ['git pull --no-edit BlissRoms', item['fetch'][method]['ref']]
Michael Bestas3952f6c2016-08-26 01:12:08 +0300441 else:
Jackeaglea3249322021-07-15 05:43:47 +0200442 cmd = ['git fetch BlissRoms', item['fetch'][method]['ref']]
Michael Bestas3952f6c2016-08-26 01:12:08 +0300443 if args.quiet:
444 cmd.append('--quiet')
445 else:
446 print(cmd)
447 result = subprocess.call([' '.join(cmd)], cwd=project_path, shell=True)
448 FETCH_HEAD = '{0}/.git/FETCH_HEAD'.format(project_path)
449 if result != 0 and os.stat(FETCH_HEAD).st_size != 0:
450 print('ERROR: git command failed')
451 sys.exit(result)
452 # Check if it worked
453 if args.gerrit != default_gerrit or os.stat(FETCH_HEAD).st_size == 0:
454 # If not using the default gerrit or github failed, fetch from gerrit.
455 if args.verbose:
456 if args.gerrit == default_gerrit:
Jackeaglea3249322021-07-15 05:43:47 +0200457 print('Fetching from BlissRoms didn\'t work, trying to fetch the change from Gerrit')
Michael Bestas3952f6c2016-08-26 01:12:08 +0300458 else:
459 print('Fetching from {0}'.format(args.gerrit))
460
461 if args.pull:
462 cmd = ['git pull --no-edit', item['fetch'][method]['url'], item['fetch'][method]['ref']]
463 else:
464 cmd = ['git fetch', item['fetch'][method]['url'], item['fetch'][method]['ref']]
465 if args.quiet:
466 cmd.append('--quiet')
467 else:
468 print(cmd)
469 result = subprocess.call([' '.join(cmd)], cwd=project_path, shell=True)
470 if result != 0:
471 print('ERROR: git command failed')
472 sys.exit(result)
473 # Perform the cherry-pick
474 if not args.pull:
Tim Schumacher49d26ba2018-10-13 14:37:54 +0200475 cmd = ['git cherry-pick --ff FETCH_HEAD']
Michael Bestas3952f6c2016-08-26 01:12:08 +0300476 if args.quiet:
477 cmd_out = open(os.devnull, 'wb')
478 else:
479 cmd_out = None
480 result = subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
481 if result != 0:
Adrian DC0f8230b2018-08-30 23:07:23 +0200482 cmd = ['git diff-index --quiet HEAD --']
483 result = subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
484 if result == 0:
485 print('WARNING: git command resulted with an empty commit, aborting cherry-pick')
486 cmd = ['git cherry-pick --abort']
487 subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
488 elif args.reset:
Harry Youd1c9c5a32017-07-18 18:52:42 +0100489 print('ERROR: git command failed, aborting cherry-pick')
490 cmd = ['git cherry-pick --abort']
491 subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
Adrian DC0f8230b2018-08-30 23:07:23 +0200492 sys.exit(result)
Harry Youd1c9c5a32017-07-18 18:52:42 +0100493 else:
494 print('ERROR: git command failed')
Adrian DC0f8230b2018-08-30 23:07:23 +0200495 sys.exit(result)
Michael Bestas3952f6c2016-08-26 01:12:08 +0300496 if not args.quiet:
497 print('')
Jackeaglea3249322021-07-15 05:43:47 +0200498