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

Side by Side Diff: scm.py

Issue 3307016: Change scm.SVN.Capture (Closed)
Patch Set: patchset 4 was wrong Created 10 years, 3 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
OLDNEW
1 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """SCM-specific utility classes.""" 5 """SCM-specific utility classes."""
6 6
7 import cStringIO 7 import cStringIO
8 import glob 8 import glob
9 import os 9 import os
10 import re 10 import re
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after
299 return (False, current_version) 299 return (False, current_version)
300 elif ver > min_ver: 300 elif ver > min_ver:
301 return (True, current_version) 301 return (True, current_version)
302 return (True, current_version) 302 return (True, current_version)
303 303
304 304
305 class SVN(object): 305 class SVN(object):
306 current_version = None 306 current_version = None
307 307
308 @staticmethod 308 @staticmethod
309 def Capture(args, in_directory=None, print_error=True): 309 def Capture(args, **kwargs):
310 """Runs svn, capturing output sent to stdout as a string. 310 """Always redirect stderr.
311 311
312 Args: 312 Throws an exception if non-0 is returned."""
313 args: A sequence of command line parameters to be passed to svn. 313 return gclient_utils.CheckCall(['svn'] + args, print_error=False,
314 in_directory: The directory where svn is to be run. 314 **kwargs)[0]
315
316 Returns:
317 The output sent to stdout as a string.
318 """
319 stderr = None
320 if not print_error:
321 stderr = subprocess.PIPE
322 return gclient_utils.Popen(['svn'] + args, cwd=in_directory,
323 stdout=subprocess.PIPE, stderr=stderr).communicate()[0]
324 315
325 @staticmethod 316 @staticmethod
326 def RunAndGetFileList(verbose, args, cwd, file_list, stdout=None): 317 def RunAndGetFileList(verbose, args, cwd, file_list, stdout=None):
327 """Runs svn checkout, update, or status, output to stdout. 318 """Runs svn checkout, update, or status, output to stdout.
328 319
329 The first item in args must be either "checkout", "update", or "status". 320 The first item in args must be either "checkout", "update", or "status".
330 321
331 svn's stdout is parsed to collect a list of files checked out or updated. 322 svn's stdout is parsed to collect a list of files checked out or updated.
332 These files are appended to file_list. svn's stdout is also printed to 323 These files are appended to file_list. svn's stdout is also printed to
333 sys.stdout as in Run. 324 sys.stdout as in Run.
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 if len(file_list) == previous_list_len and not IsKnownFailure(): 409 if len(file_list) == previous_list_len and not IsKnownFailure():
419 # No known svn error was found and no progress, bail out. 410 # No known svn error was found and no progress, bail out.
420 raise 411 raise
421 print "Sleeping %.1f seconds and retrying...." % backoff_time 412 print "Sleeping %.1f seconds and retrying...." % backoff_time
422 time.sleep(backoff_time) 413 time.sleep(backoff_time)
423 backoff_time *= 1.3 414 backoff_time *= 1.3
424 continue 415 continue
425 break 416 break
426 417
427 @staticmethod 418 @staticmethod
428 def CaptureInfo(relpath, in_directory=None, print_error=True): 419 def CaptureInfo(cwd):
429 """Returns a dictionary from the svn info output for the given file. 420 """Returns a dictionary from the svn info output for the given file.
430 421
431 Args: 422 Throws an exception if svn info fails."""
432 relpath: The directory where the working copy resides relative to 423 output = SVN.Capture(['info', '--xml', cwd])
433 the directory given by in_directory.
434 in_directory: The directory where svn is to be run.
435 """
436 output = SVN.Capture(["info", "--xml", relpath], in_directory, print_error)
437 dom = gclient_utils.ParseXML(output) 424 dom = gclient_utils.ParseXML(output)
438 result = {} 425 result = {}
439 if dom: 426 if dom:
440 GetNamedNodeText = gclient_utils.GetNamedNodeText 427 GetNamedNodeText = gclient_utils.GetNamedNodeText
441 GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText 428 GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText
442 def C(item, f): 429 def C(item, f):
443 if item is not None: 430 if item is not None:
444 return f(item) 431 return f(item)
445 # /info/entry/ 432 # /info/entry/
446 # url 433 # url
(...skipping 14 matching lines...) Expand all
461 # Differs across versions. 448 # Differs across versions.
462 if result['Node Kind'] == 'dir': 449 if result['Node Kind'] == 'dir':
463 result['Node Kind'] = 'directory' 450 result['Node Kind'] = 'directory'
464 result['Schedule'] = C(GetNamedNodeText(dom, 'schedule'), str) 451 result['Schedule'] = C(GetNamedNodeText(dom, 'schedule'), str)
465 result['Path'] = C(GetNodeNamedAttributeText(dom, 'entry', 'path'), str) 452 result['Path'] = C(GetNodeNamedAttributeText(dom, 'entry', 'path'), str)
466 result['Copied From URL'] = C(GetNamedNodeText(dom, 'copy-from-url'), str) 453 result['Copied From URL'] = C(GetNamedNodeText(dom, 'copy-from-url'), str)
467 result['Copied From Rev'] = C(GetNamedNodeText(dom, 'copy-from-rev'), str) 454 result['Copied From Rev'] = C(GetNamedNodeText(dom, 'copy-from-rev'), str)
468 return result 455 return result
469 456
470 @staticmethod 457 @staticmethod
471 def CaptureHeadRevision(url): 458 def CaptureRevision(cwd):
472 """Get the head revision of a SVN repository.
473
474 Returns:
475 Int head revision
476 """
477 info = SVN.Capture(["info", "--xml", url], os.getcwd())
478 dom = xml.dom.minidom.parseString(info)
479 return dom.getElementsByTagName('entry')[0].getAttribute('revision')
480
481 @staticmethod
482 def CaptureBaseRevision(cwd):
483 """Get the base revision of a SVN repository. 459 """Get the base revision of a SVN repository.
484 460
485 Returns: 461 Returns:
486 Int base revision 462 Int base revision
487 """ 463 """
488 info = SVN.Capture(["info", "--xml"], cwd) 464 info = SVN.Capture(['info', '--xml'], cwd=cwd)
489 dom = xml.dom.minidom.parseString(info) 465 dom = xml.dom.minidom.parseString(info)
490 return dom.getElementsByTagName('entry')[0].getAttribute('revision') 466 return dom.getElementsByTagName('entry')[0].getAttribute('revision')
491 467
492 @staticmethod 468 @staticmethod
493 def CaptureStatus(files): 469 def CaptureStatus(files):
494 """Returns the svn 1.5 svn status emulated output. 470 """Returns the svn 1.5 svn status emulated output.
495 471
496 @files can be a string (one file) or a list of files. 472 @files can be a string (one file) or a list of files.
497 473
498 Returns an array of (status, file) tuples.""" 474 Returns an array of (status, file) tuples."""
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
532 file_path = entry.getAttribute('path') 508 file_path = entry.getAttribute('path')
533 wc_status = entry.getElementsByTagName('wc-status') 509 wc_status = entry.getElementsByTagName('wc-status')
534 assert len(wc_status) == 1 510 assert len(wc_status) == 1
535 # Emulate svn 1.5 status ouput... 511 # Emulate svn 1.5 status ouput...
536 statuses = [' '] * 7 512 statuses = [' '] * 7
537 # Col 0 513 # Col 0
538 xml_item_status = wc_status[0].getAttribute('item') 514 xml_item_status = wc_status[0].getAttribute('item')
539 if xml_item_status in status_letter: 515 if xml_item_status in status_letter:
540 statuses[0] = status_letter[xml_item_status] 516 statuses[0] = status_letter[xml_item_status]
541 else: 517 else:
542 raise Exception('Unknown item status "%s"; please implement me!' % 518 raise gclient_utils.Error(
543 xml_item_status) 519 'Unknown item status "%s"; please implement me!' %
520 xml_item_status)
544 # Col 1 521 # Col 1
545 xml_props_status = wc_status[0].getAttribute('props') 522 xml_props_status = wc_status[0].getAttribute('props')
546 if xml_props_status == 'modified': 523 if xml_props_status == 'modified':
547 statuses[1] = 'M' 524 statuses[1] = 'M'
548 elif xml_props_status == 'conflicted': 525 elif xml_props_status == 'conflicted':
549 statuses[1] = 'C' 526 statuses[1] = 'C'
550 elif (not xml_props_status or xml_props_status == 'none' or 527 elif (not xml_props_status or xml_props_status == 'none' or
551 xml_props_status == 'normal'): 528 xml_props_status == 'normal'):
552 pass 529 pass
553 else: 530 else:
554 raise Exception('Unknown props status "%s"; please implement me!' % 531 raise gclient_utils.Error(
555 xml_props_status) 532 'Unknown props status "%s"; please implement me!' %
533 xml_props_status)
556 # Col 2 534 # Col 2
557 if wc_status[0].getAttribute('wc-locked') == 'true': 535 if wc_status[0].getAttribute('wc-locked') == 'true':
558 statuses[2] = 'L' 536 statuses[2] = 'L'
559 # Col 3 537 # Col 3
560 if wc_status[0].getAttribute('copied') == 'true': 538 if wc_status[0].getAttribute('copied') == 'true':
561 statuses[3] = '+' 539 statuses[3] = '+'
562 # Col 4 540 # Col 4
563 if wc_status[0].getAttribute('switched') == 'true': 541 if wc_status[0].getAttribute('switched') == 'true':
564 statuses[4] = 'S' 542 statuses[4] = 'S'
565 # TODO(maruel): Col 5 and 6 543 # TODO(maruel): Col 5 and 6
(...skipping 19 matching lines...) Expand all
585 563
586 Args: 564 Args:
587 filename: The file to check 565 filename: The file to check
588 property_name: The name of the SVN property, e.g. "svn:mime-type" 566 property_name: The name of the SVN property, e.g. "svn:mime-type"
589 567
590 Returns: 568 Returns:
591 The value of the property, which will be the empty string if the property 569 The value of the property, which will be the empty string if the property
592 is not set on the file. If the file is not under version control, the 570 is not set on the file. If the file is not under version control, the
593 empty string is also returned. 571 empty string is also returned.
594 """ 572 """
595 output = SVN.Capture(["propget", property_name, filename]) 573 try:
596 if (output.startswith("svn: ") and 574 return SVN.Capture(['propget', property_name, filename])
597 output.endswith("is not under version control")): 575 except gclient_utils.Error:
598 return "" 576 return ''
599 else:
600 return output
601 577
602 @staticmethod 578 @staticmethod
603 def DiffItem(filename, full_move=False, revision=None): 579 def DiffItem(filename, full_move=False, revision=None):
604 """Diffs a single file. 580 """Diffs a single file.
605 581
606 Should be simple, eh? No it isn't. 582 Should be simple, eh? No it isn't.
607 Be sure to be in the appropriate directory before calling to have the 583 Be sure to be in the appropriate directory before calling to have the
608 expected relative path. 584 expected relative path.
609 full_move means that move or copy operations should completely recreate the 585 full_move means that move or copy operations should completely recreate the
610 files, usually in the prospect to apply the patch for a try job.""" 586 files, usually in the prospect to apply the patch for a try job."""
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
655 data.write(GenFakeDiff(os.path.join(dirpath, f))) 631 data.write(GenFakeDiff(os.path.join(dirpath, f)))
656 if data: 632 if data:
657 tmp = data.getvalue() 633 tmp = data.getvalue()
658 data.close() 634 data.close()
659 data = tmp 635 data = tmp
660 else: 636 else:
661 data = GenFakeDiff(filename) 637 data = GenFakeDiff(filename)
662 else: 638 else:
663 if info.get("Node Kind") != "directory": 639 if info.get("Node Kind") != "directory":
664 # svn diff on a mv/cp'd file outputs nothing if there was no change. 640 # svn diff on a mv/cp'd file outputs nothing if there was no change.
665 data = SVN.Capture(command, None) 641 data = SVN.Capture(command)
666 if not data: 642 if not data:
667 # We put in an empty Index entry so upload.py knows about them. 643 # We put in an empty Index entry so upload.py knows about them.
668 data = "Index: %s\n" % filename.replace(os.sep, '/') 644 data = "Index: %s\n" % filename.replace(os.sep, '/')
669 # Otherwise silently ignore directories. 645 # Otherwise silently ignore directories.
670 else: 646 else:
671 if info.get("Node Kind") != "directory": 647 if info.get("Node Kind") != "directory":
672 # Normal simple case. 648 # Normal simple case.
673 data = SVN.Capture(command, None) 649 data = SVN.Capture(command)
674 # Otherwise silently ignore directories. 650 # Otherwise silently ignore directories.
675 return data 651 return data
676 652
677 @staticmethod 653 @staticmethod
678 def GenerateDiff(filenames, root=None, full_move=False, revision=None): 654 def GenerateDiff(filenames, root=None, full_move=False, revision=None):
679 """Returns a string containing the diff for the given file list. 655 """Returns a string containing the diff for the given file list.
680 656
681 The files in the list should either be absolute paths or relative to the 657 The files in the list should either be absolute paths or relative to the
682 given root. If no root directory is provided, the repository root will be 658 given root. If no root directory is provided, the repository root will be
683 used. 659 used.
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
754 result = buf.getvalue() 730 result = buf.getvalue()
755 buf.close() 731 buf.close()
756 return result 732 return result
757 finally: 733 finally:
758 os.chdir(previous_cwd) 734 os.chdir(previous_cwd)
759 shutil.rmtree(bogus_dir) 735 shutil.rmtree(bogus_dir)
760 736
761 @staticmethod 737 @staticmethod
762 def GetEmail(repo_root): 738 def GetEmail(repo_root):
763 """Retrieves the svn account which we assume is an email address.""" 739 """Retrieves the svn account which we assume is an email address."""
764 infos = SVN.CaptureInfo(repo_root) 740 try:
765 uuid = infos.get('UUID') 741 infos = SVN.CaptureInfo(repo_root)
766 root = infos.get('Repository Root') 742 except gclient_utils.Error:
767 if not root:
768 return None 743 return None
769 744
770 # Should check for uuid but it is incorrectly saved for https creds. 745 # Should check for uuid but it is incorrectly saved for https creds.
746 root = infos['Repository Root']
771 realm = root.rsplit('/', 1)[0] 747 realm = root.rsplit('/', 1)[0]
748 uuid = infos['UUID']
772 if root.startswith('https') or not uuid: 749 if root.startswith('https') or not uuid:
773 regexp = re.compile(r'<%s:\d+>.*' % realm) 750 regexp = re.compile(r'<%s:\d+>.*' % realm)
774 else: 751 else:
775 regexp = re.compile(r'<%s:\d+> %s' % (realm, uuid)) 752 regexp = re.compile(r'<%s:\d+> %s' % (realm, uuid))
776 if regexp is None: 753 if regexp is None:
777 return None 754 return None
778 if sys.platform.startswith('win'): 755 if sys.platform.startswith('win'):
779 if not 'APPDATA' in os.environ: 756 if not 'APPDATA' in os.environ:
780 return None 757 return None
781 auth_dir = os.path.join(os.environ['APPDATA'], 'Subversion', 'auth', 758 auth_dir = os.path.join(os.environ['APPDATA'], 'Subversion', 'auth',
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
813 values[key] = value 790 values[key] = value
814 return values 791 return values
815 792
816 @staticmethod 793 @staticmethod
817 def GetCheckoutRoot(directory): 794 def GetCheckoutRoot(directory):
818 """Returns the top level directory of the current repository. 795 """Returns the top level directory of the current repository.
819 796
820 The directory is returned as an absolute path. 797 The directory is returned as an absolute path.
821 """ 798 """
822 directory = os.path.abspath(directory) 799 directory = os.path.abspath(directory)
823 infos = SVN.CaptureInfo(directory, print_error=False) 800 try:
824 cur_dir_repo_root = infos.get("Repository Root") 801 cur_dir_repo_root = SVN.CaptureInfo(directory)['Repository Root']
825 if not cur_dir_repo_root: 802 except gclient_utils.Error:
826 return None 803 return None
827
828 while True: 804 while True:
829 parent = os.path.dirname(directory) 805 parent = os.path.dirname(directory)
830 if (SVN.CaptureInfo(parent, print_error=False).get( 806 try:
831 "Repository Root") != cur_dir_repo_root): 807 if SVN.CaptureInfo(parent)['Repository Root'] != cur_dir_repo_root:
808 break
809 except gclient_utils.Error:
832 break 810 break
833 directory = parent 811 directory = parent
834 return GetCasedPath(directory) 812 return GetCasedPath(directory)
835 813
836 @staticmethod 814 @staticmethod
837 def AssertVersion(min_version): 815 def AssertVersion(min_version):
838 """Asserts svn's version is at least min_version.""" 816 """Asserts svn's version is at least min_version."""
839 def only_int(val): 817 def only_int(val):
840 if val.isdigit(): 818 if val.isdigit():
841 return int(val) 819 return int(val)
842 else: 820 else:
843 return 0 821 return 0
844 if not SVN.current_version: 822 if not SVN.current_version:
845 SVN.current_version = SVN.Capture(['--version']).split()[2] 823 SVN.current_version = SVN.Capture(['--version']).split()[2]
846 current_version_list = map(only_int, SVN.current_version.split('.')) 824 current_version_list = map(only_int, SVN.current_version.split('.'))
847 for min_ver in map(int, min_version.split('.')): 825 for min_ver in map(int, min_version.split('.')):
848 ver = current_version_list.pop(0) 826 ver = current_version_list.pop(0)
849 if ver < min_ver: 827 if ver < min_ver:
850 return (False, SVN.current_version) 828 return (False, SVN.current_version)
851 elif ver > min_ver: 829 elif ver > min_ver:
852 return (True, SVN.current_version) 830 return (True, SVN.current_version)
853 return (True, SVN.current_version) 831 return (True, SVN.current_version)
OLDNEW
« no previous file with comments | « gclient_scm.py ('k') | tests/gclient_scm_test.py » ('j') | tests/gclient_scm_test.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698