blob: cfae856b4312065ed9ad60f9ad92d75b619610da [file] [log] [blame]
Pulser72e23242013-09-29 09:56:55 +01001#!/usr/bin/env python
2#
Marko Manb58468a2018-03-19 13:01:19 +01003# Copyright (C) 2013-15 The CyanogenMod Project
4# (C) 2017 The LineageOS Project
Pulser72e23242013-09-29 09:56:55 +01005#
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 Md91609d2018-03-31 14:26:59 +020032from functools import cmp_to_key
Marko Manb58468a2018-03-19 13:01:19 +010033from xml.etree import ElementTree
Pulser72e23242013-09-29 09:56:55 +010034
35try:
Marko Manb58468a2018-03-19 13:01:19 +010036 import requests
Pulser72e23242013-09-29 09:56:55 +010037except ImportError:
Marko Manb58468a2018-03-19 13:01:19 +010038 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
Pulser72e23242013-09-29 09:56:55 +010049
Pulser72e23242013-09-29 09:56:55 +010050
Luca Weissd1bbac62018-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
Pulser72e23242013-09-29 09:56:55 +010057# Verifies whether pathA is a subdirectory (or the same) as pathB
Marko Manb58468a2018-03-19 13:01:19 +010058def is_subdir(a, b):
59 a = os.path.realpath(a) + '/'
60 b = os.path.realpath(b) + '/'
61 return b == a[:len(b)]
Pulser72e23242013-09-29 09:56:55 +010062
Pulser72e23242013-09-29 09:56:55 +010063
Marko Manb58468a2018-03-19 13:01:19 +010064def 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
Pulser72e23242013-09-29 09:56:55 +010075 else:
Marko Manb58468a2018-03-19 13:01:19 +010076 raise Exception('Malformed URI: Expecting ssh://[user@]host[:port]')
Pulser72e23242013-09-29 09:56:55 +010077
Marko Manb58468a2018-03-19 13:01:19 +010078 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 Mb12913b2018-04-10 18:35:12 +020092 '_number': int(patch_set['number']),
Marko Manb58468a2018-03-19 13:01:19 +010093 'fetch': {
94 'ssh': {
95 'ref': patch_set['ref'],
96 'url': 'ssh://{0}:{1}/{2}'.format(userhost, port, data['project'])
97 }
Gabriele Mb12913b2018-04-10 18:35:12 +020098 },
99 'commit': {
Aayush Gupta2f4522c2020-07-26 07:19:19 +0000100 'parents': [{'commit': parent} for parent in patch_set['parents']]
Gabriele Mb12913b2018-04-10 18:35:12 +0200101 },
Marko Manb58468a2018-03-19 13:01:19 +0100102 } 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
Pulser72e23242013-09-29 09:56:55 +0100112
Pulser72e23242013-09-29 09:56:55 +0100113
Marko Manb58468a2018-03-19 13:01:19 +0100114def fetch_query_via_http(remote_url, query):
115 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 Gupta2f4522c2020-07-26 07:19:19 +0000123 status_code = '-1'
Marko Manb58468a2018-03-19 13:01:19 +0100124 if auth:
Gabriele Md91609d2018-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)
Marko Manb58468a2018-03-19 13:01:19 +0100126 data = requests.get(url, auth=auth)
Aayush Gupta2f4522c2020-07-26 07:19:19 +0000127 status_code = str(data.status_code)
128 if status_code != '200':
Marko Manb58468a2018-03-19 13:01:19 +0100129 #They didn't get good authorization or data, Let's try the old way
Gabriele Md91609d2018-03-31 14:26:59 +0200130 url = '{0}/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS'.format(remote_url, query)
Marko Manb58468a2018-03-19 13:01:19 +0100131 data = requests.get(url)
132 reviews = json.loads(data.text[5:])
133 else:
134 """Given a query, fetch the change numbers via http"""
Gabriele Md91609d2018-03-31 14:26:59 +0200135 url = '{0}/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS&o=ALL_COMMITS'.format(remote_url, query)
Marko Manb58468a2018-03-19 13:01:19 +0100136 data = urllib.request.urlopen(url).read().decode('utf-8')
137 reviews = json.loads(data[5:])
138
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 Gupta2f4522c2020-07-26 07:19:19 +0000154
Marko Manb58468a2018-03-19 13:01:19 +0100155if __name__ == '__main__':
156 # Default to OmniRom Gerrit
157 default_gerrit = 'https://gerrit.omnirom.org'
158
159 parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent('''\
160 repopick.py is a utility to simplify the process of cherry picking
161 patches from OmniRom's Gerrit instance (or any gerrit instance of your choosing)
162
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 Gupta3ed46672020-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"')
Marko Manb58468a2018-03-19 13:01:19 +0100186 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 Gupta3ed46672020-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')
Marko Manb58468a2018-03-19 13:01:19 +0100199 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')
Pulser72e23242013-09-29 09:56:55 +0100225 sys.exit(1)
226
Marko Manb58468a2018-03-19 13:01:19 +0100227 # 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
Pulser72e23242013-09-29 09:56:55 +0100240
Marko Manb58468a2018-03-19 13:01:19 +0100241 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('')
Pulser72e23242013-09-29 09:56:55 +0100248
Marko Manb58468a2018-03-19 13:01:19 +0100249 # 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 Gupta2f4522c2020-07-26 07:19:19 +0000258 # dump project data into the a list of dicts with the following data:
259 # {project: {path, revision}}
Marko Manb58468a2018-03-19 13:01:19 +0100260
261 for project in projects:
262 name = project.get('name')
Aaron Kling83fee0f2020-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 Klingeba6d822020-06-30 14:22:55 -0500265 revision = project.get('upstream')
Marko Manb58468a2018-03-19 13:01:19 +0100266 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:
LuK13375c1307d2020-11-23 10:55:24 +0100271 revision = project.get('revision', default_revision)
Marko Manb58468a2018-03-19 13:01:19 +0100272
Aayush Gupta2f4522c2020-07-26 07:19:19 +0000273 if name not in project_name_to_data:
Marko Manb58468a2018-03-19 13:01:19 +0100274 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 Md91609d2018-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
Marko Manb58468a2018-03-19 13:01:19 +0100294 if args.topic:
295 reviews = fetch_query(args.gerrit, 'topic:{0}'.format(args.topic))
Gabriele Md91609d2018-03-31 14:26:59 +0200296 change_numbers = [str(r['number']) for r in sorted(reviews, key=cmp_to_key(cmp_reviews))]
Marko Manb58468a2018-03-19 13:01:19 +0100297 if args.query:
298 reviews = fetch_query(args.gerrit, args.query)
Gabriele Md91609d2018-03-31 14:26:59 +0200299 change_numbers = [str(r['number']) for r in sorted(reviews, key=cmp_to_key(cmp_reviews))]
Marko Manb58468a2018-03-19 13:01:19 +0100300 if args.change_number:
Gabriele M1009aeb2018-04-01 17:50:58 +0200301 change_url_re = re.compile('https?://.+?/([0-9]+(?:/[0-9]+)?)/?')
Marko Manb58468a2018-03-19 13:01:19 +0100302 for c in args.change_number:
Gabriele M1009aeb2018-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:
Marko Manb58468a2018-03-19 13:01:19 +0100307 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)
331
332 if patchset is not None:
333 patchset = int(patchset)
334
335 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 M1188cbd2018-04-01 17:50:57 +0200347 'fetch': None,
348 'patchset': review['revisions'][review['current_revision']]['_number'],
Marko Manb58468a2018-03-19 13:01:19 +0100349 })
Gabriele M1188cbd2018-04-01 17:50:57 +0200350
Marko Manb58468a2018-03-19 13:01:19 +0100351 mergables[-1]['fetch'] = review['revisions'][review['current_revision']]['fetch']
352 mergables[-1]['id'] = change
353 if patchset:
354 try:
355 mergables[-1]['fetch'] = [review['revisions'][x]['fetch'] for x in review['revisions'] if review['revisions'][x]['_number'] == patchset][0]
356 mergables[-1]['id'] = '{0}/{1}'.format(change, patchset)
Gabriele M1188cbd2018-04-01 17:50:57 +0200357 mergables[-1]['patchset'] = patchset
Marko Manb58468a2018-03-19 13:01:19 +0100358 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
364 if (item['status'] != 'OPEN' and item['status'] != 'NEW' and item['status'] != 'DRAFT') and not args.query:
365 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
Pulser72e23242013-09-29 09:56:55 +0100370
371 # Convert the project name to a project path
372 # - check that the project path exists
Marko Manb58468a2018-03-19 13:01:19 +0100373 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 DC76b15f92019-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']))
Pulser72e23242013-09-29 09:56:55 +0100383 elif args.ignore_missing:
Marko Manb58468a2018-03-19 13:01:19 +0100384 print('WARNING: Skipping {0} since there is no project directory for: {1}\n'.format(item['id'], item['project']))
385 continue
Pulser72e23242013-09-29 09:56:55 +0100386 else:
Marko Manb58468a2018-03-19 13:01:19 +0100387 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)
Pulser72e23242013-09-29 09:56:55 +0100389
390 # If --start-branch is given, create the branch (more than once per path is okay; repo ignores gracefully)
391 if args.start_branch:
Marko Manb58468a2018-03-19 13:01:19 +0100392 subprocess.check_output(['repo', 'start', args.start_branch[0], project_path])
393
394 # Determine the maximum commits to check already picked changes
395 check_picked_count = args.check_picked
Michael W7c1f1ab2022-06-03 18:26:14 +0200396 max_count = '--max-count={0}'.format(check_picked_count + 1)
397 branch_commits_count = int(subprocess.check_output(['git', 'rev-list', '--count', max_count, 'HEAD'], cwd=project_path))
Marko Manb58468a2018-03-19 13:01:19 +0100398 if branch_commits_count <= check_picked_count:
399 check_picked_count = branch_commits_count - 1
400
401 # Check if change is already picked to HEAD...HEAD~check_picked_count
402 found_change = False
403 for i in range(0, check_picked_count):
404 if subprocess.call(['git', 'cat-file', '-e', 'HEAD~{0}'.format(i)], cwd=project_path, stderr=open(os.devnull, 'wb')):
405 continue
Simon Shields6a726492019-11-18 23:56:08 +1100406 output = subprocess.check_output(['git', 'show', '-q', 'HEAD~{0}'.format(i)], cwd=project_path)
407 # make sure we have a string on Python 3
408 if isinstance(output, bytes):
409 output = output.decode('utf-8')
410 output = output.split()
Marko Manb58468a2018-03-19 13:01:19 +0100411 if 'Change-Id:' in output:
412 head_change_id = ''
Aayush Gupta2f4522c2020-07-26 07:19:19 +0000413 for j, t in enumerate(reversed(output)):
Marko Manb58468a2018-03-19 13:01:19 +0100414 if t == 'Change-Id:':
415 head_change_id = output[len(output) - j]
416 break
417 if head_change_id.strip() == item['change_id']:
418 print('Skipping {0} - already picked in {1} as HEAD~{2}'.format(item['id'], project_path, i))
419 found_change = True
420 break
421 if found_change:
422 continue
Pulser72e23242013-09-29 09:56:55 +0100423
424 # Print out some useful info
425 if not args.quiet:
LuK133774f08b02019-09-21 11:47:33 +0200426 print(u'--> Subject: "{0}"'.format(item['subject']))
Marko Manb58468a2018-03-19 13:01:19 +0100427 print('--> Project path: {0}'.format(project_path))
Gabriele M1188cbd2018-04-01 17:50:57 +0200428 print('--> Change number: {0} (Patch Set {1})'.format(item['id'], item['patchset']))
Pulser72e23242013-09-29 09:56:55 +0100429
Marko Manb58468a2018-03-19 13:01:19 +0100430 if 'anonymous http' in item['fetch']:
431 method = 'anonymous http'
Pulser72e23242013-09-29 09:56:55 +0100432 else:
Marko Manb58468a2018-03-19 13:01:19 +0100433 method = 'ssh'
Pulser72e23242013-09-29 09:56:55 +0100434
Marko Manb58468a2018-03-19 13:01:19 +0100435 # Try fetching from GitHub first if using default gerrit
436 if args.gerrit == default_gerrit:
437 if args.verbose:
438 print('Trying to fetch the change from GitHub')
439
440 if args.pull:
441 cmd = ['git pull --no-edit omnirom', item['fetch'][method]['ref']]
442 else:
443 cmd = ['git fetch omnirom', item['fetch'][method]['ref']]
444 if args.quiet:
445 cmd.append('--quiet')
446 else:
447 print(cmd)
448 result = subprocess.call([' '.join(cmd)], cwd=project_path, shell=True)
449 FETCH_HEAD = '{0}/.git/FETCH_HEAD'.format(project_path)
450 if result != 0 and os.stat(FETCH_HEAD).st_size != 0:
451 print('ERROR: git command failed')
452 sys.exit(result)
453 # Check if it worked
454 if args.gerrit != default_gerrit or os.stat(FETCH_HEAD).st_size == 0:
455 # If not using the default gerrit or github failed, fetch from gerrit.
456 if args.verbose:
457 if args.gerrit == default_gerrit:
458 print('Fetching from GitHub didn\'t work, trying to fetch the change from Gerrit')
459 else:
460 print('Fetching from {0}'.format(args.gerrit))
461
462 if args.pull:
463 cmd = ['git pull --no-edit', item['fetch'][method]['url'], item['fetch'][method]['ref']]
464 else:
465 cmd = ['git fetch', item['fetch'][method]['url'], item['fetch'][method]['ref']]
466 if args.quiet:
467 cmd.append('--quiet')
468 else:
469 print(cmd)
470 result = subprocess.call([' '.join(cmd)], cwd=project_path, shell=True)
471 if result != 0:
472 print('ERROR: git command failed')
473 sys.exit(result)
474 # Perform the cherry-pick
475 if not args.pull:
Tim Schumacher074b77b2018-10-13 14:37:54 +0200476 cmd = ['git cherry-pick --ff FETCH_HEAD']
Marko Manb58468a2018-03-19 13:01:19 +0100477 if args.quiet:
478 cmd_out = open(os.devnull, 'wb')
479 else:
480 cmd_out = None
481 result = subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
482 if result != 0:
Adrian DC7bc808f2018-08-30 23:07:23 +0200483 cmd = ['git diff-index --quiet HEAD --']
484 result = subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
485 if result == 0:
486 print('WARNING: git command resulted with an empty commit, aborting cherry-pick')
487 cmd = ['git cherry-pick --abort']
488 subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
489 elif args.reset:
Marko Manb58468a2018-03-19 13:01:19 +0100490 print('ERROR: git command failed, aborting cherry-pick')
491 cmd = ['git cherry-pick --abort']
492 subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
Adrian DC7bc808f2018-08-30 23:07:23 +0200493 sys.exit(result)
Marko Manb58468a2018-03-19 13:01:19 +0100494 else:
495 print('ERROR: git command failed')
Adrian DC7bc808f2018-08-30 23:07:23 +0200496 sys.exit(result)
Pulser72e23242013-09-29 09:56:55 +0100497 if not args.quiet:
Marko Manb58468a2018-03-19 13:01:19 +0100498 print('')