Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(40)

Side by Side Diff: trychange.py

Issue 523094: Fix checkout root detection for git (Closed)
Patch Set: Created 10 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tests/trychange_unittest.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2009 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2009 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 """Client-side script to send a try job to the try server. It communicates to 5 """Client-side script to send a try job to the try server. It communicates to
6 the try server by either writting to a svn repository or by directly connecting 6 the try server by either writting to a svn repository or by directly connecting
7 to the server by HTTP. 7 to the server by HTTP.
8 """ 8 """
9 9
10 import datetime 10 import datetime
11 import errno
11 import getpass 12 import getpass
12 import logging 13 import logging
13 import optparse 14 import optparse
14 import os 15 import os
15 import posixpath 16 import posixpath
16 import shutil 17 import shutil
17 import socket
18 import subprocess
19 import sys 18 import sys
20 import tempfile 19 import tempfile
21 import urllib 20 import urllib
22 21
23 try: 22 try:
24 import breakpad 23 import breakpad
25 except ImportError: 24 except ImportError:
26 pass 25 pass
27 26
28 import gclient_utils 27 import gclient_utils
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
113 'root': self.GetCodeReviewSetting('TRYSERVER_ROOT'), 112 'root': self.GetCodeReviewSetting('TRYSERVER_ROOT'),
114 'patchlevel': self.GetCodeReviewSetting('TRYSERVER_PATCHLEVEL'), 113 'patchlevel': self.GetCodeReviewSetting('TRYSERVER_PATCHLEVEL'),
115 } 114 }
116 for (k, v) in settings.iteritems(): 115 for (k, v) in settings.iteritems():
117 if v and getattr(self.options, k) is None: 116 if v and getattr(self.options, k) is None:
118 setattr(self.options, k, v) 117 setattr(self.options, k, v)
119 118
120 def GclientStyleSettings(self): 119 def GclientStyleSettings(self):
121 """Find the root, assuming a gclient-style checkout.""" 120 """Find the root, assuming a gclient-style checkout."""
122 if not self.options.no_gclient and not self.options.root: 121 if not self.options.no_gclient and not self.options.root:
123 root = self.GetLocalRoot() 122 root = self.checkout_root
124 gclient_root = gclient_utils.FindGclientRoot(root) 123 gclient_root = gclient_utils.FindGclientRoot(root)
125 if gclient_root: 124 if gclient_root:
126 self.options.root = gclient_utils.PathDifference(gclient_root, root) 125 self.options.root = gclient_utils.PathDifference(gclient_root, root)
127 126
128 def AutomagicalSettings(self): 127 def AutomagicalSettings(self):
129 """Determines settings based on supported code review and checkout tools. 128 """Determines settings based on supported code review and checkout tools.
130 """ 129 """
131 self.GclStyleSettings() 130 self.GclStyleSettings()
132 self.GclientStyleSettings() 131 self.GclientStyleSettings()
133 132
133 def ReadRootFile(self, filename):
134 raise NotImplementedError()
135
134 136
135 class SVN(SCM): 137 class SVN(SCM):
136 """Gathers the options and diff for a subversion checkout.""" 138 """Gathers the options and diff for a subversion checkout."""
137 def __init__(self, *args, **kwargs): 139 def __init__(self, *args, **kwargs):
138 SCM.__init__(self, *args, **kwargs) 140 SCM.__init__(self, *args, **kwargs)
139 self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root) 141 self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root)
140 if not self.options.email: 142 if not self.options.email:
141 # Assumes the svn credential is an email address. 143 # Assumes the svn credential is an email address.
142 self.options.email = scm.SVN.GetEmail(self.checkout_root) 144 self.options.email = scm.SVN.GetEmail(self.checkout_root)
145 logging.info("SVN(%s)" % self.checkout_root)
143 146
144 def ReadRootFile(self, filename): 147 def ReadRootFile(self, filename):
145 try: 148 try:
146 # Try to search on the subversion repository for the file. 149 # Try to search on the subversion repository for the file.
147 import gcl 150 import gcl
148 data = gcl.GetCachedFile(filename, use_root=True) 151 data = gcl.GetCachedFile(filename, use_root=True)
149 logging.debug('%s:\n%s' % (filename, data)) 152 logging.debug('%s:\n%s' % (filename, data))
150 return data 153 return data
151 except ImportError: 154 except ImportError:
152 try: 155 try:
(...skipping 15 matching lines...) Expand all
168 previous_cwd = os.getcwd() 171 previous_cwd = os.getcwd()
169 os.chdir(self.checkout_root) 172 os.chdir(self.checkout_root)
170 excluded = ['!', '?', 'X', ' ', '~'] 173 excluded = ['!', '?', 'X', ' ', '~']
171 self.files = [ 174 self.files = [
172 f[1] for f in scm.SVN.CaptureStatus(self.checkout_root) 175 f[1] for f in scm.SVN.CaptureStatus(self.checkout_root)
173 if f[0][0] not in excluded 176 if f[0][0] not in excluded
174 ] 177 ]
175 os.chdir(previous_cwd) 178 os.chdir(previous_cwd)
176 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True) 179 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True)
177 180
178 def GetLocalRoot(self):
179 """Return the path of the repository root."""
180 return self.checkout_root
181
182 181
183 class GIT(SCM): 182 class GIT(SCM):
184 """Gathers the options and diff for a git checkout.""" 183 """Gathers the options and diff for a git checkout."""
185 def __init__(self, *args, **kwargs): 184 def __init__(self, *args, **kwargs):
186 SCM.__init__(self, *args, **kwargs) 185 SCM.__init__(self, *args, **kwargs)
187 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root) 186 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root)
188 if not self.options.name: 187 if not self.options.name:
189 self.options.name = scm.GIT.GetPatchName(self.checkout_root) 188 self.options.name = scm.GIT.GetPatchName(self.checkout_root)
190 if not self.options.email: 189 if not self.options.email:
191 self.options.email = scm.GIT.GetEmail(self.checkout_root) 190 self.options.email = scm.GIT.GetEmail(self.checkout_root)
191 logging.info("GIT(%s)" % self.checkout_root)
192 192
193 def ReadRootFile(self, filename): 193 def ReadRootFile(self, filename):
194 try: 194 try:
195 # A git checkout is always a full checkout. 195 # A git checkout is always a full checkout.
196 data = gclient_utils.FileRead(os.path.join(self.checkout_root, filename)) 196 data = gclient_utils.FileRead(os.path.join(self.checkout_root, filename))
197 logging.debug('%s:\n%s' % (filename, data)) 197 logging.debug('%s:\n%s' % (filename, data))
198 return data 198 return data
199 except (IOError, OSError): 199 except (IOError, OSError):
200 logging.debug('%s:\nNone' % filename) 200 logging.debug('%s:\nNone' % filename)
201 return None 201 return None
202 202
203 def GetLocalRoot(self):
204 """Return the path of the repository root."""
205 return self.checkout_root
206
207 def GenerateDiff(self): 203 def GenerateDiff(self):
208 # For now, ignores self.files 204 # For now, ignores self.files
209 return scm.GIT.GenerateDiff(self.checkout_root, full_move=True) 205 return scm.GIT.GenerateDiff(self.checkout_root, full_move=True)
210 206
211 207
212 def _ParseSendChangeOptions(options): 208 def _ParseSendChangeOptions(options):
213 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN.""" 209 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN."""
214 values = {} 210 values = {}
215 if options.email: 211 if options.email:
216 values['email'] = options.email 212 values['email'] = options.email
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
348 344
349 345
350 def PrintSuccess(options): 346 def PrintSuccess(options):
351 if not options.dry_run: 347 if not options.dry_run:
352 text = 'Patch \'%s\' sent to try server' % options.name 348 text = 'Patch \'%s\' sent to try server' % options.name
353 if options.bot: 349 if options.bot:
354 text += ': %s' % ', '.join(options.bot) 350 text += ': %s' % ', '.join(options.bot)
355 print(text) 351 print(text)
356 352
357 353
358 def GuessVCS(options, cwd): 354 def GuessVCS(options, path):
359 """Helper to guess the version control system. 355 """Helper to guess the version control system.
360 356
361 NOTE: Very similar to upload.GuessVCS. Doesn't look for hg since we don't 357 NOTE: Very similar to upload.GuessVCS. Doesn't look for hg since we don't
362 support it yet. 358 support it yet.
363 359
364 This examines the current directory, guesses which SCM we're using, and 360 This examines the path directory, guesses which SCM we're using, and
365 returns an instance of the appropriate class. Exit with an error if we can't 361 returns an instance of the appropriate class. Exit with an error if we can't
366 figure it out. 362 figure it out.
367 363
368 Returns: 364 Returns:
369 A SCM instance. Exits if the SCM can't be guessed. 365 A SCM instance. Exits if the SCM can't be guessed.
370 """ 366 """
371 __pychecker__ = 'no-returnvalues' 367 __pychecker__ = 'no-returnvalues'
368 logging.info("GuessVCS(%s)" % path)
372 # Subversion has a .svn in all working directories. 369 # Subversion has a .svn in all working directories.
373 if os.path.isdir(os.path.join(cwd, '.svn')): 370 if os.path.isdir(os.path.join(path, '.svn')):
374 logging.info("GuessVCS(%s) = Subversion" % cwd) 371 return SVN(options, path)
375 return SVN(options, cwd)
376 372
377 # Git has a command to test if you're in a git tree. 373 # Git has a command to test if you're in a git tree.
378 # Try running it, but don't die if we don't have git installed. 374 # Try running it, but don't die if we don't have git installed.
379 try: 375 try:
380 gclient_utils.CheckCall(["git", "rev-parse", "--is-inside-work-tree"], cwd) 376 gclient_utils.CheckCall(["git", "rev-parse", "--is-inside-work-tree"],
381 logging.info("GuessVCS(%s) = Git" % cwd) 377 path)
382 return GIT(options, cwd) 378 return GIT(options, path)
383 except gclient_utils.CheckCallError, e: 379 except gclient_utils.CheckCallError, e:
384 if e.retcode != 2: # ENOENT -- they don't have git installed. 380 if e.retcode != errno.ENOENT and e.retcode != 128:
381 # ENOENT == 2 = they don't have git installed.
382 # 128 = git error code when not in a repo.
383 logging.warn(e.retcode)
385 raise 384 raise
386 raise NoTryServerAccess("Could not guess version control system. " 385 raise NoTryServerAccess("Could not guess version control system. "
387 "Are you in a working copy directory?") 386 "Are you in a working copy directory?")
388 387
389 388
390 def TryChange(argv, 389 def TryChange(argv,
391 file_list, 390 file_list,
392 swallow_exception, 391 swallow_exception,
393 prog=None): 392 prog=None):
394 """ 393 """
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
454 metavar="FILE", action="append", 453 metavar="FILE", action="append",
455 help="Use many times to list the files to include in the " 454 help="Use many times to list the files to include in the "
456 "try, relative to the repository root") 455 "try, relative to the repository root")
457 group.add_option("--diff", 456 group.add_option("--diff",
458 help="File containing the diff to try") 457 help="File containing the diff to try")
459 group.add_option("--url", 458 group.add_option("--url",
460 help="Url where to grab a patch") 459 help="Url where to grab a patch")
461 group.add_option("--root", 460 group.add_option("--root",
462 help="Root to use for the patch; base subdirectory for " 461 help="Root to use for the patch; base subdirectory for "
463 "patch created in a subdirectory") 462 "patch created in a subdirectory")
464 group.add_option("--patchlevel", type='int', metavar="LEVEL", 463 group.add_option("-p", "--patchlevel", type='int', metavar="LEVEL",
465 help="Used as -pN parameter to patch") 464 help="Used as -pN parameter to patch")
466 group.add_option("--sub_rep", action="append", default=[], 465 group.add_option("-s", "--sub_rep", action="append", default=[],
467 help="Subcheckout to use in addition. This is mainly " 466 help="Subcheckout to use in addition. This is mainly "
468 "useful for gclient-style checkouts.") 467 "useful for gclient-style checkouts.")
469 group.add_option("--no_gclient", action="store_true", 468 group.add_option("--no_gclient", action="store_true",
470 help="Disable automatic search for gclient checkout.") 469 help="Disable automatic search for gclient checkout.")
471 parser.add_option_group(group) 470 parser.add_option_group(group)
472 471
473 group = optparse.OptionGroup(parser, "Access the try server by HTTP") 472 group = optparse.OptionGroup(parser, "Access the try server by HTTP")
474 group.add_option("--use_http", 473 group.add_option("--use_http",
475 action="store_const", 474 action="store_const",
476 const=_SendChangeHTTP, 475 const=_SendChangeHTTP,
477 dest="send_patch", 476 dest="send_patch",
478 help="Use HTTP to talk to the try server [default]") 477 help="Use HTTP to talk to the try server [default]")
479 group.add_option("--host", 478 group.add_option("-H", "--host",
480 help="Host address") 479 help="Host address")
481 group.add_option("--port", 480 group.add_option("-P", "--port",
482 help="HTTP port") 481 help="HTTP port")
483 group.add_option("--proxy", 482 group.add_option("--proxy",
484 help="HTTP proxy") 483 help="HTTP proxy")
485 parser.add_option_group(group) 484 parser.add_option_group(group)
486 485
487 group = optparse.OptionGroup(parser, "Access the try server with SVN") 486 group = optparse.OptionGroup(parser, "Access the try server with SVN")
488 group.add_option("--use_svn", 487 group.add_option("--use_svn",
489 action="store_const", 488 action="store_const",
490 const=_SendChangeSVN, 489 const=_SendChangeSVN,
491 dest="send_patch", 490 dest="send_patch",
492 help="Use SVN to talk to the try server") 491 help="Use SVN to talk to the try server")
493 group.add_option("--svn_repo", 492 group.add_option("-S", "--svn_repo",
494 metavar="SVN_URL", 493 metavar="SVN_URL",
495 help="SVN url to use to write the changes in; --use_svn is " 494 help="SVN url to use to write the changes in; --use_svn is "
496 "implied when using --svn_repo") 495 "implied when using --svn_repo")
497 parser.add_option_group(group) 496 parser.add_option_group(group)
498 497
499 options, args = parser.parse_args(argv) 498 options, args = parser.parse_args(argv)
500 if len(args) == 1 and args[0] == 'help': 499 if len(args) == 1 and args[0] == 'help':
501 parser.print_help() 500 parser.print_help()
502 501
503 if options.verbose == 0: 502 if not swallow_exception:
504 logging.basicConfig(level=logging.ERROR) 503 if options.verbose == 0:
505 elif options.verbose == 1: 504 logging.basicConfig(level=logging.ERROR)
506 logging.basicConfig(level=logging.WARNING) 505 elif options.verbose == 1:
507 elif options.verbose == 2: 506 logging.basicConfig(level=logging.WARNING)
508 logging.basicConfig(level=logging.INFO) 507 elif options.verbose == 2:
509 elif options.verbose > 2: 508 logging.basicConfig(level=logging.INFO)
510 logging.basicConfig(level=logging.DEBUG) 509 elif options.verbose > 2:
510 logging.basicConfig(level=logging.DEBUG)
511 511
512 try: 512 try:
513 # Always include os.getcwd() in the checkout settings. 513 # Always include os.getcwd() in the checkout settings.
514 checkouts = [] 514 checkouts = []
515 checkouts.append(GuessVCS(options, os.getcwd())) 515 checkouts.append(GuessVCS(options, os.getcwd()))
516 checkouts[0].AutomagicalSettings() 516 checkouts[0].AutomagicalSettings()
517 for item in options.sub_rep: 517 for item in options.sub_rep:
518 checkout = GuessVCS(options, item) 518 checkout = GuessVCS(options, os.path.join(checkouts[0].checkout_root,
519 if checkout.GetLocalRoot() in [c.GetLocalRoot() for c in checkouts]: 519 item))
520 if checkout.checkout_root in [c.checkout_root for c in checkouts]:
520 parser.error('Specified the root %s two times.' % 521 parser.error('Specified the root %s two times.' %
521 checkout.GetLocalRoot()) 522 checkout.checkout_root)
522 checkouts.append(checkout) 523 checkouts.append(checkout)
523 524
524 can_http = options.port and options.host 525 can_http = options.port and options.host
525 can_svn = options.svn_repo 526 can_svn = options.svn_repo
526 # If there was no transport selected yet, now we must have enough data to 527 # If there was no transport selected yet, now we must have enough data to
527 # select one. 528 # select one.
528 if not options.send_patch and not (can_http or can_svn): 529 if not options.send_patch and not (can_http or can_svn):
529 parser.error('Please specify an access method.') 530 parser.error('Please specify an access method.')
530 531
531 # Convert options.diff into the content of the diff. 532 # Convert options.diff into the content of the diff.
532 if options.url: 533 if options.url:
533 if options.files: 534 if options.files:
534 parser.error('You cannot specify files and --url at the same time.') 535 parser.error('You cannot specify files and --url at the same time.')
535 options.diff = urllib.urlopen(options.url).read() 536 options.diff = urllib.urlopen(options.url).read()
536 elif options.diff: 537 elif options.diff:
537 if options.files: 538 if options.files:
538 parser.error('You cannot specify files and --diff at the same time.') 539 parser.error('You cannot specify files and --diff at the same time.')
539 options.diff = gclient_utils.FileRead(options.diff, 'rb') 540 options.diff = gclient_utils.FileRead(options.diff, 'rb')
540 else: 541 else:
541 # Use this as the base. 542 # Use this as the base.
542 root = checkouts[0].GetLocalRoot() 543 root = checkouts[0].checkout_root
543 diffs = [] 544 diffs = []
544 for checkout in checkouts: 545 for checkout in checkouts:
545 diff = checkout.GenerateDiff().splitlines(True) 546 diff = checkout.GenerateDiff().splitlines(True)
546 # Munge it. 547 # Munge it.
547 path_diff = gclient_utils.PathDifference(root, checkout.GetLocalRoot()) 548 path_diff = gclient_utils.PathDifference(root, checkout.checkout_root)
548 for i in range(len(diff)): 549 for i in range(len(diff)):
549 if diff[i].startswith('--- ') or diff[i].startswith('+++ '): 550 if diff[i].startswith('--- ') or diff[i].startswith('+++ '):
550 diff[i] = diff[i][0:4] + posixpath.join(path_diff, diff[i][4:]) 551 diff[i] = diff[i][0:4] + posixpath.join(path_diff, diff[i][4:])
551 diffs.extend(diff) 552 diffs.extend(diff)
552 options.diff = ''.join(diffs) 553 options.diff = ''.join(diffs)
553 554
554 if not options.bot: 555 if not options.bot:
555 # Get try slaves from PRESUBMIT.py files if not specified. 556 # Get try slaves from PRESUBMIT.py files if not specified.
556 # Even if the diff comes from options.url, use the local checkout for bot 557 # Even if the diff comes from options.url, use the local checkout for bot
557 # selection. 558 # selection.
558 try: 559 try:
559 import presubmit_support 560 import presubmit_support
560 root_presubmit = checkouts[0].ReadRootFile('PRESUBMIT.py') 561 root_presubmit = checkouts[0].ReadRootFile('PRESUBMIT.py')
561 options.bot = presubmit_support.DoGetTrySlaves( 562 options.bot = presubmit_support.DoGetTrySlaves(
562 checkouts[0].GetFileNames(), 563 checkouts[0].GetFileNames(),
563 checkouts[0].GetLocalRoot(), 564 checkouts[0].checkout_root,
564 root_presubmit, 565 root_presubmit,
565 False, 566 False,
566 sys.stdout) 567 sys.stdout)
567 except ImportError: 568 except ImportError:
568 pass 569 pass
569 # If no bot is specified, either the default pool will be selected or the 570 # If no bot is specified, either the default pool will be selected or the
570 # try server will refuse the job. Either case we don't need to interfere. 571 # try server will refuse the job. Either case we don't need to interfere.
571 572
572 if options.name is None: 573 if options.name is None:
573 if options.issue: 574 if options.issue:
(...skipping 27 matching lines...) Expand all
601 except (InvalidScript, NoTryServerAccess), e: 602 except (InvalidScript, NoTryServerAccess), e:
602 if swallow_exception: 603 if swallow_exception:
603 return 1 604 return 1
604 print e 605 print e
605 return 1 606 return 1
606 return 0 607 return 0
607 608
608 609
609 if __name__ == "__main__": 610 if __name__ == "__main__":
610 sys.exit(TryChange(None, [], False)) 611 sys.exit(TryChange(None, [], False))
OLDNEW
« no previous file with comments | « tests/trychange_unittest.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698