Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # git-cl -- a git-command for integrating reviews on Rietveld | 2 # git-cl -- a git-command for integrating reviews on Rietveld |
| 3 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 3 # Copyright (C) 2008 Evan Martin <martine@danga.com> |
| 4 | 4 |
| 5 import errno | 5 import errno |
| 6 import logging | 6 import logging |
| 7 import optparse | 7 import optparse |
| 8 import os | 8 import os |
| 9 import re | 9 import re |
| 10 import StringIO | 10 import StringIO |
| 11 import subprocess | 11 import subprocess |
| 12 import sys | 12 import sys |
| 13 import tempfile | 13 import tempfile |
| 14 import textwrap | 14 import textwrap |
| 15 import upload | |
| 16 import urlparse | 15 import urlparse |
| 17 import urllib2 | 16 import urllib2 |
| 18 | 17 |
| 19 try: | 18 try: |
| 20 import readline | 19 import readline # pylint: disable=W0611 |
| 21 except ImportError: | 20 except ImportError: |
| 22 pass | 21 pass |
| 23 | 22 |
| 23 # TODO(dpranke): don't use relative import. | |
| 24 import upload # pylint: disable=W0403 | |
| 24 try: | 25 try: |
| 25 # TODO(dpranke): We wrap this in a try block for a limited form of | 26 # TODO(dpranke): We wrap this in a try block for a limited form of |
| 26 # backwards-compatibility with older versions of git-cl that weren't | 27 # backwards-compatibility with older versions of git-cl that weren't |
| 27 # dependent on depot_tools. This version should still work outside of | 28 # dependent on depot_tools. This version should still work outside of |
| 28 # depot_tools as long as --bypass-hooks is used. We should remove this | 29 # depot_tools as long as --bypass-hooks is used. We should remove this |
| 29 # once this has baked for a while and things seem safe. | 30 # once this has baked for a while and things seem safe. |
| 30 depot_tools_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | 31 depot_tools_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| 31 sys.path.append(depot_tools_path) | 32 sys.path.append(depot_tools_path) |
| 32 import breakpad | 33 import breakpad # pylint: disable=W0611 |
| 33 except ImportError: | 34 except ImportError: |
| 34 pass | 35 pass |
| 35 | 36 |
| 36 DEFAULT_SERVER = 'http://codereview.appspot.com' | 37 DEFAULT_SERVER = 'http://codereview.appspot.com' |
| 37 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s' | 38 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s' |
| 38 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' | 39 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' |
| 39 | 40 |
| 40 def DieWithError(message): | 41 def DieWithError(message): |
| 41 print >> sys.stderr, message | 42 print >> sys.stderr, message |
| 42 sys.exit(1) | 43 sys.exit(1) |
| (...skipping 557 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 600 #PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof | 601 #PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof |
| 601 #ORIGIN_URL_CONFIG: http://src.chromium.org/git | 602 #ORIGIN_URL_CONFIG: http://src.chromium.org/git |
| 602 RunGit(['config', keyvals['PUSH_URL_CONFIG'], | 603 RunGit(['config', keyvals['PUSH_URL_CONFIG'], |
| 603 keyvals['ORIGIN_URL_CONFIG']]) | 604 keyvals['ORIGIN_URL_CONFIG']]) |
| 604 | 605 |
| 605 | 606 |
| 606 @usage('[repo root containing codereview.settings]') | 607 @usage('[repo root containing codereview.settings]') |
| 607 def CMDconfig(parser, args): | 608 def CMDconfig(parser, args): |
| 608 """edit configuration for this tree""" | 609 """edit configuration for this tree""" |
| 609 | 610 |
| 610 (options, args) = parser.parse_args(args) | 611 _, args = parser.parse_args(args) |
| 611 if len(args) == 0: | 612 if len(args) == 0: |
| 612 GetCodereviewSettingsInteractively() | 613 GetCodereviewSettingsInteractively() |
| 613 return 0 | 614 return 0 |
| 614 | 615 |
| 615 url = args[0] | 616 url = args[0] |
| 616 if not url.endswith('codereview.settings'): | 617 if not url.endswith('codereview.settings'): |
| 617 url = os.path.join(url, 'codereview.settings') | 618 url = os.path.join(url, 'codereview.settings') |
| 618 | 619 |
| 619 # Load code review settings and download hooks (if available). | 620 # Load code review settings and download hooks (if available). |
| 620 LoadCodereviewSettingsFromFile(urllib2.urlopen(url)) | 621 LoadCodereviewSettingsFromFile(urllib2.urlopen(url)) |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 666 print cl.GetDescription(pretty=True) | 667 print cl.GetDescription(pretty=True) |
| 667 return 0 | 668 return 0 |
| 668 | 669 |
| 669 | 670 |
| 670 @usage('[issue_number]') | 671 @usage('[issue_number]') |
| 671 def CMDissue(parser, args): | 672 def CMDissue(parser, args): |
| 672 """Set or display the current code review issue number. | 673 """Set or display the current code review issue number. |
| 673 | 674 |
| 674 Pass issue number 0 to clear the current issue. | 675 Pass issue number 0 to clear the current issue. |
| 675 """ | 676 """ |
| 676 (options, args) = parser.parse_args(args) | 677 _, args = parser.parse_args(args) |
| 677 | 678 |
| 678 cl = Changelist() | 679 cl = Changelist() |
| 679 if len(args) > 0: | 680 if len(args) > 0: |
| 680 try: | 681 try: |
| 681 issue = int(args[0]) | 682 issue = int(args[0]) |
| 682 except ValueError: | 683 except ValueError: |
| 683 DieWithError('Pass a number to set the issue or none to list it.\n' | 684 DieWithError('Pass a number to set the issue or none to list it.\n' |
| 684 'Maybe you want to run git cl status?') | 685 'Maybe you want to run git cl status?') |
| 685 cl.SetIssue(issue) | 686 cl.SetIssue(issue) |
| 686 print 'Issue number:', cl.GetIssue(), '(%s)' % cl.GetIssueURL() | 687 print 'Issue number:', cl.GetIssue(), '(%s)' % cl.GetIssueURL() |
| (...skipping 497 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1184 parser.add_option('-f', action='store_true', dest='force', | 1185 parser.add_option('-f', action='store_true', dest='force', |
| 1185 help='with -b, clobber any existing branch') | 1186 help='with -b, clobber any existing branch') |
| 1186 parser.add_option('--reject', action='store_true', dest='reject', | 1187 parser.add_option('--reject', action='store_true', dest='reject', |
| 1187 help='allow failed patches and spew .rej files') | 1188 help='allow failed patches and spew .rej files') |
| 1188 parser.add_option('-n', '--no-commit', action='store_true', dest='nocommit', | 1189 parser.add_option('-n', '--no-commit', action='store_true', dest='nocommit', |
| 1189 help="don't commit after patch applies") | 1190 help="don't commit after patch applies") |
| 1190 (options, args) = parser.parse_args(args) | 1191 (options, args) = parser.parse_args(args) |
| 1191 if len(args) != 1: | 1192 if len(args) != 1: |
| 1192 parser.print_help() | 1193 parser.print_help() |
| 1193 return 1 | 1194 return 1 |
| 1194 input = args[0] | 1195 issue_arg = args[0] |
| 1195 | 1196 |
| 1196 if re.match(r'\d+', input): | 1197 if re.match(r'\d+', input): |
| 1197 # Input is an issue id. Figure out the URL. | 1198 # Input is an issue id. Figure out the URL. |
| 1198 issue = input | 1199 issue = issue_arg |
| 1199 server = settings.GetDefaultServerUrl() | 1200 server = settings.GetDefaultServerUrl() |
| 1200 fetch = urllib2.urlopen('%s/%s' % (server, issue)).read() | 1201 fetch = urllib2.urlopen('%s/%s' % (server, issue)).read() |
| 1201 m = re.search(r'/download/issue[0-9]+_[0-9]+.diff', fetch) | 1202 m = re.search(r'/download/issue[0-9]+_[0-9]+.diff', fetch) |
| 1202 if not m: | 1203 if not m: |
| 1203 DieWithError('Must pass an issue ID or full URL for ' | 1204 DieWithError('Must pass an issue ID or full URL for ' |
| 1204 '\'Download raw patch set\'') | 1205 '\'Download raw patch set\'') |
| 1205 url = '%s%s' % (server, m.group(0).strip()) | 1206 url = '%s%s' % (server, m.group(0).strip()) |
| 1206 else: | 1207 else: |
| 1207 # Assume it's a URL to the patch. Default to http. | 1208 # Assume it's a URL to the patch. Default to http. |
| 1208 input = FixUrl(input) | 1209 issue_url = FixUrl(issue_arg) |
| 1209 match = re.match(r'.*?/issue(\d+)_\d+.diff', input) | 1210 match = re.match(r'.*?/issue(\d+)_\d+.diff', issue_url) |
| 1210 if match: | 1211 if match: |
| 1211 issue = match.group(1) | 1212 issue = match.group(1) |
| 1212 url = input | 1213 url = input |
| 1213 else: | 1214 else: |
| 1214 DieWithError('Must pass an issue ID or full URL for ' | 1215 DieWithError('Must pass an issue ID or full URL for ' |
| 1215 '\'Download raw patch set\'') | 1216 '\'Download raw patch set\'') |
| 1216 | 1217 |
| 1217 if options.newbranch: | 1218 if options.newbranch: |
| 1218 if options.force: | 1219 if options.force: |
| 1219 RunGit(['branch', '-D', options.newbranch], | 1220 RunGit(['branch', '-D', options.newbranch], |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1282 return 'open' | 1283 return 'open' |
| 1283 return 'unknown' | 1284 return 'unknown' |
| 1284 return 'unset' | 1285 return 'unset' |
| 1285 | 1286 |
| 1286 | 1287 |
| 1287 def GetTreeStatusReason(): | 1288 def GetTreeStatusReason(): |
| 1288 """Fetches the tree status from a json url and returns the message | 1289 """Fetches the tree status from a json url and returns the message |
| 1289 with the reason for the tree to be opened or closed.""" | 1290 with the reason for the tree to be opened or closed.""" |
| 1290 # Don't import it at file level since simplejson is not installed by default | 1291 # Don't import it at file level since simplejson is not installed by default |
| 1291 # on python 2.5 and it is only used for git-cl tree which isn't often used, | 1292 # on python 2.5 and it is only used for git-cl tree which isn't often used, |
| 1292 # forcing everyone to install simplejson isn't efficient. | 1293 # forcing everyone to install simplejson isn't efficient. |
|
M-A Ruel
2011/03/13 20:45:48
We need to change that once git-cl is moved one di
| |
| 1293 try: | 1294 try: |
| 1294 import simplejson as json | 1295 import simplejson as json # pylint: disable=F0401 |
| 1295 except ImportError: | 1296 except ImportError: |
| 1296 try: | 1297 try: |
| 1297 import json | 1298 import json |
| 1298 # Some versions of python2.5 have an incomplete json module. Check to make | 1299 # Some versions of python2.5 have an incomplete json module. Check to make |
| 1299 # sure loads exists. | 1300 # sure loads exists. |
| 1300 json.loads | 1301 if not json.loads: |
|
M-A Ruel
2011/03/13 20:45:48
We don't need this check anymore.
| |
| 1301 except (ImportError, AttributeError): | 1302 raise ImportError |
| 1303 except (ImportError): | |
| 1302 print >> sys.stderr, 'Please install simplejson' | 1304 print >> sys.stderr, 'Please install simplejson' |
| 1303 sys.exit(1) | 1305 sys.exit(1) |
| 1304 | 1306 |
| 1305 url = settings.GetTreeStatusUrl() | 1307 url = settings.GetTreeStatusUrl() |
| 1306 json_url = urlparse.urljoin(url, '/current?format=json') | 1308 json_url = urlparse.urljoin(url, '/current?format=json') |
| 1307 connection = urllib2.urlopen(json_url) | 1309 connection = urllib2.urlopen(json_url) |
| 1308 status = json.loads(connection.read()) | 1310 status = json.loads(connection.read()) |
| 1309 connection.close() | 1311 connection.close() |
| 1310 return status['message'] | 1312 return status['message'] |
| 1311 | 1313 |
| 1312 | 1314 |
| 1313 def CMDtree(parser, args): | 1315 def CMDtree(parser, args): |
| 1314 """show the status of the tree""" | 1316 """show the status of the tree""" |
| 1315 (options, args) = parser.parse_args(args) | 1317 _, args = parser.parse_args(args) |
| 1316 status = GetTreeStatus() | 1318 status = GetTreeStatus() |
| 1317 if 'unset' == status: | 1319 if 'unset' == status: |
| 1318 print 'You must configure your tree status URL by running "git cl config".' | 1320 print 'You must configure your tree status URL by running "git cl config".' |
| 1319 return 2 | 1321 return 2 |
| 1320 | 1322 |
| 1321 print "The tree is %s" % status | 1323 print "The tree is %s" % status |
| 1322 print | 1324 print |
| 1323 print GetTreeStatusReason() | 1325 print GetTreeStatusReason() |
| 1324 if status != 'open': | 1326 if status != 'open': |
| 1325 return 1 | 1327 return 1 |
| 1326 return 0 | 1328 return 0 |
| 1327 | 1329 |
| 1328 | 1330 |
| 1329 def CMDupstream(parser, args): | 1331 def CMDupstream(parser, args): |
| 1330 """print the name of the upstream branch, if any""" | 1332 """print the name of the upstream branch, if any""" |
| 1331 (options, args) = parser.parse_args(args) | 1333 _, args = parser.parse_args(args) |
| 1332 cl = Changelist() | 1334 cl = Changelist() |
| 1333 print cl.GetUpstreamBranch() | 1335 print cl.GetUpstreamBranch() |
| 1334 return 0 | 1336 return 0 |
| 1335 | 1337 |
| 1336 | 1338 |
| 1337 def Command(name): | 1339 def Command(name): |
| 1338 return getattr(sys.modules[__name__], 'CMD' + name, None) | 1340 return getattr(sys.modules[__name__], 'CMD' + name, None) |
| 1339 | 1341 |
| 1340 | 1342 |
| 1341 def CMDhelp(parser, args): | 1343 def CMDhelp(parser, args): |
| 1342 """print list of commands or help for a specific command""" | 1344 """print list of commands or help for a specific command""" |
| 1343 (options, args) = parser.parse_args(args) | 1345 _, args = parser.parse_args(args) |
| 1344 if len(args) == 1: | 1346 if len(args) == 1: |
| 1345 return main(args + ['--help']) | 1347 return main(args + ['--help']) |
| 1346 parser.print_help() | 1348 parser.print_help() |
| 1347 return 0 | 1349 return 0 |
| 1348 | 1350 |
| 1349 | 1351 |
| 1350 def GenUsage(parser, command): | 1352 def GenUsage(parser, command): |
| 1351 """Modify an OptParse object with the function's documentation.""" | 1353 """Modify an OptParse object with the function's documentation.""" |
| 1352 obj = Command(command) | 1354 obj = Command(command) |
| 1353 more = getattr(obj, 'usage_more', '') | 1355 more = getattr(obj, 'usage_more', '') |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1394 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' | 1396 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' |
| 1395 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 1397 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) |
| 1396 | 1398 |
| 1397 # Not a known command. Default to help. | 1399 # Not a known command. Default to help. |
| 1398 GenUsage(parser, 'help') | 1400 GenUsage(parser, 'help') |
| 1399 return CMDhelp(parser, argv) | 1401 return CMDhelp(parser, argv) |
| 1400 | 1402 |
| 1401 | 1403 |
| 1402 if __name__ == '__main__': | 1404 if __name__ == '__main__': |
| 1403 sys.exit(main(sys.argv[1:])) | 1405 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |