blob: 035d5591cc329f5d74b038aa8d5e85608bc0c0ec [file] [log] [blame]
Pulser72e23242013-09-29 09:56:55 +01001#!/usr/bin/env python
2#
dianlujitaoe34c26d2024-01-13 17:33:41 +08003# Copyright (C) 2013-2015 The CyanogenMod Project
4# (C) 2017-2024 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
Pulser72e23242013-09-29 09:56:55 +010023import sys
24import json
25import os
26import subprocess
27import re
28import argparse
29import textwrap
Gabriele Md91609d2018-03-31 14:26:59 +020030from functools import cmp_to_key
Marko Manb58468a2018-03-19 13:01:19 +010031from xml.etree import ElementTree
Pulser72e23242013-09-29 09:56:55 +010032
33try:
Marko Manb58468a2018-03-19 13:01:19 +010034 import requests
Pulser72e23242013-09-29 09:56:55 +010035except ImportError:
dianlujitaoe34c26d2024-01-13 17:33:41 +080036 import urllib.request
Pulser72e23242013-09-29 09:56:55 +010037
Pulser72e23242013-09-29 09:56:55 +010038
Luca Weissd1bbac62018-11-25 14:07:12 +010039# cmp() is not available in Python 3, define it manually
40# See https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons
41def cmp(a, b):
42 return (a > b) - (a < b)
43
44
Pulser72e23242013-09-29 09:56:55 +010045# Verifies whether pathA is a subdirectory (or the same) as pathB
Marko Manb58468a2018-03-19 13:01:19 +010046def is_subdir(a, b):
47 a = os.path.realpath(a) + '/'
48 b = os.path.realpath(b) + '/'
49 return b == a[:len(b)]
Pulser72e23242013-09-29 09:56:55 +010050
Pulser72e23242013-09-29 09:56:55 +010051
Marko Manb58468a2018-03-19 13:01:19 +010052def fetch_query_via_ssh(remote_url, query):
53 """Given a remote_url and a query, return the list of changes that fit it
54 This function is slightly messy - the ssh api does not return data in the same structure as the HTTP REST API
55 We have to get the data, then transform it to match what we're expecting from the HTTP RESET API"""
56 if remote_url.count(':') == 2:
57 (uri, userhost, port) = remote_url.split(':')
58 userhost = userhost[2:]
59 elif remote_url.count(':') == 1:
60 (uri, userhost) = remote_url.split(':')
61 userhost = userhost[2:]
62 port = 29418
Pulser72e23242013-09-29 09:56:55 +010063 else:
Marko Manb58468a2018-03-19 13:01:19 +010064 raise Exception('Malformed URI: Expecting ssh://[user@]host[:port]')
Pulser72e23242013-09-29 09:56:55 +010065
Marko Manb58468a2018-03-19 13:01:19 +010066 out = subprocess.check_output(['ssh', '-x', '-p{0}'.format(port), userhost, 'gerrit', 'query', '--format=JSON --patch-sets --current-patch-set', query])
67 if not hasattr(out, 'encode'):
68 out = out.decode()
69 reviews = []
70 for line in out.split('\n'):
71 try:
72 data = json.loads(line)
73 # make our data look like the http rest api data
74 review = {
75 'branch': data['branch'],
76 'change_id': data['id'],
77 'current_revision': data['currentPatchSet']['revision'],
78 'number': int(data['number']),
79 'revisions': {patch_set['revision']: {
Gabriele Mb12913b2018-04-10 18:35:12 +020080 '_number': int(patch_set['number']),
Marko Manb58468a2018-03-19 13:01:19 +010081 'fetch': {
82 'ssh': {
83 'ref': patch_set['ref'],
84 'url': 'ssh://{0}:{1}/{2}'.format(userhost, port, data['project'])
85 }
Gabriele Mb12913b2018-04-10 18:35:12 +020086 },
87 'commit': {
Aayush Gupta2f4522c2020-07-26 07:19:19 +000088 'parents': [{'commit': parent} for parent in patch_set['parents']]
Gabriele Mb12913b2018-04-10 18:35:12 +020089 },
Marko Manb58468a2018-03-19 13:01:19 +010090 } for patch_set in data['patchSets']},
91 'subject': data['subject'],
92 'project': data['project'],
93 'status': data['status']
94 }
95 reviews.append(review)
96 except:
97 pass
98 args.quiet or print('Found {0} reviews'.format(len(reviews)))
99 return reviews
Pulser72e23242013-09-29 09:56:55 +0100100
Pulser72e23242013-09-29 09:56:55 +0100101
Marko Manb58468a2018-03-19 13:01:19 +0100102def fetch_query_via_http(remote_url, query):
103 if "requests" in sys.modules:
104 auth = None
105 if os.path.isfile(os.getenv("HOME") + "/.gerritrc"):
106 f = open(os.getenv("HOME") + "/.gerritrc", "r")
107 for line in f:
108 parts = line.rstrip().split("|")
109 if parts[0] in remote_url:
110 auth = requests.auth.HTTPBasicAuth(username=parts[1], password=parts[2])
Aayush Gupta2f4522c2020-07-26 07:19:19 +0000111 status_code = '-1'
Marko Manb58468a2018-03-19 13:01:19 +0100112 if auth:
Gabriele Md91609d2018-03-31 14:26:59 +0200113 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 +0100114 data = requests.get(url, auth=auth)
Aayush Gupta2f4522c2020-07-26 07:19:19 +0000115 status_code = str(data.status_code)
116 if status_code != '200':
Marko Manb58468a2018-03-19 13:01:19 +0100117 #They didn't get good authorization or data, Let's try the old way
Gabriele Md91609d2018-03-31 14:26:59 +0200118 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 +0100119 data = requests.get(url)
120 reviews = json.loads(data.text[5:])
121 else:
122 """Given a query, fetch the change numbers via http"""
Gabriele Md91609d2018-03-31 14:26:59 +0200123 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 +0100124 data = urllib.request.urlopen(url).read().decode('utf-8')
125 reviews = json.loads(data[5:])
126
127 for review in reviews:
128 review['number'] = review.pop('_number')
129
130 return reviews
131
132
133def fetch_query(remote_url, query):
134 """Wrapper for fetch_query_via_proto functions"""
135 if remote_url[0:3] == 'ssh':
136 return fetch_query_via_ssh(remote_url, query)
137 elif remote_url[0:4] == 'http':
138 return fetch_query_via_http(remote_url, query.replace(' ', '+'))
139 else:
140 raise Exception('Gerrit URL should be in the form http[s]://hostname/ or ssh://[user@]host[:port]')
141
Aayush Gupta2f4522c2020-07-26 07:19:19 +0000142
Marko Manb58468a2018-03-19 13:01:19 +0100143if __name__ == '__main__':
144 # Default to OmniRom Gerrit
145 default_gerrit = 'https://gerrit.omnirom.org'
146
147 parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent('''\
148 repopick.py is a utility to simplify the process of cherry picking
149 patches from OmniRom's Gerrit instance (or any gerrit instance of your choosing)
150
151 Given a list of change numbers, repopick will cd into the project path
152 and cherry pick the latest patch available.
153
154 With the --start-branch argument, the user can specify that a branch
155 should be created before cherry picking. This is useful for
156 cherry-picking many patches into a common branch which can be easily
157 abandoned later (good for testing other's changes.)
158
159 The --abandon-first argument, when used in conjunction with the
160 --start-branch option, will cause repopick to abandon the specified
161 branch in all repos first before performing any cherry picks.'''))
Aayush Gupta3ed46672020-07-26 06:47:01 +0000162 parser.add_argument('change_number', nargs='*',
163 help='change number to cherry pick. Use {change number}/{patchset number} to get a specific revision.')
164 parser.add_argument('-i', '--ignore-missing', action='store_true',
165 help='do not error out if a patch applies to a missing directory')
166 parser.add_argument('-s', '--start-branch', nargs=1,
167 metavar='', help='start the specified branch before cherry picking')
168 parser.add_argument('-r', '--reset', action='store_true',
169 help='reset to initial state (abort cherry-pick) if there is a conflict')
170 parser.add_argument('-a', '--abandon-first', action='store_true',
171 help='before cherry picking, abandon the branch specified in --start-branch')
172 parser.add_argument('-b', '--auto-branch', action='store_true',
173 help='shortcut to "--start-branch auto --abandon-first --ignore-missing"')
Marko Manb58468a2018-03-19 13:01:19 +0100174 parser.add_argument('-q', '--quiet', action='store_true', help='print as little as possible')
175 parser.add_argument('-v', '--verbose', action='store_true', help='print extra information to aid in debug')
176 parser.add_argument('-f', '--force', action='store_true', help='force cherry pick even if change is closed')
177 parser.add_argument('-p', '--pull', action='store_true', help='execute pull instead of cherry-pick')
Aayush Gupta3ed46672020-07-26 06:47:01 +0000178 parser.add_argument('-P', '--path', metavar='', help='use the specified path for the change')
179 parser.add_argument('-t', '--topic', metavar='', help='pick all commits from a specified topic')
180 parser.add_argument('-Q', '--query', metavar='', help='pick all commits using the specified query')
181 parser.add_argument('-g', '--gerrit', default=default_gerrit,
182 metavar='', help='Gerrit Instance to use. Form proto://[user@]host[:port]')
183 parser.add_argument('-e', '--exclude', nargs=1,
184 metavar='', help='exclude a list of commit numbers separated by a ,')
185 parser.add_argument('-c', '--check-picked', type=int, default=10,
186 metavar='', help='pass the amount of commits to check for already picked changes')
Marko Manb58468a2018-03-19 13:01:19 +0100187 args = parser.parse_args()
188 if not args.start_branch and args.abandon_first:
189 parser.error('if --abandon-first is set, you must also give the branch name with --start-branch')
190 if args.auto_branch:
191 args.abandon_first = True
192 args.ignore_missing = True
193 if not args.start_branch:
194 args.start_branch = ['auto']
195 if args.quiet and args.verbose:
196 parser.error('--quiet and --verbose cannot be specified together')
197
198 if (1 << bool(args.change_number) << bool(args.topic) << bool(args.query)) != 2:
199 parser.error('One (and only one) of change_number, topic, and query are allowed')
200
201 # Change current directory to the top of the tree
202 if 'ANDROID_BUILD_TOP' in os.environ:
203 top = os.environ['ANDROID_BUILD_TOP']
204
205 if not is_subdir(os.getcwd(), top):
206 sys.stderr.write('ERROR: You must run this tool from within $ANDROID_BUILD_TOP!\n')
207 sys.exit(1)
208 os.chdir(os.environ['ANDROID_BUILD_TOP'])
209
210 # Sanity check that we are being run from the top level of the tree
211 if not os.path.isdir('.repo'):
212 sys.stderr.write('ERROR: No .repo directory found. Please run this from the top of your tree.\n')
Pulser72e23242013-09-29 09:56:55 +0100213 sys.exit(1)
214
Marko Manb58468a2018-03-19 13:01:19 +0100215 # If --abandon-first is given, abandon the branch before starting
216 if args.abandon_first:
217 # Determine if the branch already exists; skip the abandon if it does not
218 plist = subprocess.check_output(['repo', 'info'])
219 if not hasattr(plist, 'encode'):
220 plist = plist.decode()
221 needs_abandon = False
222 for pline in plist.splitlines():
223 matchObj = re.match(r'Local Branches.*\[(.*)\]', pline)
224 if matchObj:
LuK13377e4b70e2023-09-20 20:28:20 +0200225 local_branches = re.split(r'\s*,\s*', matchObj.group(1))
Marko Manb58468a2018-03-19 13:01:19 +0100226 if any(args.start_branch[0] in s for s in local_branches):
227 needs_abandon = True
Pulser72e23242013-09-29 09:56:55 +0100228
Marko Manb58468a2018-03-19 13:01:19 +0100229 if needs_abandon:
230 # Perform the abandon only if the branch already exists
231 if not args.quiet:
232 print('Abandoning branch: %s' % args.start_branch[0])
233 subprocess.check_output(['repo', 'abandon', args.start_branch[0]])
234 if not args.quiet:
235 print('')
Pulser72e23242013-09-29 09:56:55 +0100236
Marko Manb58468a2018-03-19 13:01:19 +0100237 # Get the master manifest from repo
238 # - convert project name and revision to a path
239 project_name_to_data = {}
240 manifest = subprocess.check_output(['repo', 'manifest'])
241 xml_root = ElementTree.fromstring(manifest)
242 projects = xml_root.findall('project')
243 remotes = xml_root.findall('remote')
244 default_revision = xml_root.findall('default')[0].get('revision')
245
Aayush Gupta2f4522c2020-07-26 07:19:19 +0000246 # dump project data into the a list of dicts with the following data:
247 # {project: {path, revision}}
Marko Manb58468a2018-03-19 13:01:19 +0100248
249 for project in projects:
250 name = project.get('name')
Aaron Kling83fee0f2020-06-19 18:43:16 -0500251 # when name and path are equal, "repo manifest" doesn't return a path at all, so fall back to name
252 path = project.get('path', name)
Aaron Klingeba6d822020-06-30 14:22:55 -0500253 revision = project.get('upstream')
Marko Manb58468a2018-03-19 13:01:19 +0100254 if revision is None:
255 for remote in remotes:
256 if remote.get('name') == project.get('remote'):
257 revision = remote.get('revision')
258 if revision is None:
LuK13375c1307d2020-11-23 10:55:24 +0100259 revision = project.get('revision', default_revision)
Marko Manb58468a2018-03-19 13:01:19 +0100260
Aayush Gupta2f4522c2020-07-26 07:19:19 +0000261 if name not in project_name_to_data:
Marko Manb58468a2018-03-19 13:01:19 +0100262 project_name_to_data[name] = {}
263 revision = revision.split('refs/heads/')[-1]
264 project_name_to_data[name][revision] = path
265
266 # get data on requested changes
267 reviews = []
268 change_numbers = []
Gabriele Md91609d2018-03-31 14:26:59 +0200269
270 def cmp_reviews(review_a, review_b):
271 current_a = review_a['current_revision']
272 parents_a = [r['commit'] for r in review_a['revisions'][current_a]['commit']['parents']]
273 current_b = review_b['current_revision']
274 parents_b = [r['commit'] for r in review_b['revisions'][current_b]['commit']['parents']]
275 if current_a in parents_b:
276 return -1
277 elif current_b in parents_a:
278 return 1
279 else:
280 return cmp(review_a['number'], review_b['number'])
281
Marko Manb58468a2018-03-19 13:01:19 +0100282 if args.topic:
283 reviews = fetch_query(args.gerrit, 'topic:{0}'.format(args.topic))
Gabriele Md91609d2018-03-31 14:26:59 +0200284 change_numbers = [str(r['number']) for r in sorted(reviews, key=cmp_to_key(cmp_reviews))]
Marko Manb58468a2018-03-19 13:01:19 +0100285 if args.query:
286 reviews = fetch_query(args.gerrit, args.query)
Gabriele Md91609d2018-03-31 14:26:59 +0200287 change_numbers = [str(r['number']) for r in sorted(reviews, key=cmp_to_key(cmp_reviews))]
Marko Manb58468a2018-03-19 13:01:19 +0100288 if args.change_number:
LuK13377e4b70e2023-09-20 20:28:20 +0200289 change_url_re = re.compile(r'https?://.+?/([0-9]+(?:/[0-9]+)?)/?')
Marko Manb58468a2018-03-19 13:01:19 +0100290 for c in args.change_number:
Gabriele M1009aeb2018-04-01 17:50:58 +0200291 change_number = change_url_re.findall(c)
292 if change_number:
293 change_numbers.extend(change_number)
294 elif '-' in c:
Marko Manb58468a2018-03-19 13:01:19 +0100295 templist = c.split('-')
296 for i in range(int(templist[0]), int(templist[1]) + 1):
297 change_numbers.append(str(i))
298 else:
299 change_numbers.append(c)
300 reviews = fetch_query(args.gerrit, ' OR '.join('change:{0}'.format(x.split('/')[0]) for x in change_numbers))
301
302 # make list of things to actually merge
303 mergables = []
304
305 # If --exclude is given, create the list of commits to ignore
306 exclude = []
307 if args.exclude:
308 exclude = args.exclude[0].split(',')
309
310 for change in change_numbers:
311 patchset = None
312 if '/' in change:
313 (change, patchset) = change.split('/')
314
315 if change in exclude:
316 continue
317
318 change = int(change)
319
320 if patchset is not None:
321 patchset = int(patchset)
322
323 review = next((x for x in reviews if x['number'] == change), None)
324 if review is None:
325 print('Change %d not found, skipping' % change)
326 continue
327
328 mergables.append({
329 'subject': review['subject'],
330 'project': review['project'],
331 'branch': review['branch'],
332 'change_id': review['change_id'],
333 'change_number': review['number'],
334 'status': review['status'],
Gabriele M1188cbd2018-04-01 17:50:57 +0200335 'fetch': None,
336 'patchset': review['revisions'][review['current_revision']]['_number'],
Marko Manb58468a2018-03-19 13:01:19 +0100337 })
Gabriele M1188cbd2018-04-01 17:50:57 +0200338
Marko Manb58468a2018-03-19 13:01:19 +0100339 mergables[-1]['fetch'] = review['revisions'][review['current_revision']]['fetch']
340 mergables[-1]['id'] = change
341 if patchset:
342 try:
343 mergables[-1]['fetch'] = [review['revisions'][x]['fetch'] for x in review['revisions'] if review['revisions'][x]['_number'] == patchset][0]
344 mergables[-1]['id'] = '{0}/{1}'.format(change, patchset)
Gabriele M1188cbd2018-04-01 17:50:57 +0200345 mergables[-1]['patchset'] = patchset
Marko Manb58468a2018-03-19 13:01:19 +0100346 except (IndexError, ValueError):
347 args.quiet or print('ERROR: The patch set {0}/{1} could not be found, using CURRENT_REVISION instead.'.format(change, patchset))
348
349 for item in mergables:
350 args.quiet or print('Applying change number {0}...'.format(item['id']))
351 # Check if change is open and exit if it's not, unless -f is specified
Chirayu Desai0331bc72023-05-27 05:14:32 +0530352 if (item['status'] != 'OPEN' and item['status'] != 'NEW' and item['status'] != 'DRAFT'):
Marko Manb58468a2018-03-19 13:01:19 +0100353 if args.force:
354 print('!! Force-picking a closed change !!\n')
355 else:
356 print('Change status is ' + item['status'] + '. Skipping the cherry pick.\nUse -f to force this pick.')
357 continue
Pulser72e23242013-09-29 09:56:55 +0100358
359 # Convert the project name to a project path
360 # - check that the project path exists
Marko Manb58468a2018-03-19 13:01:19 +0100361 project_path = None
362
363 if item['project'] in project_name_to_data and item['branch'] in project_name_to_data[item['project']]:
364 project_path = project_name_to_data[item['project']][item['branch']]
365 elif args.path:
366 project_path = args.path
Adrian DC76b15f92019-10-13 12:34:06 +0200367 elif item['project'] in project_name_to_data and len(project_name_to_data[item['project']]) == 1:
368 local_branch = list(project_name_to_data[item['project']])[0]
369 project_path = project_name_to_data[item['project']][local_branch]
370 print('WARNING: Project {0} has a different branch ("{1}" != "{2}")'.format(project_path, local_branch, item['branch']))
Pulser72e23242013-09-29 09:56:55 +0100371 elif args.ignore_missing:
Marko Manb58468a2018-03-19 13:01:19 +0100372 print('WARNING: Skipping {0} since there is no project directory for: {1}\n'.format(item['id'], item['project']))
373 continue
Pulser72e23242013-09-29 09:56:55 +0100374 else:
Marko Manb58468a2018-03-19 13:01:19 +0100375 sys.stderr.write('ERROR: For {0}, could not determine the project path for project {1}\n'.format(item['id'], item['project']))
376 sys.exit(1)
Pulser72e23242013-09-29 09:56:55 +0100377
378 # If --start-branch is given, create the branch (more than once per path is okay; repo ignores gracefully)
379 if args.start_branch:
Marko Manb58468a2018-03-19 13:01:19 +0100380 subprocess.check_output(['repo', 'start', args.start_branch[0], project_path])
381
382 # Determine the maximum commits to check already picked changes
383 check_picked_count = args.check_picked
Michael W7c1f1ab2022-06-03 18:26:14 +0200384 max_count = '--max-count={0}'.format(check_picked_count + 1)
385 branch_commits_count = int(subprocess.check_output(['git', 'rev-list', '--count', max_count, 'HEAD'], cwd=project_path))
Marko Manb58468a2018-03-19 13:01:19 +0100386 if branch_commits_count <= check_picked_count:
387 check_picked_count = branch_commits_count - 1
388
389 # Check if change is already picked to HEAD...HEAD~check_picked_count
390 found_change = False
391 for i in range(0, check_picked_count):
392 if subprocess.call(['git', 'cat-file', '-e', 'HEAD~{0}'.format(i)], cwd=project_path, stderr=open(os.devnull, 'wb')):
393 continue
Simon Shields6a726492019-11-18 23:56:08 +1100394 output = subprocess.check_output(['git', 'show', '-q', 'HEAD~{0}'.format(i)], cwd=project_path)
395 # make sure we have a string on Python 3
396 if isinstance(output, bytes):
397 output = output.decode('utf-8')
398 output = output.split()
Marko Manb58468a2018-03-19 13:01:19 +0100399 if 'Change-Id:' in output:
400 head_change_id = ''
Aayush Gupta2f4522c2020-07-26 07:19:19 +0000401 for j, t in enumerate(reversed(output)):
Marko Manb58468a2018-03-19 13:01:19 +0100402 if t == 'Change-Id:':
403 head_change_id = output[len(output) - j]
404 break
405 if head_change_id.strip() == item['change_id']:
406 print('Skipping {0} - already picked in {1} as HEAD~{2}'.format(item['id'], project_path, i))
407 found_change = True
408 break
409 if found_change:
410 continue
Pulser72e23242013-09-29 09:56:55 +0100411
412 # Print out some useful info
413 if not args.quiet:
dianlujitaoe34c26d2024-01-13 17:33:41 +0800414 print('--> Subject: "{0}"'.format(item['subject']))
Marko Manb58468a2018-03-19 13:01:19 +0100415 print('--> Project path: {0}'.format(project_path))
Gabriele M1188cbd2018-04-01 17:50:57 +0200416 print('--> Change number: {0} (Patch Set {1})'.format(item['id'], item['patchset']))
Pulser72e23242013-09-29 09:56:55 +0100417
Marko Manb58468a2018-03-19 13:01:19 +0100418 if 'anonymous http' in item['fetch']:
419 method = 'anonymous http'
Pulser72e23242013-09-29 09:56:55 +0100420 else:
Marko Manb58468a2018-03-19 13:01:19 +0100421 method = 'ssh'
Pulser72e23242013-09-29 09:56:55 +0100422
Marko Manb58468a2018-03-19 13:01:19 +0100423 # Try fetching from GitHub first if using default gerrit
424 if args.gerrit == default_gerrit:
425 if args.verbose:
426 print('Trying to fetch the change from GitHub')
427
428 if args.pull:
429 cmd = ['git pull --no-edit omnirom', item['fetch'][method]['ref']]
430 else:
431 cmd = ['git fetch omnirom', item['fetch'][method]['ref']]
432 if args.quiet:
433 cmd.append('--quiet')
434 else:
435 print(cmd)
436 result = subprocess.call([' '.join(cmd)], cwd=project_path, shell=True)
437 FETCH_HEAD = '{0}/.git/FETCH_HEAD'.format(project_path)
438 if result != 0 and os.stat(FETCH_HEAD).st_size != 0:
439 print('ERROR: git command failed')
440 sys.exit(result)
441 # Check if it worked
442 if args.gerrit != default_gerrit or os.stat(FETCH_HEAD).st_size == 0:
443 # If not using the default gerrit or github failed, fetch from gerrit.
444 if args.verbose:
445 if args.gerrit == default_gerrit:
446 print('Fetching from GitHub didn\'t work, trying to fetch the change from Gerrit')
447 else:
448 print('Fetching from {0}'.format(args.gerrit))
449
450 if args.pull:
451 cmd = ['git pull --no-edit', item['fetch'][method]['url'], item['fetch'][method]['ref']]
452 else:
453 cmd = ['git fetch', item['fetch'][method]['url'], item['fetch'][method]['ref']]
454 if args.quiet:
455 cmd.append('--quiet')
456 else:
457 print(cmd)
458 result = subprocess.call([' '.join(cmd)], cwd=project_path, shell=True)
459 if result != 0:
460 print('ERROR: git command failed')
461 sys.exit(result)
462 # Perform the cherry-pick
463 if not args.pull:
Tim Schumacher074b77b2018-10-13 14:37:54 +0200464 cmd = ['git cherry-pick --ff FETCH_HEAD']
Marko Manb58468a2018-03-19 13:01:19 +0100465 if args.quiet:
466 cmd_out = open(os.devnull, 'wb')
467 else:
468 cmd_out = None
469 result = subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
470 if result != 0:
Adrian DC7bc808f2018-08-30 23:07:23 +0200471 cmd = ['git diff-index --quiet HEAD --']
472 result = subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
473 if result == 0:
474 print('WARNING: git command resulted with an empty commit, aborting cherry-pick')
475 cmd = ['git cherry-pick --abort']
476 subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
477 elif args.reset:
Marko Manb58468a2018-03-19 13:01:19 +0100478 print('ERROR: git command failed, aborting cherry-pick')
479 cmd = ['git cherry-pick --abort']
480 subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
Adrian DC7bc808f2018-08-30 23:07:23 +0200481 sys.exit(result)
Marko Manb58468a2018-03-19 13:01:19 +0100482 else:
483 print('ERROR: git command failed')
Adrian DC7bc808f2018-08-30 23:07:23 +0200484 sys.exit(result)
Pulser72e23242013-09-29 09:56:55 +0100485 if not args.quiet:
Marko Manb58468a2018-03-19 13:01:19 +0100486 print('')