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 |