| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # | 2 # |
| 3 # Copyright 2008 Google Inc. All Rights Reserved. | 3 # Copyright 2008 Google Inc. All Rights Reserved. |
| 4 # | 4 # |
| 5 # Licensed under the Apache License, Version 2.0 (the "License"); | 5 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 # you may not use this file except in compliance with the License. | 6 # you may not use this file except in compliance with the License. |
| 7 # You may obtain a copy of the License at | 7 # You may obtain a copy of the License at |
| 8 # | 8 # |
| 9 # http://www.apache.org/licenses/LICENSE-2.0 | 9 # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 # | 10 # |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 import os | 71 import os |
| 72 import re | 72 import re |
| 73 import stat | 73 import stat |
| 74 import subprocess | 74 import subprocess |
| 75 import sys | 75 import sys |
| 76 import time | 76 import time |
| 77 import urlparse | 77 import urlparse |
| 78 import xml.dom.minidom | 78 import xml.dom.minidom |
| 79 import urllib | 79 import urllib |
| 80 | 80 |
| 81 def getText(nodelist): | |
| 82 """ | |
| 83 Return the concatenated text for the children of a list of DOM nodes. | |
| 84 """ | |
| 85 rc = [] | |
| 86 for node in nodelist: | |
| 87 if node.nodeType == node.TEXT_NODE: | |
| 88 rc.append(node.data) | |
| 89 else: | |
| 90 rc.append(getText(node.childNodes)) | |
| 91 return ''.join(rc) | |
| 92 | |
| 93 | 81 |
| 94 SVN_COMMAND = "svn" | 82 SVN_COMMAND = "svn" |
| 95 | 83 |
| 96 | 84 |
| 97 # default help text | 85 # default help text |
| 98 DEFAULT_USAGE_TEXT = ( | 86 DEFAULT_USAGE_TEXT = ( |
| 99 """usage: %prog <subcommand> [options] [--] [svn options/args...] | 87 """usage: %prog <subcommand> [options] [--] [svn options/args...] |
| 100 a wrapper for managing a set of client modules in svn. | 88 a wrapper for managing a set of client modules in svn. |
| 101 Version """ + __version__ + """ | 89 Version """ + __version__ + """ |
| 102 | 90 |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 264 }, | 252 }, |
| 265 \"safesync_url\": \"%s\" | 253 \"safesync_url\": \"%s\" |
| 266 } | 254 } |
| 267 ] | 255 ] |
| 268 """) | 256 """) |
| 269 | 257 |
| 270 | 258 |
| 271 ## Generic utils | 259 ## Generic utils |
| 272 | 260 |
| 273 | 261 |
| 262 def getText(nodelist): |
| 263 """ |
| 264 Return the concatenated text for the children of a list of DOM nodes. |
| 265 """ |
| 266 rc = [] |
| 267 for node in nodelist: |
| 268 if node.nodeType == node.TEXT_NODE: |
| 269 rc.append(node.data) |
| 270 else: |
| 271 rc.append(getText(node.childNodes)) |
| 272 return ''.join(rc) |
| 273 |
| 274 |
| 275 def ParseXML(output): |
| 276 try: |
| 277 return xml.dom.minidom.parseString(output) |
| 278 except xml.parsers.expat.ExpatError: |
| 279 return None |
| 280 |
| 281 |
| 274 class Error(Exception): | 282 class Error(Exception): |
| 275 """gclient exception class.""" | 283 """gclient exception class.""" |
| 276 pass | 284 pass |
| 277 | 285 |
| 278 class PrintableObject(object): | 286 class PrintableObject(object): |
| 279 def __str__(self): | 287 def __str__(self): |
| 280 output = '' | 288 output = '' |
| 281 for i in dir(self): | 289 for i in dir(self): |
| 282 if i.startswith('__'): | 290 if i.startswith('__'): |
| 283 continue | 291 continue |
| (...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 577 | 585 |
| 578 Returns: | 586 Returns: |
| 579 Int head revision | 587 Int head revision |
| 580 """ | 588 """ |
| 581 info = CaptureSVN(options, ["info", "--xml", url], os.getcwd()) | 589 info = CaptureSVN(options, ["info", "--xml", url], os.getcwd()) |
| 582 dom = xml.dom.minidom.parseString(info) | 590 dom = xml.dom.minidom.parseString(info) |
| 583 return int(dom.getElementsByTagName('entry')[0].getAttribute('revision')) | 591 return int(dom.getElementsByTagName('entry')[0].getAttribute('revision')) |
| 584 | 592 |
| 585 | 593 |
| 586 class FileStatus: | 594 class FileStatus: |
| 587 def __init__(self, path, text_status, props, locked, history, switched, | 595 def __init__(self, path, text_status, props, history): |
| 588 repo_locked, out_of_date): | 596 self.path = path |
| 589 self.path = path.strip() | |
| 590 self.text_status = text_status | 597 self.text_status = text_status |
| 591 self.props = props | 598 self.props = props |
| 592 self.locked = locked | |
| 593 self.history = history | 599 self.history = history |
| 594 self.switched = switched | |
| 595 self.repo_locked = repo_locked | |
| 596 self.out_of_date = out_of_date | |
| 597 | 600 |
| 598 def __str__(self): | 601 def __str__(self): |
| 599 return (self.text_status + self.props + self.locked + self.history + | 602 # Emulate svn status 1.5 output. |
| 600 self.switched + self.repo_locked + self.out_of_date + | 603 return (self.text_status + self.props + ' ' + self.history + ' ' + |
| 601 self.path) | 604 self.path) |
| 602 | 605 |
| 603 | 606 |
| 604 def CaptureSVNStatus(options, path): | 607 def CaptureSVNStatus(options, path): |
| 605 """Runs 'svn status' on an existing path. | 608 """Runs 'svn status' on an existing path. |
| 606 | 609 |
| 607 Args: | 610 Args: |
| 608 path: The directory to run svn status. | 611 path: The directory to run svn status. |
| 609 | 612 |
| 610 Returns: | 613 Returns: |
| 611 An array of FileStatus corresponding to the output of 'svn status' | 614 An array of FileStatus corresponding to the emulated output of 'svn status' |
| 612 """ | 615 version 1.5.""" |
| 613 info = CaptureSVN(options, ["status"], path) | 616 dom = ParseXML(CaptureSVN(options, ["status", "--xml"], path)) |
| 614 result = [] | 617 results = [] |
| 615 if not info: | 618 if dom: |
| 616 return result | 619 # /status/target/entry/(wc-status|commit|author|date) |
| 617 for line in info.splitlines(): | 620 for target in dom.getElementsByTagName('target'): |
| 618 if line: | 621 base_path = target.getAttribute('path') |
| 619 new_item = FileStatus(line[7:], line[0:1], line[1:2], line[2:3], | 622 for entry in target.getElementsByTagName('entry'): |
| 620 line[3:4], line[4:5], line[5:6], line[6:7]) | 623 file = entry.getAttribute('path') |
| 621 result.append(new_item) | 624 wc_status = entry.getElementsByTagName('wc-status') |
| 622 return result | 625 assert len(wc_status) == 1 |
| 626 # Emulate svn 1.5 status ouput... |
| 627 statuses = [' ' for i in range(7)] |
| 628 # Col 0 |
| 629 xml_item_status = wc_status[0].getAttribute('item') |
| 630 if xml_item_status == 'unversioned': |
| 631 statuses[0] = '?' |
| 632 elif xml_item_status == 'modified': |
| 633 statuses[0] = 'M' |
| 634 elif xml_item_status == 'added': |
| 635 statuses[0] = 'A' |
| 636 elif xml_item_status == 'conflicted': |
| 637 statuses[0] = 'C' |
| 638 elif not xml_item_status: |
| 639 pass |
| 640 else: |
| 641 raise Exception('Unknown item status "%s"; please implement me!' % |
| 642 xml_item_status) |
| 643 # Col 1 |
| 644 xml_props_status = wc_status[0].getAttribute('props') |
| 645 if xml_props_status == 'modified': |
| 646 statuses[1] = 'M' |
| 647 elif xml_props_status == 'conflicted': |
| 648 statuses[1] = 'C' |
| 649 elif (not xml_props_status or xml_props_status == 'none' or |
| 650 xml_props_status == 'normal'): |
| 651 pass |
| 652 else: |
| 653 raise Exception('Unknown props status "%s"; please implement me!' % |
| 654 xml_props_status) |
| 655 # Col 3 |
| 656 if wc_status[0].getAttribute('copied') == 'true': |
| 657 statuses[3] = '+' |
| 658 item = FileStatus(file, statuses[0], statuses[1], statuses[3]) |
| 659 results.append(item) |
| 660 return results |
| 623 | 661 |
| 624 | 662 |
| 625 ### SCM abstraction layer | 663 ### SCM abstraction layer |
| 626 | 664 |
| 627 | 665 |
| 628 class SCMWrapper(object): | 666 class SCMWrapper(object): |
| 629 """Add necessary glue between all the supported SCM. | 667 """Add necessary glue between all the supported SCM. |
| 630 | 668 |
| 631 This is the abstraction layer to bind to different SCM. Since currently only | 669 This is the abstraction layer to bind to different SCM. Since currently only |
| 632 subversion is supported, a lot of subersionism remains. This can be sorted out | 670 subversion is supported, a lot of subersionism remains. This can be sorted out |
| 633 once another SCM is supported.""" | 671 once another SCM is supported.""" |
| 634 def __init__(self, url=None, root_dir=None, relpath=None, | 672 def __init__(self, url=None, root_dir=None, relpath=None, |
| 635 scm_name='svn'): | 673 scm_name='svn'): |
| 636 # TODO(maruel): Deduce the SCM from the url. | 674 # TODO(maruel): Deduce the SCM from the url. |
| 637 self.scm_name = scm_name | 675 self.scm_name = scm_name |
| 638 self.url = url | 676 self.url = url |
| 639 self._root_dir = root_dir | 677 self._root_dir = root_dir |
| 640 if self._root_dir: | 678 if self._root_dir: |
| 641 self._root_dir = self._root_dir.replace('/', os.sep).strip() | 679 self._root_dir = self._root_dir.replace('/', os.sep) |
| 642 self.relpath = relpath | 680 self.relpath = relpath |
| 643 if self.relpath: | 681 if self.relpath: |
| 644 self.relpath = self.relpath.replace('/', os.sep).strip() | 682 self.relpath = self.relpath.replace('/', os.sep) |
| 645 | 683 |
| 646 def FullUrlForRelativeUrl(self, url): | 684 def FullUrlForRelativeUrl(self, url): |
| 647 # Find the forth '/' and strip from there. A bit hackish. | 685 # Find the forth '/' and strip from there. A bit hackish. |
| 648 return '/'.join(self.url.split('/')[:4]) + url | 686 return '/'.join(self.url.split('/')[:4]) + url |
| 649 | 687 |
| 650 def RunCommand(self, command, options, args, file_list=None): | 688 def RunCommand(self, command, options, args, file_list=None): |
| 651 # file_list will have all files that are modified appended to it. | 689 # file_list will have all files that are modified appended to it. |
| 652 | 690 |
| 653 if file_list == None: | 691 if file_list == None: |
| 654 file_list = [] | 692 file_list = [] |
| (...skipping 985 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1640 | 1678 |
| 1641 if "__main__" == __name__: | 1679 if "__main__" == __name__: |
| 1642 try: | 1680 try: |
| 1643 result = Main(sys.argv) | 1681 result = Main(sys.argv) |
| 1644 except Error, e: | 1682 except Error, e: |
| 1645 print "Error: %s" % str(e) | 1683 print "Error: %s" % str(e) |
| 1646 result = 1 | 1684 result = 1 |
| 1647 sys.exit(result) | 1685 sys.exit(result) |
| 1648 | 1686 |
| 1649 # vim: ts=2:sw=2:tw=80:et: | 1687 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |