| OLD | NEW | 
|---|
| 1 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2011 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 logging | 9 import logging | 
| 10 import os | 10 import os | 
| (...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 391         return (False, cls.current_version) | 391         return (False, cls.current_version) | 
| 392       elif ver > min_ver: | 392       elif ver > min_ver: | 
| 393         return (True, cls.current_version) | 393         return (True, cls.current_version) | 
| 394     return (True, cls.current_version) | 394     return (True, cls.current_version) | 
| 395 | 395 | 
| 396 | 396 | 
| 397 class SVN(object): | 397 class SVN(object): | 
| 398   current_version = None | 398   current_version = None | 
| 399 | 399 | 
| 400   @staticmethod | 400   @staticmethod | 
| 401   def Capture(args, **kwargs): | 401   def Capture(args, cwd, **kwargs): | 
| 402     """Always redirect stderr. | 402     """Always redirect stderr. | 
| 403 | 403 | 
| 404     Throws an exception if non-0 is returned. | 404     Throws an exception if non-0 is returned. | 
| 405     """ | 405     """ | 
| 406     return subprocess2.check_output( | 406     return subprocess2.check_output( | 
| 407         ['svn'] + args, stderr=subprocess2.PIPE, **kwargs) | 407         ['svn'] + args, stderr=subprocess2.PIPE, cwd=cwd, **kwargs) | 
| 408 | 408 | 
| 409   @staticmethod | 409   @staticmethod | 
| 410   def RunAndGetFileList(verbose, args, cwd, file_list, stdout=None): | 410   def RunAndGetFileList(verbose, args, cwd, file_list, stdout=None): | 
| 411     """Runs svn checkout, update, or status, output to stdout. | 411     """Runs svn checkout, update, or status, output to stdout. | 
| 412 | 412 | 
| 413     The first item in args must be either "checkout", "update", or "status". | 413     The first item in args must be either "checkout", "update", or "status". | 
| 414 | 414 | 
| 415     svn's stdout is parsed to collect a list of files checked out or updated. | 415     svn's stdout is parsed to collect a list of files checked out or updated. | 
| 416     These files are appended to file_list.  svn's stdout is also printed to | 416     These files are appended to file_list.  svn's stdout is also printed to | 
| 417     sys.stdout as in Run. | 417     sys.stdout as in Run. | 
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 507             raise | 507             raise | 
| 508         if retries == 10: | 508         if retries == 10: | 
| 509           raise | 509           raise | 
| 510         print "Sleeping %.1f seconds and retrying...." % backoff_time | 510         print "Sleeping %.1f seconds and retrying...." % backoff_time | 
| 511         time.sleep(backoff_time) | 511         time.sleep(backoff_time) | 
| 512         backoff_time *= 1.3 | 512         backoff_time *= 1.3 | 
| 513         continue | 513         continue | 
| 514       break | 514       break | 
| 515 | 515 | 
| 516   @staticmethod | 516   @staticmethod | 
| 517   def CaptureInfo(cwd): | 517   def CaptureRemoteInfo(url): | 
|  | 518     """Returns a dictionary from the svn info output for the given url. | 
|  | 519 | 
|  | 520     Throws an exception if svn info fails. | 
|  | 521     """ | 
|  | 522     assert isinstance(url, str) | 
|  | 523     return SVN._CaptureInfo([url], None) | 
|  | 524 | 
|  | 525   @staticmethod | 
|  | 526   def CaptureLocalInfo(files, cwd): | 
|  | 527     """Returns a dictionary from the svn info output for the given files. | 
|  | 528 | 
|  | 529     Throws an exception if svn info fails. | 
|  | 530     """ | 
|  | 531     assert isinstance(files, (list, tuple)) | 
|  | 532     return SVN._CaptureInfo(files, cwd) | 
|  | 533 | 
|  | 534   @staticmethod | 
|  | 535   def _CaptureInfo(files, cwd): | 
| 518     """Returns a dictionary from the svn info output for the given file. | 536     """Returns a dictionary from the svn info output for the given file. | 
| 519 | 537 | 
| 520     Throws an exception if svn info fails.""" | 538     Throws an exception if svn info fails.""" | 
| 521     result = {} | 539     result = {} | 
| 522     output = SVN.Capture(['info', '--xml', cwd]) | 540     info = ElementTree.XML(SVN.Capture(['info', '--xml'] + files, cwd)) | 
| 523     info = ElementTree.XML(output) |  | 
| 524     if info is None: | 541     if info is None: | 
| 525       return result | 542       return result | 
| 526     entry = info.find('entry') | 543     entry = info.find('entry') | 
| 527     if entry is None: | 544     if entry is None: | 
| 528       return result | 545       return result | 
| 529 | 546 | 
| 530     # Use .text when the item is not optional. | 547     # Use .text when the item is not optional. | 
| 531     result['Path'] = entry.attrib['path'] | 548     result['Path'] = entry.attrib['path'] | 
| 532     result['Revision'] = int(entry.attrib['revision']) | 549     result['Revision'] = int(entry.attrib['revision']) | 
| 533     result['Node Kind'] = entry.attrib['kind'] | 550     result['Node Kind'] = entry.attrib['kind'] | 
| (...skipping 22 matching lines...) Expand all  Loading... | 
| 556       result[key] = getattr(result[key], 'text', result[key]) | 573       result[key] = getattr(result[key], 'text', result[key]) | 
| 557     return result | 574     return result | 
| 558 | 575 | 
| 559   @staticmethod | 576   @staticmethod | 
| 560   def CaptureRevision(cwd): | 577   def CaptureRevision(cwd): | 
| 561     """Get the base revision of a SVN repository. | 578     """Get the base revision of a SVN repository. | 
| 562 | 579 | 
| 563     Returns: | 580     Returns: | 
| 564       Int base revision | 581       Int base revision | 
| 565     """ | 582     """ | 
| 566     return SVN.CaptureInfo(cwd).get('Revision') | 583     return SVN.CaptureLocalInfo([], cwd).get('Revision') | 
| 567 | 584 | 
| 568   @staticmethod | 585   @staticmethod | 
| 569   def CaptureStatus(files): | 586   def CaptureStatus(files, cwd): | 
| 570     """Returns the svn 1.5 svn status emulated output. | 587     """Returns the svn 1.5 svn status emulated output. | 
| 571 | 588 | 
| 572     @files can be a string (one file) or a list of files. | 589     @files can be a string (one file) or a list of files. | 
| 573 | 590 | 
| 574     Returns an array of (status, file) tuples.""" | 591     Returns an array of (status, file) tuples.""" | 
| 575     command = ["status", "--xml"] | 592     command = ["status", "--xml"] | 
| 576     if not files: | 593     if not files: | 
| 577       pass | 594       pass | 
| 578     elif isinstance(files, basestring): | 595     elif isinstance(files, basestring): | 
| 579       command.append(files) | 596       command.append(files) | 
| (...skipping 11 matching lines...) Expand all  Loading... | 
| 591       'incomplete': '!', | 608       'incomplete': '!', | 
| 592       'merged': 'G', | 609       'merged': 'G', | 
| 593       'missing': '!', | 610       'missing': '!', | 
| 594       'modified': 'M', | 611       'modified': 'M', | 
| 595       'none': ' ', | 612       'none': ' ', | 
| 596       'normal': ' ', | 613       'normal': ' ', | 
| 597       'obstructed': '~', | 614       'obstructed': '~', | 
| 598       'replaced': 'R', | 615       'replaced': 'R', | 
| 599       'unversioned': '?', | 616       'unversioned': '?', | 
| 600     } | 617     } | 
| 601     dom = ElementTree.XML(SVN.Capture(command)) | 618     dom = ElementTree.XML(SVN.Capture(command, cwd)) | 
| 602     results = [] | 619     results = [] | 
| 603     if dom is None: | 620     if dom is None: | 
| 604       return results | 621       return results | 
| 605     # /status/target/entry/(wc-status|commit|author|date) | 622     # /status/target/entry/(wc-status|commit|author|date) | 
| 606     for target in dom.findall('target'): | 623     for target in dom.findall('target'): | 
| 607       for entry in target.findall('entry'): | 624       for entry in target.findall('entry'): | 
| 608         file_path = entry.attrib['path'] | 625         file_path = entry.attrib['path'] | 
| 609         wc_status = entry.find('wc-status') | 626         wc_status = entry.find('wc-status') | 
| 610         # Emulate svn 1.5 status ouput... | 627         # Emulate svn 1.5 status ouput... | 
| 611         statuses = [' '] * 7 | 628         statuses = [' '] * 7 | 
| (...skipping 26 matching lines...) Expand all  Loading... | 
| 638           statuses[3] = '+' | 655           statuses[3] = '+' | 
| 639         # Col 4 | 656         # Col 4 | 
| 640         if wc_status.attrib.get('switched') == 'true': | 657         if wc_status.attrib.get('switched') == 'true': | 
| 641           statuses[4] = 'S' | 658           statuses[4] = 'S' | 
| 642         # TODO(maruel): Col 5 and 6 | 659         # TODO(maruel): Col 5 and 6 | 
| 643         item = (''.join(statuses), file_path) | 660         item = (''.join(statuses), file_path) | 
| 644         results.append(item) | 661         results.append(item) | 
| 645     return results | 662     return results | 
| 646 | 663 | 
| 647   @staticmethod | 664   @staticmethod | 
| 648   def IsMoved(filename): | 665   def IsMoved(filename, cwd): | 
| 649     """Determine if a file has been added through svn mv""" | 666     """Determine if a file has been added through svn mv""" | 
| 650     return SVN.IsMovedInfo(SVN.CaptureInfo(filename)) | 667     assert isinstance(filename, basestring) | 
|  | 668     return SVN.IsMovedInfo(SVN.CaptureLocalInfo([filename], cwd)) | 
| 651 | 669 | 
| 652   @staticmethod | 670   @staticmethod | 
| 653   def IsMovedInfo(info): | 671   def IsMovedInfo(info): | 
| 654     """Determine if a file has been added through svn mv""" | 672     """Determine if a file has been added through svn mv""" | 
| 655     return (info.get('Copied From URL') and | 673     return (info.get('Copied From URL') and | 
| 656             info.get('Copied From Rev') and | 674             info.get('Copied From Rev') and | 
| 657             info.get('Schedule') == 'add') | 675             info.get('Schedule') == 'add') | 
| 658 | 676 | 
| 659   @staticmethod | 677   @staticmethod | 
| 660   def GetFileProperty(filename, property_name): | 678   def GetFileProperty(filename, property_name, cwd): | 
| 661     """Returns the value of an SVN property for the given file. | 679     """Returns the value of an SVN property for the given file. | 
| 662 | 680 | 
| 663     Args: | 681     Args: | 
| 664       filename: The file to check | 682       filename: The file to check | 
| 665       property_name: The name of the SVN property, e.g. "svn:mime-type" | 683       property_name: The name of the SVN property, e.g. "svn:mime-type" | 
| 666 | 684 | 
| 667     Returns: | 685     Returns: | 
| 668       The value of the property, which will be the empty string if the property | 686       The value of the property, which will be the empty string if the property | 
| 669       is not set on the file.  If the file is not under version control, the | 687       is not set on the file.  If the file is not under version control, the | 
| 670       empty string is also returned. | 688       empty string is also returned. | 
| 671     """ | 689     """ | 
| 672     try: | 690     try: | 
| 673       return SVN.Capture(['propget', property_name, filename]) | 691       return SVN.Capture(['propget', property_name, filename], cwd) | 
| 674     except subprocess2.CalledProcessError: | 692     except subprocess2.CalledProcessError: | 
| 675       return '' | 693       return '' | 
| 676 | 694 | 
| 677   @staticmethod | 695   @staticmethod | 
| 678   def DiffItem(filename, full_move=False, revision=None): | 696   def DiffItem(filename, cwd, full_move, revision): | 
| 679     """Diffs a single file. | 697     """Diffs a single file. | 
| 680 | 698 | 
| 681     Should be simple, eh? No it isn't. | 699     Should be simple, eh? No it isn't. | 
| 682     Be sure to be in the appropriate directory before calling to have the | 700     Be sure to be in the appropriate directory before calling to have the | 
| 683     expected relative path. | 701     expected relative path. | 
| 684     full_move means that move or copy operations should completely recreate the | 702     full_move means that move or copy operations should completely recreate the | 
| 685     files, usually in the prospect to apply the patch for a try job.""" | 703     files, usually in the prospect to apply the patch for a try job.""" | 
| 686     # If the user specified a custom diff command in their svn config file, | 704     # If the user specified a custom diff command in their svn config file, | 
| 687     # then it'll be used when we do svn diff, which we don't want to happen | 705     # then it'll be used when we do svn diff, which we don't want to happen | 
| 688     # since we want the unified diff.  Using --diff-cmd=diff doesn't always | 706     # since we want the unified diff.  Using --diff-cmd=diff doesn't always | 
| 689     # work, since they can have another diff executable in their path that | 707     # work, since they can have another diff executable in their path that | 
| 690     # gives different line endings.  So we use a bogus temp directory as the | 708     # gives different line endings.  So we use a bogus temp directory as the | 
| 691     # config directory, which gets around these problems. | 709     # config directory, which gets around these problems. | 
| 692     bogus_dir = tempfile.mkdtemp() | 710     bogus_dir = tempfile.mkdtemp() | 
| 693     try: | 711     try: | 
| 694       # Use "svn info" output instead of os.path.isdir because the latter fails | 712       # Use "svn info" output instead of os.path.isdir because the latter fails | 
| 695       # when the file is deleted. | 713       # when the file is deleted. | 
| 696       return SVN._DiffItemInternal(filename, SVN.CaptureInfo(filename), | 714       return SVN._DiffItemInternal( | 
| 697                                    bogus_dir, | 715           filename, | 
| 698                                    full_move=full_move, revision=revision) | 716           cwd, | 
|  | 717           SVN.CaptureLocalInfo([filename], cwd), | 
|  | 718           bogus_dir, | 
|  | 719           full_move, | 
|  | 720           revision) | 
| 699     finally: | 721     finally: | 
| 700       gclient_utils.RemoveDirectory(bogus_dir) | 722       gclient_utils.RemoveDirectory(bogus_dir) | 
| 701 | 723 | 
| 702   @staticmethod | 724   @staticmethod | 
| 703   def _DiffItemInternal(filename, info, bogus_dir, full_move=False, | 725   def _DiffItemInternal(filename, cwd, info, bogus_dir, full_move, revision): | 
| 704                         revision=None): |  | 
| 705     """Grabs the diff data.""" | 726     """Grabs the diff data.""" | 
| 706     command = ["diff", "--config-dir", bogus_dir, filename] | 727     command = ["diff", "--config-dir", bogus_dir, filename] | 
| 707     if revision: | 728     if revision: | 
| 708       command.extend(['--revision', revision]) | 729       command.extend(['--revision', revision]) | 
| 709     data = None | 730     data = None | 
| 710     if SVN.IsMovedInfo(info): | 731     if SVN.IsMovedInfo(info): | 
| 711       if full_move: | 732       if full_move: | 
| 712         if info.get("Node Kind") == "directory": | 733         if info.get("Node Kind") == "directory": | 
| 713           # Things become tricky here. It's a directory copy/move. We need to | 734           # Things become tricky here. It's a directory copy/move. We need to | 
| 714           # diff all the files inside it. | 735           # diff all the files inside it. | 
| (...skipping 15 matching lines...) Expand all  Loading... | 
| 730               data.write(GenFakeDiff(os.path.join(dirpath, f))) | 751               data.write(GenFakeDiff(os.path.join(dirpath, f))) | 
| 731           if data: | 752           if data: | 
| 732             tmp = data.getvalue() | 753             tmp = data.getvalue() | 
| 733             data.close() | 754             data.close() | 
| 734             data = tmp | 755             data = tmp | 
| 735         else: | 756         else: | 
| 736           data = GenFakeDiff(filename) | 757           data = GenFakeDiff(filename) | 
| 737       else: | 758       else: | 
| 738         if info.get("Node Kind") != "directory": | 759         if info.get("Node Kind") != "directory": | 
| 739           # svn diff on a mv/cp'd file outputs nothing if there was no change. | 760           # svn diff on a mv/cp'd file outputs nothing if there was no change. | 
| 740           data = SVN.Capture(command) | 761           data = SVN.Capture(command, cwd) | 
| 741           if not data: | 762           if not data: | 
| 742             # We put in an empty Index entry so upload.py knows about them. | 763             # We put in an empty Index entry so upload.py knows about them. | 
| 743             data = "Index: %s\n" % filename.replace(os.sep, '/') | 764             data = "Index: %s\n" % filename.replace(os.sep, '/') | 
| 744         # Otherwise silently ignore directories. | 765         # Otherwise silently ignore directories. | 
| 745     else: | 766     else: | 
| 746       if info.get("Node Kind") != "directory": | 767       if info.get("Node Kind") != "directory": | 
| 747         # Normal simple case. | 768         # Normal simple case. | 
| 748         try: | 769         try: | 
| 749           data = SVN.Capture(command) | 770           data = SVN.Capture(command, cwd) | 
| 750         except subprocess2.CalledProcessError: | 771         except subprocess2.CalledProcessError: | 
| 751           if revision: | 772           if revision: | 
| 752             data = GenFakeDiff(filename) | 773             data = GenFakeDiff(filename) | 
| 753           else: | 774           else: | 
| 754             raise | 775             raise | 
| 755       # Otherwise silently ignore directories. | 776       # Otherwise silently ignore directories. | 
| 756     return data | 777     return data | 
| 757 | 778 | 
| 758   @staticmethod | 779   @staticmethod | 
| 759   def GenerateDiff(filenames, root=None, full_move=False, revision=None): | 780   def GenerateDiff(filenames, cwd, full_move, revision): | 
| 760     """Returns a string containing the diff for the given file list. | 781     """Returns a string containing the diff for the given file list. | 
| 761 | 782 | 
| 762     The files in the list should either be absolute paths or relative to the | 783     The files in the list should either be absolute paths or relative to the | 
| 763     given root. If no root directory is provided, the repository root will be | 784     given root. If no root directory is provided, the repository root will be | 
| 764     used. | 785     used. | 
| 765     The diff will always use relative paths. | 786     The diff will always use relative paths. | 
| 766     """ | 787     """ | 
| 767     assert isinstance(filenames, (list, tuple)) | 788     assert isinstance(filenames, (list, tuple)) | 
| 768     previous_cwd = os.getcwd() | 789     root = os.path.normcase(os.path.join(cwd, '')) | 
| 769     root = root or SVN.GetCheckoutRoot(previous_cwd) |  | 
| 770     root = os.path.normcase(os.path.join(root, '')) |  | 
| 771     def RelativePath(path, root): | 790     def RelativePath(path, root): | 
| 772       """We must use relative paths.""" | 791       """We must use relative paths.""" | 
| 773       if os.path.normcase(path).startswith(root): | 792       if os.path.normcase(path).startswith(root): | 
| 774         return path[len(root):] | 793         return path[len(root):] | 
| 775       return path | 794       return path | 
| 776     # If the user specified a custom diff command in their svn config file, | 795     # If the user specified a custom diff command in their svn config file, | 
| 777     # then it'll be used when we do svn diff, which we don't want to happen | 796     # then it'll be used when we do svn diff, which we don't want to happen | 
| 778     # since we want the unified diff.  Using --diff-cmd=diff doesn't always | 797     # since we want the unified diff.  Using --diff-cmd=diff doesn't always | 
| 779     # work, since they can have another diff executable in their path that | 798     # work, since they can have another diff executable in their path that | 
| 780     # gives different line endings.  So we use a bogus temp directory as the | 799     # gives different line endings.  So we use a bogus temp directory as the | 
| 781     # config directory, which gets around these problems. | 800     # config directory, which gets around these problems. | 
| 782     bogus_dir = tempfile.mkdtemp() | 801     bogus_dir = tempfile.mkdtemp() | 
| 783     try: | 802     try: | 
| 784       os.chdir(root) |  | 
| 785       # Cleanup filenames | 803       # Cleanup filenames | 
| 786       filenames = [RelativePath(f, root) for f in filenames] | 804       filenames = [RelativePath(f, root) for f in filenames] | 
| 787       # Get information about the modified items (files and directories) | 805       # Get information about the modified items (files and directories) | 
| 788       data = dict([(f, SVN.CaptureInfo(f)) for f in filenames]) | 806       data = dict([(f, SVN.CaptureLocalInfo([f], root)) for f in filenames]) | 
| 789       diffs = [] | 807       diffs = [] | 
| 790       if full_move: | 808       if full_move: | 
| 791         # Eliminate modified files inside moved/copied directory. | 809         # Eliminate modified files inside moved/copied directory. | 
| 792         for (filename, info) in data.iteritems(): | 810         for (filename, info) in data.iteritems(): | 
| 793           if SVN.IsMovedInfo(info) and info.get("Node Kind") == "directory": | 811           if SVN.IsMovedInfo(info) and info.get("Node Kind") == "directory": | 
| 794             # Remove files inside the directory. | 812             # Remove files inside the directory. | 
| 795             filenames = [f for f in filenames | 813             filenames = [f for f in filenames | 
| 796                          if not f.startswith(filename + os.path.sep)] | 814                          if not f.startswith(filename + os.path.sep)] | 
| 797         for filename in data.keys(): | 815         for filename in data.keys(): | 
| 798           if not filename in filenames: | 816           if not filename in filenames: | 
| 799             # Remove filtered out items. | 817             # Remove filtered out items. | 
| 800             del data[filename] | 818             del data[filename] | 
| 801       else: | 819       else: | 
| 802         metaheaders = [] | 820         metaheaders = [] | 
| 803         for (filename, info) in data.iteritems(): | 821         for (filename, info) in data.iteritems(): | 
| 804           if SVN.IsMovedInfo(info): | 822           if SVN.IsMovedInfo(info): | 
| 805             # for now, the most common case is a head copy, | 823             # for now, the most common case is a head copy, | 
| 806             # so let's just encode that as a straight up cp. | 824             # so let's just encode that as a straight up cp. | 
| 807             srcurl = info.get('Copied From URL') | 825             srcurl = info.get('Copied From URL') | 
| 808             root = info.get('Repository Root') | 826             file_root = info.get('Repository Root') | 
| 809             rev = int(info.get('Copied From Rev')) | 827             rev = int(info.get('Copied From Rev')) | 
| 810             assert srcurl.startswith(root) | 828             assert srcurl.startswith(file_root) | 
| 811             src = srcurl[len(root)+1:] | 829             src = srcurl[len(file_root)+1:] | 
| 812             try: | 830             try: | 
| 813               srcinfo = SVN.CaptureInfo(srcurl) | 831               srcinfo = SVN.CaptureRemoteInfo(srcurl) | 
| 814             except subprocess2.CalledProcessError, e: | 832             except subprocess2.CalledProcessError, e: | 
| 815               if not 'Not a valid URL' in e.stderr: | 833               if not 'Not a valid URL' in e.stderr: | 
| 816                 raise | 834                 raise | 
| 817               # Assume the file was deleted. No idea how to figure out at which | 835               # Assume the file was deleted. No idea how to figure out at which | 
| 818               # revision the file was deleted. | 836               # revision the file was deleted. | 
| 819               srcinfo = {'Revision': rev} | 837               srcinfo = {'Revision': rev} | 
| 820             if (srcinfo.get('Revision') != rev and | 838             if (srcinfo.get('Revision') != rev and | 
| 821                 SVN.Capture(['diff', '-r', '%d:head' % rev, srcurl])): | 839                 SVN.Capture(['diff', '-r', '%d:head' % rev, srcurl], cwd)): | 
| 822               metaheaders.append("#$ svn cp -r %d %s %s " | 840               metaheaders.append("#$ svn cp -r %d %s %s " | 
| 823                                  "### WARNING: note non-trunk copy\n" % | 841                                  "### WARNING: note non-trunk copy\n" % | 
| 824                                  (rev, src, filename)) | 842                                  (rev, src, filename)) | 
| 825             else: | 843             else: | 
| 826               metaheaders.append("#$ cp %s %s\n" % (src, | 844               metaheaders.append("#$ cp %s %s\n" % (src, | 
| 827                                                     filename)) | 845                                                     filename)) | 
| 828 | 846 | 
| 829         if metaheaders: | 847         if metaheaders: | 
| 830           diffs.append("### BEGIN SVN COPY METADATA\n") | 848           diffs.append("### BEGIN SVN COPY METADATA\n") | 
| 831           diffs.extend(metaheaders) | 849           diffs.extend(metaheaders) | 
| 832           diffs.append("### END SVN COPY METADATA\n") | 850           diffs.append("### END SVN COPY METADATA\n") | 
| 833       # Now ready to do the actual diff. | 851       # Now ready to do the actual diff. | 
| 834       for filename in sorted(data.iterkeys()): | 852       for filename in sorted(data.iterkeys()): | 
| 835         diffs.append(SVN._DiffItemInternal(filename, data[filename], bogus_dir, | 853         diffs.append(SVN._DiffItemInternal( | 
| 836                                            full_move=full_move, | 854             filename, cwd, data[filename], bogus_dir, full_move, revision)) | 
| 837                                            revision=revision)) |  | 
| 838       # Use StringIO since it can be messy when diffing a directory move with | 855       # Use StringIO since it can be messy when diffing a directory move with | 
| 839       # full_move=True. | 856       # full_move=True. | 
| 840       buf = cStringIO.StringIO() | 857       buf = cStringIO.StringIO() | 
| 841       for d in filter(None, diffs): | 858       for d in filter(None, diffs): | 
| 842         buf.write(d) | 859         buf.write(d) | 
| 843       result = buf.getvalue() | 860       result = buf.getvalue() | 
| 844       buf.close() | 861       buf.close() | 
| 845       return result | 862       return result | 
| 846     finally: | 863     finally: | 
| 847       os.chdir(previous_cwd) |  | 
| 848       gclient_utils.RemoveDirectory(bogus_dir) | 864       gclient_utils.RemoveDirectory(bogus_dir) | 
| 849 | 865 | 
| 850   @staticmethod | 866   @staticmethod | 
| 851   def GetEmail(repo_root): | 867   def GetEmail(cwd): | 
| 852     """Retrieves the svn account which we assume is an email address.""" | 868     """Retrieves the svn account which we assume is an email address.""" | 
| 853     try: | 869     try: | 
| 854       infos = SVN.CaptureInfo(repo_root) | 870       infos = SVN.CaptureLocalInfo([], cwd) | 
| 855     except subprocess2.CalledProcessError: | 871     except subprocess2.CalledProcessError: | 
| 856       return None | 872       return None | 
| 857 | 873 | 
| 858     # Should check for uuid but it is incorrectly saved for https creds. | 874     # Should check for uuid but it is incorrectly saved for https creds. | 
| 859     root = infos['Repository Root'] | 875     root = infos['Repository Root'] | 
| 860     realm = root.rsplit('/', 1)[0] | 876     realm = root.rsplit('/', 1)[0] | 
| 861     uuid = infos['UUID'] | 877     uuid = infos['UUID'] | 
| 862     if root.startswith('https') or not uuid: | 878     if root.startswith('https') or not uuid: | 
| 863       regexp = re.compile(r'<%s:\d+>.*' % realm) | 879       regexp = re.compile(r'<%s:\d+>.*' % realm) | 
| 864     else: | 880     else: | 
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 897       key = ReadOneItem('K') | 913       key = ReadOneItem('K') | 
| 898       if not key: | 914       if not key: | 
| 899         break | 915         break | 
| 900       value = ReadOneItem('V') | 916       value = ReadOneItem('V') | 
| 901       if not value: | 917       if not value: | 
| 902         break | 918         break | 
| 903       values[key] = value | 919       values[key] = value | 
| 904     return values | 920     return values | 
| 905 | 921 | 
| 906   @staticmethod | 922   @staticmethod | 
| 907   def GetCheckoutRoot(directory): | 923   def GetCheckoutRoot(cwd): | 
| 908     """Returns the top level directory of the current repository. | 924     """Returns the top level directory of the current repository. | 
| 909 | 925 | 
| 910     The directory is returned as an absolute path. | 926     The directory is returned as an absolute path. | 
| 911     """ | 927     """ | 
| 912     directory = os.path.abspath(directory) | 928     cwd = os.path.abspath(cwd) | 
| 913     try: | 929     try: | 
| 914       info = SVN.CaptureInfo(directory) | 930       info = SVN.CaptureLocalInfo([], cwd) | 
| 915       cur_dir_repo_root = info['Repository Root'] | 931       cur_dir_repo_root = info['Repository Root'] | 
| 916       url = info['URL'] | 932       url = info['URL'] | 
| 917     except subprocess2.CalledProcessError: | 933     except subprocess2.CalledProcessError: | 
| 918       return None | 934       return None | 
| 919     while True: | 935     while True: | 
| 920       parent = os.path.dirname(directory) | 936       parent = os.path.dirname(cwd) | 
| 921       try: | 937       try: | 
| 922         info = SVN.CaptureInfo(parent) | 938         info = SVN.CaptureLocalInfo([], parent) | 
| 923         if (info['Repository Root'] != cur_dir_repo_root or | 939         if (info['Repository Root'] != cur_dir_repo_root or | 
| 924             info['URL'] != os.path.dirname(url)): | 940             info['URL'] != os.path.dirname(url)): | 
| 925           break | 941           break | 
| 926         url = info['URL'] | 942         url = info['URL'] | 
| 927       except subprocess2.CalledProcessError: | 943       except subprocess2.CalledProcessError: | 
| 928         break | 944         break | 
| 929       directory = parent | 945       cwd = parent | 
| 930     return GetCasedPath(directory) | 946     return GetCasedPath(cwd) | 
| 931 | 947 | 
| 932   @classmethod | 948   @classmethod | 
| 933   def AssertVersion(cls, min_version): | 949   def AssertVersion(cls, min_version): | 
| 934     """Asserts svn's version is at least min_version.""" | 950     """Asserts svn's version is at least min_version.""" | 
| 935     if cls.current_version is None: | 951     if cls.current_version is None: | 
| 936       cls.current_version = cls.Capture(['--version']).split()[2] | 952       cls.current_version = cls.Capture(['--version'], None).split()[2] | 
| 937     current_version_list = map(only_int, cls.current_version.split('.')) | 953     current_version_list = map(only_int, cls.current_version.split('.')) | 
| 938     for min_ver in map(int, min_version.split('.')): | 954     for min_ver in map(int, min_version.split('.')): | 
| 939       ver = current_version_list.pop(0) | 955       ver = current_version_list.pop(0) | 
| 940       if ver < min_ver: | 956       if ver < min_ver: | 
| 941         return (False, cls.current_version) | 957         return (False, cls.current_version) | 
| 942       elif ver > min_ver: | 958       elif ver > min_ver: | 
| 943         return (True, cls.current_version) | 959         return (True, cls.current_version) | 
| 944     return (True, cls.current_version) | 960     return (True, cls.current_version) | 
| 945 | 961 | 
| 946   @staticmethod | 962   @staticmethod | 
| 947   def Revert(repo_root, callback=None, ignore_externals=False): | 963   def Revert(cwd, callback=None, ignore_externals=False): | 
| 948     """Reverts all svn modifications in repo_root, including properties. | 964     """Reverts all svn modifications in cwd, including properties. | 
| 949 | 965 | 
| 950     Deletes any modified files or directory. | 966     Deletes any modified files or directory. | 
| 951 | 967 | 
| 952     A "svn update --revision BASE" call is required after to revive deleted | 968     A "svn update --revision BASE" call is required after to revive deleted | 
| 953     files. | 969     files. | 
| 954     """ | 970     """ | 
| 955     for file_status in SVN.CaptureStatus(repo_root): | 971     for file_status in SVN.CaptureStatus(None, cwd): | 
| 956       file_path = os.path.join(repo_root, file_status[1]) | 972       file_path = os.path.join(cwd, file_status[1]) | 
| 957       if (ignore_externals and | 973       if (ignore_externals and | 
| 958           file_status[0][0] == 'X' and | 974           file_status[0][0] == 'X' and | 
| 959           file_status[0][1:].isspace()): | 975           file_status[0][1:].isspace()): | 
| 960         # Ignore externals. | 976         # Ignore externals. | 
| 961         logging.info('Ignoring external %s' % file_status[1]) | 977         logging.info('Ignoring external %s' % file_status[1]) | 
| 962         continue | 978         continue | 
| 963 | 979 | 
| 964       if callback: | 980       if callback: | 
| 965         callback(file_status) | 981         callback(file_status) | 
| 966 | 982 | 
| (...skipping 11 matching lines...) Expand all  Loading... | 
| 978           gclient_utils.RemoveDirectory(file_path) | 994           gclient_utils.RemoveDirectory(file_path) | 
| 979         else: | 995         else: | 
| 980           logging.critical( | 996           logging.critical( | 
| 981             ('No idea what is %s.\nYou just found a bug in gclient' | 997             ('No idea what is %s.\nYou just found a bug in gclient' | 
| 982               ', please ping maruel@chromium.org ASAP!') % file_path) | 998               ', please ping maruel@chromium.org ASAP!') % file_path) | 
| 983 | 999 | 
| 984       if (file_status[0][0] in ('D', 'A', '!') or | 1000       if (file_status[0][0] in ('D', 'A', '!') or | 
| 985           not file_status[0][1:].isspace()): | 1001           not file_status[0][1:].isspace()): | 
| 986         # Added, deleted file requires manual intervention and require calling | 1002         # Added, deleted file requires manual intervention and require calling | 
| 987         # revert, like for properties. | 1003         # revert, like for properties. | 
| 988         if not os.path.isdir(repo_root): | 1004         if not os.path.isdir(cwd): | 
| 989           # '.' was deleted. It's not worth continuing. | 1005           # '.' was deleted. It's not worth continuing. | 
| 990           return | 1006           return | 
| 991         try: | 1007         try: | 
| 992           SVN.Capture(['revert', file_status[1]], cwd=repo_root) | 1008           SVN.Capture(['revert', file_status[1]], cwd=cwd) | 
| 993         except subprocess2.CalledProcessError: | 1009         except subprocess2.CalledProcessError: | 
| 994           if not os.path.exists(file_path): | 1010           if not os.path.exists(file_path): | 
| 995             continue | 1011             continue | 
| 996           raise | 1012           raise | 
| OLD | NEW | 
|---|