diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | .project | 2 | ||||
| -rw-r--r-- | .pydevproject | 2 | ||||
| -rw-r--r-- | .pylintrc | 2 | ||||
| -rw-r--r-- | command.py | 15 | ||||
| -rw-r--r-- | docs/manifest-format.txt | 48 | ||||
| -rw-r--r-- | git_config.py | 43 | ||||
| -rw-r--r-- | git_refs.py | 9 | ||||
| -rwxr-xr-x | main.py | 16 | ||||
| -rw-r--r-- | manifest_xml.py | 103 | ||||
| -rw-r--r-- | project.py | 164 | ||||
| -rw-r--r-- | pyversion.py | 19 | ||||
| -rwxr-xr-x | repo | 202 | ||||
| -rw-r--r-- | subcmds/__init__.py | 4 | ||||
| -rw-r--r-- | subcmds/branches.py | 5 | ||||
| -rw-r--r-- | subcmds/cherry_pick.py | 2 | ||||
| -rw-r--r-- | subcmds/forall.py | 21 | ||||
| -rw-r--r-- | subcmds/help.py | 12 | ||||
| -rw-r--r-- | subcmds/info.py | 12 | ||||
| -rw-r--r-- | subcmds/init.py | 30 | ||||
| -rw-r--r-- | subcmds/list.py | 33 | ||||
| -rw-r--r-- | subcmds/overview.py | 2 | ||||
| -rw-r--r-- | subcmds/rebase.py | 2 | ||||
| -rw-r--r-- | subcmds/stage.py | 8 | ||||
| -rw-r--r-- | subcmds/status.py | 12 | ||||
| -rw-r--r-- | subcmds/sync.py | 55 | ||||
| -rw-r--r-- | subcmds/upload.py | 56 |
27 files changed, 597 insertions, 283 deletions
| @@ -1,2 +1,3 @@ | |||
| 1 | *.pyc | 1 | *.pyc |
| 2 | .repopickle_* | 2 | .repopickle_* |
| 3 | /repoc | ||
| @@ -1,6 +1,6 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <projectDescription> | 2 | <projectDescription> |
| 3 | <name>repo</name> | 3 | <name>git-repo</name> |
| 4 | <comment></comment> | 4 | <comment></comment> |
| 5 | <projects> | 5 | <projects> |
| 6 | </projects> | 6 | </projects> |
diff --git a/.pydevproject b/.pydevproject index 880abd62..27c2485a 100644 --- a/.pydevproject +++ b/.pydevproject | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | <pydev_project> | 4 | <pydev_project> |
| 5 | <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> | 5 | <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> |
| 6 | <path>/repo</path> | 6 | <path>/git-repo</path> |
| 7 | </pydev_pathproperty> | 7 | </pydev_pathproperty> |
| 8 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property> | 8 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property> |
| 9 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> | 9 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> |
| @@ -53,7 +53,7 @@ load-plugins= | |||
| 53 | enable=RP0004 | 53 | enable=RP0004 |
| 54 | 54 | ||
| 55 | # Disable the message(s) with the given id(s). | 55 | # Disable the message(s) with the given id(s). |
| 56 | disable=R0903,R0912,R0913,R0914,R0915,W0141,C0111,C0103,W0603,W0703,R0911,C0301,C0302,R0902,R0904,W0142,W0212,E1101,E1103,R0201,W0201,W0122,W0232,RP0001,RP0003,RP0101,RP0002,RP0401,RP0701,RP0801 | 56 | disable=R0903,R0912,R0913,R0914,R0915,W0141,C0111,C0103,W0603,W0703,R0911,C0301,C0302,R0902,R0904,W0142,W0212,E1101,E1103,R0201,W0201,W0122,W0232,RP0001,RP0003,RP0101,RP0002,RP0401,RP0701,RP0801,F0401,E0611,R0801,I0011 |
| 57 | 57 | ||
| 58 | [REPORTS] | 58 | [REPORTS] |
| 59 | 59 | ||
| @@ -136,11 +136,11 @@ class Command(object): | |||
| 136 | 136 | ||
| 137 | groups = mp.config.GetString('manifest.groups') | 137 | groups = mp.config.GetString('manifest.groups') |
| 138 | if not groups: | 138 | if not groups: |
| 139 | groups = 'all,-notdefault,platform-' + platform.system().lower() | 139 | groups = 'default,platform-' + platform.system().lower() |
| 140 | groups = [x for x in re.split(r'[,\s]+', groups) if x] | 140 | groups = [x for x in re.split(r'[,\s]+', groups) if x] |
| 141 | 141 | ||
| 142 | if not args: | 142 | if not args: |
| 143 | all_projects_list = all_projects.values() | 143 | all_projects_list = list(all_projects.values()) |
| 144 | derived_projects = {} | 144 | derived_projects = {} |
| 145 | for project in all_projects_list: | 145 | for project in all_projects_list: |
| 146 | if submodules_ok or project.sync_s: | 146 | if submodules_ok or project.sync_s: |
| @@ -186,6 +186,17 @@ class Command(object): | |||
| 186 | result.sort(key=_getpath) | 186 | result.sort(key=_getpath) |
| 187 | return result | 187 | return result |
| 188 | 188 | ||
| 189 | def FindProjects(self, args): | ||
| 190 | result = [] | ||
| 191 | patterns = [re.compile(r'%s' % a, re.IGNORECASE) for a in args] | ||
| 192 | for project in self.GetProjects(''): | ||
| 193 | for pattern in patterns: | ||
| 194 | if pattern.search(project.name) or pattern.search(project.relpath): | ||
| 195 | result.append(project) | ||
| 196 | break | ||
| 197 | result.sort(key=lambda project: project.relpath) | ||
| 198 | return result | ||
| 199 | |||
| 189 | # pylint: disable=W0223 | 200 | # pylint: disable=W0223 |
| 190 | # Pylint warns that the `InteractiveCommand` and `PagedCommand` classes do not | 201 | # Pylint warns that the `InteractiveCommand` and `PagedCommand` classes do not |
| 191 | # override method `Execute` which is abstract in `Command`. Since that method | 202 | # override method `Execute` which is abstract in `Command`. Since that method |
diff --git a/docs/manifest-format.txt b/docs/manifest-format.txt index 0bf09f6f..dcc90d07 100644 --- a/docs/manifest-format.txt +++ b/docs/manifest-format.txt | |||
| @@ -37,25 +37,29 @@ following DTD: | |||
| 37 | <!ATTLIST remote review CDATA #IMPLIED> | 37 | <!ATTLIST remote review CDATA #IMPLIED> |
| 38 | 38 | ||
| 39 | <!ELEMENT default (EMPTY)> | 39 | <!ELEMENT default (EMPTY)> |
| 40 | <!ATTLIST default remote IDREF #IMPLIED> | 40 | <!ATTLIST default remote IDREF #IMPLIED> |
| 41 | <!ATTLIST default revision CDATA #IMPLIED> | 41 | <!ATTLIST default revision CDATA #IMPLIED> |
| 42 | <!ATTLIST default sync-j CDATA #IMPLIED> | 42 | <!ATTLIST default dest-branch CDATA #IMPLIED> |
| 43 | <!ATTLIST default sync-c CDATA #IMPLIED> | 43 | <!ATTLIST default sync-j CDATA #IMPLIED> |
| 44 | <!ATTLIST default sync-s CDATA #IMPLIED> | 44 | <!ATTLIST default sync-c CDATA #IMPLIED> |
| 45 | <!ATTLIST default sync-s CDATA #IMPLIED> | ||
| 45 | 46 | ||
| 46 | <!ELEMENT manifest-server (EMPTY)> | 47 | <!ELEMENT manifest-server (EMPTY)> |
| 47 | <!ATTLIST url CDATA #REQUIRED> | 48 | <!ATTLIST url CDATA #REQUIRED> |
| 48 | 49 | ||
| 49 | <!ELEMENT project (annotation?, | 50 | <!ELEMENT project (annotation?, |
| 50 | project*)> | 51 | project*)> |
| 51 | <!ATTLIST project name CDATA #REQUIRED> | 52 | <!ATTLIST project name CDATA #REQUIRED> |
| 52 | <!ATTLIST project path CDATA #IMPLIED> | 53 | <!ATTLIST project path CDATA #IMPLIED> |
| 53 | <!ATTLIST project remote IDREF #IMPLIED> | 54 | <!ATTLIST project remote IDREF #IMPLIED> |
| 54 | <!ATTLIST project revision CDATA #IMPLIED> | 55 | <!ATTLIST project revision CDATA #IMPLIED> |
| 55 | <!ATTLIST project groups CDATA #IMPLIED> | 56 | <!ATTLIST project dest-branch CDATA #IMPLIED> |
| 56 | <!ATTLIST project sync-c CDATA #IMPLIED> | 57 | <!ATTLIST project groups CDATA #IMPLIED> |
| 57 | <!ATTLIST project sync-s CDATA #IMPLIED> | 58 | <!ATTLIST project sync-c CDATA #IMPLIED> |
| 59 | <!ATTLIST project sync-s CDATA #IMPLIED> | ||
| 58 | <!ATTLIST project upstream CDATA #IMPLIED> | 60 | <!ATTLIST project upstream CDATA #IMPLIED> |
| 61 | <!ATTLIST project clone-depth CDATA #IMPLIED> | ||
| 62 | <!ATTLIST project force-path CDATA #IMPLIED> | ||
| 59 | 63 | ||
| 60 | <!ELEMENT annotation (EMPTY)> | 64 | <!ELEMENT annotation (EMPTY)> |
| 61 | <!ATTLIST annotation name CDATA #REQUIRED> | 65 | <!ATTLIST annotation name CDATA #REQUIRED> |
| @@ -123,6 +127,11 @@ Attribute `revision`: Name of a Git branch (e.g. `master` or | |||
| 123 | `refs/heads/master`). Project elements lacking their own | 127 | `refs/heads/master`). Project elements lacking their own |
| 124 | revision attribute will use this revision. | 128 | revision attribute will use this revision. |
| 125 | 129 | ||
| 130 | Attribute `dest-branch`: Name of a Git branch (e.g. `master`). | ||
| 131 | Project elements not setting their own `dest-branch` will inherit | ||
| 132 | this value. If this value is not set, projects will use `revision` | ||
| 133 | by default instead. | ||
| 134 | |||
| 126 | Attribute `sync_j`: Number of parallel jobs to use when synching. | 135 | Attribute `sync_j`: Number of parallel jobs to use when synching. |
| 127 | 136 | ||
| 128 | Attribute `sync_c`: Set to true to only sync the given Git | 137 | Attribute `sync_c`: Set to true to only sync the given Git |
| @@ -201,6 +210,11 @@ Tags and/or explicit SHA-1s should work in theory, but have not | |||
| 201 | been extensively tested. If not supplied the revision given by | 210 | been extensively tested. If not supplied the revision given by |
| 202 | the default element is used. | 211 | the default element is used. |
| 203 | 212 | ||
| 213 | Attribute `dest-branch`: Name of a Git branch (e.g. `master`). | ||
| 214 | When using `repo upload`, changes will be submitted for code | ||
| 215 | review on this branch. If unspecified both here and in the | ||
| 216 | default element, `revision` is used instead. | ||
| 217 | |||
| 204 | Attribute `groups`: List of groups to which this project belongs, | 218 | Attribute `groups`: List of groups to which this project belongs, |
| 205 | whitespace or comma separated. All projects belong to the group | 219 | whitespace or comma separated. All projects belong to the group |
| 206 | "all", and each project automatically belongs to a group of | 220 | "all", and each project automatically belongs to a group of |
| @@ -222,6 +236,16 @@ Attribute `upstream`: Name of the Git branch in which a sha1 | |||
| 222 | can be found. Used when syncing a revision locked manifest in | 236 | can be found. Used when syncing a revision locked manifest in |
| 223 | -c mode to avoid having to sync the entire ref space. | 237 | -c mode to avoid having to sync the entire ref space. |
| 224 | 238 | ||
| 239 | Attribute `clone-depth`: Set the depth to use when fetching this | ||
| 240 | project. If specified, this value will override any value given | ||
| 241 | to repo init with the --depth option on the command line. | ||
| 242 | |||
| 243 | Attribute `force-path`: Set to true to force this project to create the | ||
| 244 | local mirror repository according to its `path` attribute (if supplied) | ||
| 245 | rather than the `name` attribute. This attribute only applies to the | ||
| 246 | local mirrors syncing, it will be ignored when syncing the projects in a | ||
| 247 | client working directory. | ||
| 248 | |||
| 225 | Element annotation | 249 | Element annotation |
| 226 | ------------------ | 250 | ------------------ |
| 227 | 251 | ||
diff --git a/git_config.py b/git_config.py index 56cc6a24..a294a0b6 100644 --- a/git_config.py +++ b/git_config.py | |||
| @@ -14,8 +14,9 @@ | |||
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | 16 | from __future__ import print_function |
| 17 | import cPickle | 17 | |
| 18 | import os | 18 | import os |
| 19 | import pickle | ||
| 19 | import re | 20 | import re |
| 20 | import subprocess | 21 | import subprocess |
| 21 | import sys | 22 | import sys |
| @@ -24,14 +25,13 @@ try: | |||
| 24 | except ImportError: | 25 | except ImportError: |
| 25 | import dummy_threading as _threading | 26 | import dummy_threading as _threading |
| 26 | import time | 27 | import time |
| 27 | try: | 28 | |
| 28 | import urllib2 | 29 | from pyversion import is_python3 |
| 29 | except ImportError: | 30 | if is_python3(): |
| 30 | # For python3 | ||
| 31 | import urllib.request | 31 | import urllib.request |
| 32 | import urllib.error | 32 | import urllib.error |
| 33 | else: | 33 | else: |
| 34 | # For python2 | 34 | import urllib2 |
| 35 | import imp | 35 | import imp |
| 36 | urllib = imp.new_module('urllib') | 36 | urllib = imp.new_module('urllib') |
| 37 | urllib.request = urllib2 | 37 | urllib.request = urllib2 |
| @@ -40,6 +40,10 @@ else: | |||
| 40 | from signal import SIGTERM | 40 | from signal import SIGTERM |
| 41 | from error import GitError, UploadError | 41 | from error import GitError, UploadError |
| 42 | from trace import Trace | 42 | from trace import Trace |
| 43 | if is_python3(): | ||
| 44 | from http.client import HTTPException | ||
| 45 | else: | ||
| 46 | from httplib import HTTPException | ||
| 43 | 47 | ||
| 44 | from git_command import GitCommand | 48 | from git_command import GitCommand |
| 45 | from git_command import ssh_sock | 49 | from git_command import ssh_sock |
| @@ -262,7 +266,7 @@ class GitConfig(object): | |||
| 262 | Trace(': unpickle %s', self.file) | 266 | Trace(': unpickle %s', self.file) |
| 263 | fd = open(self._pickle, 'rb') | 267 | fd = open(self._pickle, 'rb') |
| 264 | try: | 268 | try: |
| 265 | return cPickle.load(fd) | 269 | return pickle.load(fd) |
| 266 | finally: | 270 | finally: |
| 267 | fd.close() | 271 | fd.close() |
| 268 | except EOFError: | 272 | except EOFError: |
| @@ -271,7 +275,7 @@ class GitConfig(object): | |||
| 271 | except IOError: | 275 | except IOError: |
| 272 | os.remove(self._pickle) | 276 | os.remove(self._pickle) |
| 273 | return None | 277 | return None |
| 274 | except cPickle.PickleError: | 278 | except pickle.PickleError: |
| 275 | os.remove(self._pickle) | 279 | os.remove(self._pickle) |
| 276 | return None | 280 | return None |
| 277 | 281 | ||
| @@ -279,13 +283,13 @@ class GitConfig(object): | |||
| 279 | try: | 283 | try: |
| 280 | fd = open(self._pickle, 'wb') | 284 | fd = open(self._pickle, 'wb') |
| 281 | try: | 285 | try: |
| 282 | cPickle.dump(cache, fd, cPickle.HIGHEST_PROTOCOL) | 286 | pickle.dump(cache, fd, pickle.HIGHEST_PROTOCOL) |
| 283 | finally: | 287 | finally: |
| 284 | fd.close() | 288 | fd.close() |
| 285 | except IOError: | 289 | except IOError: |
| 286 | if os.path.exists(self._pickle): | 290 | if os.path.exists(self._pickle): |
| 287 | os.remove(self._pickle) | 291 | os.remove(self._pickle) |
| 288 | except cPickle.PickleError: | 292 | except pickle.PickleError: |
| 289 | if os.path.exists(self._pickle): | 293 | if os.path.exists(self._pickle): |
| 290 | os.remove(self._pickle) | 294 | os.remove(self._pickle) |
| 291 | 295 | ||
| @@ -537,8 +541,8 @@ class Remote(object): | |||
| 537 | self.url = self._Get('url') | 541 | self.url = self._Get('url') |
| 538 | self.review = self._Get('review') | 542 | self.review = self._Get('review') |
| 539 | self.projectname = self._Get('projectname') | 543 | self.projectname = self._Get('projectname') |
| 540 | self.fetch = map(RefSpec.FromString, | 544 | self.fetch = list(map(RefSpec.FromString, |
| 541 | self._Get('fetch', all_keys=True)) | 545 | self._Get('fetch', all_keys=True))) |
| 542 | self._review_url = None | 546 | self._review_url = None |
| 543 | 547 | ||
| 544 | def _InsteadOf(self): | 548 | def _InsteadOf(self): |
| @@ -592,14 +596,11 @@ class Remote(object): | |||
| 592 | try: | 596 | try: |
| 593 | info_url = u + 'ssh_info' | 597 | info_url = u + 'ssh_info' |
| 594 | info = urllib.request.urlopen(info_url).read() | 598 | info = urllib.request.urlopen(info_url).read() |
| 595 | if '<' in info: | 599 | if info == 'NOT_AVAILABLE' or '<' in info: |
| 596 | # Assume the server gave us some sort of HTML | 600 | # If `info` contains '<', we assume the server gave us some sort |
| 597 | # response back, like maybe a login page. | 601 | # of HTML response back, like maybe a login page. |
| 598 | # | 602 | # |
| 599 | raise UploadError('%s: Cannot parse response' % info_url) | 603 | # Assume HTTP if SSH is not enabled or ssh_info doesn't look right. |
| 600 | |||
| 601 | if info == 'NOT_AVAILABLE': | ||
| 602 | # Assume HTTP if SSH is not enabled. | ||
| 603 | self._review_url = http_url + 'p/' | 604 | self._review_url = http_url + 'p/' |
| 604 | else: | 605 | else: |
| 605 | host, port = info.split() | 606 | host, port = info.split() |
| @@ -608,6 +609,8 @@ class Remote(object): | |||
| 608 | raise UploadError('%s: %s' % (self.review, str(e))) | 609 | raise UploadError('%s: %s' % (self.review, str(e))) |
| 609 | except urllib.error.URLError as e: | 610 | except urllib.error.URLError as e: |
| 610 | raise UploadError('%s: %s' % (self.review, str(e))) | 611 | raise UploadError('%s: %s' % (self.review, str(e))) |
| 612 | except HTTPException as e: | ||
| 613 | raise UploadError('%s: %s' % (self.review, e.__class__.__name__)) | ||
| 611 | 614 | ||
| 612 | REVIEW_CACHE[u] = self._review_url | 615 | REVIEW_CACHE[u] = self._review_url |
| 613 | return self._review_url + self.projectname | 616 | return self._review_url + self.projectname |
| @@ -657,7 +660,7 @@ class Remote(object): | |||
| 657 | self._Set('url', self.url) | 660 | self._Set('url', self.url) |
| 658 | self._Set('review', self.review) | 661 | self._Set('review', self.review) |
| 659 | self._Set('projectname', self.projectname) | 662 | self._Set('projectname', self.projectname) |
| 660 | self._Set('fetch', map(str, self.fetch)) | 663 | self._Set('fetch', list(map(str, self.fetch))) |
| 661 | 664 | ||
| 662 | def _Set(self, key, value): | 665 | def _Set(self, key, value): |
| 663 | key = 'remote.%s.%s' % (self.name, key) | 666 | key = 'remote.%s.%s' % (self.name, key) |
diff --git a/git_refs.py b/git_refs.py index cfeffba9..4dd68769 100644 --- a/git_refs.py +++ b/git_refs.py | |||
| @@ -66,7 +66,7 @@ class GitRefs(object): | |||
| 66 | def _NeedUpdate(self): | 66 | def _NeedUpdate(self): |
| 67 | Trace(': scan refs %s', self._gitdir) | 67 | Trace(': scan refs %s', self._gitdir) |
| 68 | 68 | ||
| 69 | for name, mtime in self._mtime.iteritems(): | 69 | for name, mtime in self._mtime.items(): |
| 70 | try: | 70 | try: |
| 71 | if mtime != os.path.getmtime(os.path.join(self._gitdir, name)): | 71 | if mtime != os.path.getmtime(os.path.join(self._gitdir, name)): |
| 72 | return True | 72 | return True |
| @@ -89,7 +89,7 @@ class GitRefs(object): | |||
| 89 | attempts = 0 | 89 | attempts = 0 |
| 90 | while scan and attempts < 5: | 90 | while scan and attempts < 5: |
| 91 | scan_next = {} | 91 | scan_next = {} |
| 92 | for name, dest in scan.iteritems(): | 92 | for name, dest in scan.items(): |
| 93 | if dest in self._phyref: | 93 | if dest in self._phyref: |
| 94 | self._phyref[name] = self._phyref[dest] | 94 | self._phyref[name] = self._phyref[dest] |
| 95 | else: | 95 | else: |
| @@ -108,6 +108,7 @@ class GitRefs(object): | |||
| 108 | return | 108 | return |
| 109 | try: | 109 | try: |
| 110 | for line in fd: | 110 | for line in fd: |
| 111 | line = str(line) | ||
| 111 | if line[0] == '#': | 112 | if line[0] == '#': |
| 112 | continue | 113 | continue |
| 113 | if line[0] == '^': | 114 | if line[0] == '^': |
| @@ -150,6 +151,10 @@ class GitRefs(object): | |||
| 150 | finally: | 151 | finally: |
| 151 | fd.close() | 152 | fd.close() |
| 152 | 153 | ||
| 154 | try: | ||
| 155 | ref_id = ref_id.decode() | ||
| 156 | except AttributeError: | ||
| 157 | pass | ||
| 153 | if not ref_id: | 158 | if not ref_id: |
| 154 | return | 159 | return |
| 155 | ref_id = ref_id[:-1] | 160 | ref_id = ref_id[:-1] |
| @@ -22,13 +22,12 @@ import optparse | |||
| 22 | import os | 22 | import os |
| 23 | import sys | 23 | import sys |
| 24 | import time | 24 | import time |
| 25 | try: | 25 | |
| 26 | import urllib2 | 26 | from pyversion import is_python3 |
| 27 | except ImportError: | 27 | if is_python3(): |
| 28 | # For python3 | ||
| 29 | import urllib.request | 28 | import urllib.request |
| 30 | else: | 29 | else: |
| 31 | # For python2 | 30 | import urllib2 |
| 32 | urllib = imp.new_module('urllib') | 31 | urllib = imp.new_module('urllib') |
| 33 | urllib.request = urllib2 | 32 | urllib.request = urllib2 |
| 34 | 33 | ||
| @@ -50,6 +49,11 @@ from pager import RunPager | |||
| 50 | 49 | ||
| 51 | from subcmds import all_commands | 50 | from subcmds import all_commands |
| 52 | 51 | ||
| 52 | if not is_python3(): | ||
| 53 | # pylint:disable=W0622 | ||
| 54 | input = raw_input | ||
| 55 | # pylint:enable=W0622 | ||
| 56 | |||
| 53 | global_options = optparse.OptionParser( | 57 | global_options = optparse.OptionParser( |
| 54 | usage="repo [-p|--paginate|--no-pager] COMMAND [ARGS]" | 58 | usage="repo [-p|--paginate|--no-pager] COMMAND [ARGS]" |
| 55 | ) | 59 | ) |
| @@ -286,7 +290,7 @@ def _AddPasswordFromUserInput(handler, msg, req): | |||
| 286 | if user is None: | 290 | if user is None: |
| 287 | print(msg) | 291 | print(msg) |
| 288 | try: | 292 | try: |
| 289 | user = raw_input('User: ') | 293 | user = input('User: ') |
| 290 | password = getpass.getpass() | 294 | password = getpass.getpass() |
| 291 | except KeyboardInterrupt: | 295 | except KeyboardInterrupt: |
| 292 | return | 296 | return |
diff --git a/manifest_xml.py b/manifest_xml.py index 07f0c66a..bdbb1d40 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
| @@ -18,9 +18,17 @@ import itertools | |||
| 18 | import os | 18 | import os |
| 19 | import re | 19 | import re |
| 20 | import sys | 20 | import sys |
| 21 | import urlparse | ||
| 22 | import xml.dom.minidom | 21 | import xml.dom.minidom |
| 23 | 22 | ||
| 23 | from pyversion import is_python3 | ||
| 24 | if is_python3(): | ||
| 25 | import urllib.parse | ||
| 26 | else: | ||
| 27 | import imp | ||
| 28 | import urlparse | ||
| 29 | urllib = imp.new_module('urllib') | ||
| 30 | urllib.parse = urlparse | ||
| 31 | |||
| 24 | from git_config import GitConfig | 32 | from git_config import GitConfig |
| 25 | from git_refs import R_HEADS, HEAD | 33 | from git_refs import R_HEADS, HEAD |
| 26 | from project import RemoteSpec, Project, MetaProject | 34 | from project import RemoteSpec, Project, MetaProject |
| @@ -30,13 +38,14 @@ MANIFEST_FILE_NAME = 'manifest.xml' | |||
| 30 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' | 38 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' |
| 31 | LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' | 39 | LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' |
| 32 | 40 | ||
| 33 | urlparse.uses_relative.extend(['ssh', 'git']) | 41 | urllib.parse.uses_relative.extend(['ssh', 'git']) |
| 34 | urlparse.uses_netloc.extend(['ssh', 'git']) | 42 | urllib.parse.uses_netloc.extend(['ssh', 'git']) |
| 35 | 43 | ||
| 36 | class _Default(object): | 44 | class _Default(object): |
| 37 | """Project defaults within the manifest.""" | 45 | """Project defaults within the manifest.""" |
| 38 | 46 | ||
| 39 | revisionExpr = None | 47 | revisionExpr = None |
| 48 | destBranchExpr = None | ||
| 40 | remote = None | 49 | remote = None |
| 41 | sync_j = 1 | 50 | sync_j = 1 |
| 42 | sync_c = False | 51 | sync_c = False |
| @@ -73,7 +82,7 @@ class _XmlRemote(object): | |||
| 73 | # ie, if manifestUrl is of the form <hostname:port> | 82 | # ie, if manifestUrl is of the form <hostname:port> |
| 74 | if manifestUrl.find(':') != manifestUrl.find('/') - 1: | 83 | if manifestUrl.find(':') != manifestUrl.find('/') - 1: |
| 75 | manifestUrl = 'gopher://' + manifestUrl | 84 | manifestUrl = 'gopher://' + manifestUrl |
| 76 | url = urlparse.urljoin(manifestUrl, url) | 85 | url = urllib.parse.urljoin(manifestUrl, url) |
| 77 | url = re.sub(r'^gopher://', '', url) | 86 | url = re.sub(r'^gopher://', '', url) |
| 78 | if p: | 87 | if p: |
| 79 | url = 'persistent-' + url | 88 | url = 'persistent-' + url |
| @@ -83,7 +92,7 @@ class _XmlRemote(object): | |||
| 83 | url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName | 92 | url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName |
| 84 | remoteName = self.name | 93 | remoteName = self.name |
| 85 | if self.remoteAlias: | 94 | if self.remoteAlias: |
| 86 | remoteName = self.remoteAlias | 95 | remoteName = self.remoteAlias |
| 87 | return RemoteSpec(remoteName, url, self.reviewUrl) | 96 | return RemoteSpec(remoteName, url, self.reviewUrl) |
| 88 | 97 | ||
| 89 | class XmlManifest(object): | 98 | class XmlManifest(object): |
| @@ -94,6 +103,7 @@ class XmlManifest(object): | |||
| 94 | self.topdir = os.path.dirname(self.repodir) | 103 | self.topdir = os.path.dirname(self.repodir) |
| 95 | self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME) | 104 | self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME) |
| 96 | self.globalConfig = GitConfig.ForUser() | 105 | self.globalConfig = GitConfig.ForUser() |
| 106 | self.localManifestWarning = False | ||
| 97 | 107 | ||
| 98 | self.repoProject = MetaProject(self, 'repo', | 108 | self.repoProject = MetaProject(self, 'repo', |
| 99 | gitdir = os.path.join(repodir, 'repo/.git'), | 109 | gitdir = os.path.join(repodir, 'repo/.git'), |
| @@ -137,6 +147,8 @@ class XmlManifest(object): | |||
| 137 | root.appendChild(e) | 147 | root.appendChild(e) |
| 138 | e.setAttribute('name', r.name) | 148 | e.setAttribute('name', r.name) |
| 139 | e.setAttribute('fetch', r.fetchUrl) | 149 | e.setAttribute('fetch', r.fetchUrl) |
| 150 | if r.remoteAlias is not None: | ||
| 151 | e.setAttribute('alias', r.remoteAlias) | ||
| 140 | if r.reviewUrl is not None: | 152 | if r.reviewUrl is not None: |
| 141 | e.setAttribute('review', r.reviewUrl) | 153 | e.setAttribute('review', r.reviewUrl) |
| 142 | 154 | ||
| @@ -163,10 +175,8 @@ class XmlManifest(object): | |||
| 163 | notice_element.appendChild(doc.createTextNode(indented_notice)) | 175 | notice_element.appendChild(doc.createTextNode(indented_notice)) |
| 164 | 176 | ||
| 165 | d = self.default | 177 | d = self.default |
| 166 | sort_remotes = list(self.remotes.keys()) | ||
| 167 | sort_remotes.sort() | ||
| 168 | 178 | ||
| 169 | for r in sort_remotes: | 179 | for r in sorted(self.remotes): |
| 170 | self._RemoteToXml(self.remotes[r], doc, root) | 180 | self._RemoteToXml(self.remotes[r], doc, root) |
| 171 | if self.remotes: | 181 | if self.remotes: |
| 172 | root.appendChild(doc.createTextNode('')) | 182 | root.appendChild(doc.createTextNode('')) |
| @@ -217,7 +227,8 @@ class XmlManifest(object): | |||
| 217 | e.setAttribute('name', name) | 227 | e.setAttribute('name', name) |
| 218 | if relpath != name: | 228 | if relpath != name: |
| 219 | e.setAttribute('path', relpath) | 229 | e.setAttribute('path', relpath) |
| 220 | if not d.remote or p.remote.name != d.remote.name: | 230 | remoteName = d.remote.remoteAlias or d.remote.name |
| 231 | if not d.remote or p.remote.name != remoteName: | ||
| 221 | e.setAttribute('remote', p.remote.name) | 232 | e.setAttribute('remote', p.remote.name) |
| 222 | if peg_rev: | 233 | if peg_rev: |
| 223 | if self.IsMirror: | 234 | if self.IsMirror: |
| @@ -258,12 +269,11 @@ class XmlManifest(object): | |||
| 258 | e.setAttribute('sync-s', 'true') | 269 | e.setAttribute('sync-s', 'true') |
| 259 | 270 | ||
| 260 | if p.subprojects: | 271 | if p.subprojects: |
| 261 | sort_projects = [subp.name for subp in p.subprojects] | 272 | sort_projects = list(sorted([subp.name for subp in p.subprojects])) |
| 262 | sort_projects.sort() | ||
| 263 | output_projects(p, e, sort_projects) | 273 | output_projects(p, e, sort_projects) |
| 264 | 274 | ||
| 265 | sort_projects = [key for key in self.projects.keys() | 275 | sort_projects = list(sorted([key for key, value in self.projects.items() |
| 266 | if not self.projects[key].parent] | 276 | if not value.parent])) |
| 267 | sort_projects.sort() | 277 | sort_projects.sort() |
| 268 | output_projects(None, root, sort_projects) | 278 | output_projects(None, root, sort_projects) |
| 269 | 279 | ||
| @@ -335,9 +345,11 @@ class XmlManifest(object): | |||
| 335 | 345 | ||
| 336 | local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME) | 346 | local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME) |
| 337 | if os.path.exists(local): | 347 | if os.path.exists(local): |
| 338 | print('warning: %s is deprecated; put local manifests in `%s` instead' | 348 | if not self.localManifestWarning: |
| 339 | % (LOCAL_MANIFEST_NAME, os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME)), | 349 | self.localManifestWarning = True |
| 340 | file=sys.stderr) | 350 | print('warning: %s is deprecated; put local manifests in `%s` instead' |
| 351 | % (LOCAL_MANIFEST_NAME, os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME)), | ||
| 352 | file=sys.stderr) | ||
| 341 | nodes.append(self._ParseManifestXml(local, self.repodir)) | 353 | nodes.append(self._ParseManifestXml(local, self.repodir)) |
| 342 | 354 | ||
| 343 | local_dir = os.path.abspath(os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME)) | 355 | local_dir = os.path.abspath(os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME)) |
| @@ -385,9 +397,8 @@ class XmlManifest(object): | |||
| 385 | name = self._reqatt(node, 'name') | 397 | name = self._reqatt(node, 'name') |
| 386 | fp = os.path.join(include_root, name) | 398 | fp = os.path.join(include_root, name) |
| 387 | if not os.path.isfile(fp): | 399 | if not os.path.isfile(fp): |
| 388 | raise ManifestParseError, \ | 400 | raise ManifestParseError("include %s doesn't exist or isn't a file" |
| 389 | "include %s doesn't exist or isn't a file" % \ | 401 | % (name,)) |
| 390 | (name,) | ||
| 391 | try: | 402 | try: |
| 392 | nodes.extend(self._ParseManifestXml(fp, include_root)) | 403 | nodes.extend(self._ParseManifestXml(fp, include_root)) |
| 393 | # should isolate this to the exact exception, but that's | 404 | # should isolate this to the exact exception, but that's |
| @@ -493,7 +504,7 @@ class XmlManifest(object): | |||
| 493 | name = None | 504 | name = None |
| 494 | m_url = m.GetRemote(m.remote.name).url | 505 | m_url = m.GetRemote(m.remote.name).url |
| 495 | if m_url.endswith('/.git'): | 506 | if m_url.endswith('/.git'): |
| 496 | raise ManifestParseError, 'refusing to mirror %s' % m_url | 507 | raise ManifestParseError('refusing to mirror %s' % m_url) |
| 497 | 508 | ||
| 498 | if self._default and self._default.remote: | 509 | if self._default and self._default.remote: |
| 499 | url = self._default.remote.resolvedFetchUrl | 510 | url = self._default.remote.resolvedFetchUrl |
| @@ -550,6 +561,8 @@ class XmlManifest(object): | |||
| 550 | if d.revisionExpr == '': | 561 | if d.revisionExpr == '': |
| 551 | d.revisionExpr = None | 562 | d.revisionExpr = None |
| 552 | 563 | ||
| 564 | d.destBranchExpr = node.getAttribute('dest-branch') or None | ||
| 565 | |||
| 553 | sync_j = node.getAttribute('sync-j') | 566 | sync_j = node.getAttribute('sync-j') |
| 554 | if sync_j == '' or sync_j is None: | 567 | if sync_j == '' or sync_j is None: |
| 555 | d.sync_j = 1 | 568 | d.sync_j = 1 |
| @@ -587,7 +600,7 @@ class XmlManifest(object): | |||
| 587 | 600 | ||
| 588 | # Figure out minimum indentation, skipping the first line (the same line | 601 | # Figure out minimum indentation, skipping the first line (the same line |
| 589 | # as the <notice> tag)... | 602 | # as the <notice> tag)... |
| 590 | minIndent = sys.maxint | 603 | minIndent = sys.maxsize |
| 591 | lines = notice.splitlines() | 604 | lines = notice.splitlines() |
| 592 | for line in lines[1:]: | 605 | for line in lines[1:]: |
| 593 | lstrippedLine = line.lstrip() | 606 | lstrippedLine = line.lstrip() |
| @@ -626,25 +639,22 @@ class XmlManifest(object): | |||
| 626 | if remote is None: | 639 | if remote is None: |
| 627 | remote = self._default.remote | 640 | remote = self._default.remote |
| 628 | if remote is None: | 641 | if remote is None: |
| 629 | raise ManifestParseError, \ | 642 | raise ManifestParseError("no remote for project %s within %s" % |
| 630 | "no remote for project %s within %s" % \ | 643 | (name, self.manifestFile)) |
| 631 | (name, self.manifestFile) | ||
| 632 | 644 | ||
| 633 | revisionExpr = node.getAttribute('revision') | 645 | revisionExpr = node.getAttribute('revision') |
| 634 | if not revisionExpr: | 646 | if not revisionExpr: |
| 635 | revisionExpr = self._default.revisionExpr | 647 | revisionExpr = self._default.revisionExpr |
| 636 | if not revisionExpr: | 648 | if not revisionExpr: |
| 637 | raise ManifestParseError, \ | 649 | raise ManifestParseError("no revision for project %s within %s" % |
| 638 | "no revision for project %s within %s" % \ | 650 | (name, self.manifestFile)) |
| 639 | (name, self.manifestFile) | ||
| 640 | 651 | ||
| 641 | path = node.getAttribute('path') | 652 | path = node.getAttribute('path') |
| 642 | if not path: | 653 | if not path: |
| 643 | path = name | 654 | path = name |
| 644 | if path.startswith('/'): | 655 | if path.startswith('/'): |
| 645 | raise ManifestParseError, \ | 656 | raise ManifestParseError("project %s path cannot be absolute in %s" % |
| 646 | "project %s path cannot be absolute in %s" % \ | 657 | (name, self.manifestFile)) |
| 647 | (name, self.manifestFile) | ||
| 648 | 658 | ||
| 649 | rebase = node.getAttribute('rebase') | 659 | rebase = node.getAttribute('rebase') |
| 650 | if not rebase: | 660 | if not rebase: |
| @@ -664,6 +674,18 @@ class XmlManifest(object): | |||
| 664 | else: | 674 | else: |
| 665 | sync_s = sync_s.lower() in ("yes", "true", "1") | 675 | sync_s = sync_s.lower() in ("yes", "true", "1") |
| 666 | 676 | ||
| 677 | clone_depth = node.getAttribute('clone-depth') | ||
| 678 | if clone_depth: | ||
| 679 | try: | ||
| 680 | clone_depth = int(clone_depth) | ||
| 681 | if clone_depth <= 0: | ||
| 682 | raise ValueError() | ||
| 683 | except ValueError: | ||
| 684 | raise ManifestParseError('invalid clone-depth %s in %s' % | ||
| 685 | (clone_depth, self.manifestFile)) | ||
| 686 | |||
| 687 | dest_branch = node.getAttribute('dest-branch') or self._default.destBranchExpr | ||
| 688 | |||
| 667 | upstream = node.getAttribute('upstream') | 689 | upstream = node.getAttribute('upstream') |
| 668 | 690 | ||
| 669 | groups = '' | 691 | groups = '' |
| @@ -679,6 +701,10 @@ class XmlManifest(object): | |||
| 679 | default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath] | 701 | default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath] |
| 680 | groups.extend(set(default_groups).difference(groups)) | 702 | groups.extend(set(default_groups).difference(groups)) |
| 681 | 703 | ||
| 704 | if self.IsMirror and node.hasAttribute('force-path'): | ||
| 705 | if node.getAttribute('force-path').lower() in ("yes", "true", "1"): | ||
| 706 | gitdir = os.path.join(self.topdir, '%s.git' % path) | ||
| 707 | |||
| 682 | project = Project(manifest = self, | 708 | project = Project(manifest = self, |
| 683 | name = name, | 709 | name = name, |
| 684 | remote = remote.ToRemoteSpec(name), | 710 | remote = remote.ToRemoteSpec(name), |
| @@ -691,8 +717,10 @@ class XmlManifest(object): | |||
| 691 | groups = groups, | 717 | groups = groups, |
| 692 | sync_c = sync_c, | 718 | sync_c = sync_c, |
| 693 | sync_s = sync_s, | 719 | sync_s = sync_s, |
| 720 | clone_depth = clone_depth, | ||
| 694 | upstream = upstream, | 721 | upstream = upstream, |
| 695 | parent = parent) | 722 | parent = parent, |
| 723 | dest_branch = dest_branch) | ||
| 696 | 724 | ||
| 697 | for n in node.childNodes: | 725 | for n in node.childNodes: |
| 698 | if n.nodeName == 'copyfile': | 726 | if n.nodeName == 'copyfile': |
| @@ -748,7 +776,8 @@ class XmlManifest(object): | |||
| 748 | except ManifestParseError: | 776 | except ManifestParseError: |
| 749 | keep = "true" | 777 | keep = "true" |
| 750 | if keep != "true" and keep != "false": | 778 | if keep != "true" and keep != "false": |
| 751 | raise ManifestParseError, "optional \"keep\" attribute must be \"true\" or \"false\"" | 779 | raise ManifestParseError('optional "keep" attribute must be ' |
| 780 | '"true" or "false"') | ||
| 752 | project.AddAnnotation(name, value, keep) | 781 | project.AddAnnotation(name, value, keep) |
| 753 | 782 | ||
| 754 | def _get_remote(self, node): | 783 | def _get_remote(self, node): |
| @@ -758,9 +787,8 @@ class XmlManifest(object): | |||
| 758 | 787 | ||
| 759 | v = self._remotes.get(name) | 788 | v = self._remotes.get(name) |
| 760 | if not v: | 789 | if not v: |
| 761 | raise ManifestParseError, \ | 790 | raise ManifestParseError("remote %s not defined in %s" % |
| 762 | "remote %s not defined in %s" % \ | 791 | (name, self.manifestFile)) |
| 763 | (name, self.manifestFile) | ||
| 764 | return v | 792 | return v |
| 765 | 793 | ||
| 766 | def _reqatt(self, node, attname): | 794 | def _reqatt(self, node, attname): |
| @@ -769,7 +797,6 @@ class XmlManifest(object): | |||
| 769 | """ | 797 | """ |
| 770 | v = node.getAttribute(attname) | 798 | v = node.getAttribute(attname) |
| 771 | if not v: | 799 | if not v: |
| 772 | raise ManifestParseError, \ | 800 | raise ManifestParseError("no %s in <%s> within %s" % |
| 773 | "no %s in <%s> within %s" % \ | 801 | (attname, node.nodeName, self.manifestFile)) |
| 774 | (attname, node.nodeName, self.manifestFile) | ||
| 775 | return v | 802 | return v |
| @@ -36,6 +36,12 @@ from trace import IsTrace, Trace | |||
| 36 | 36 | ||
| 37 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M | 37 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M |
| 38 | 38 | ||
| 39 | from pyversion import is_python3 | ||
| 40 | if not is_python3(): | ||
| 41 | # pylint:disable=W0622 | ||
| 42 | input = raw_input | ||
| 43 | # pylint:enable=W0622 | ||
| 44 | |||
| 39 | def _lwrite(path, content): | 45 | def _lwrite(path, content): |
| 40 | lock = '%s.lock' % path | 46 | lock = '%s.lock' % path |
| 41 | 47 | ||
| @@ -78,7 +84,7 @@ def _ProjectHooks(): | |||
| 78 | if _project_hook_list is None: | 84 | if _project_hook_list is None: |
| 79 | d = os.path.abspath(os.path.dirname(__file__)) | 85 | d = os.path.abspath(os.path.dirname(__file__)) |
| 80 | d = os.path.join(d , 'hooks') | 86 | d = os.path.join(d , 'hooks') |
| 81 | _project_hook_list = map(lambda x: os.path.join(d, x), os.listdir(d)) | 87 | _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)] |
| 82 | return _project_hook_list | 88 | return _project_hook_list |
| 83 | 89 | ||
| 84 | 90 | ||
| @@ -151,11 +157,12 @@ class ReviewableBranch(object): | |||
| 151 | R_HEADS + self.name, | 157 | R_HEADS + self.name, |
| 152 | '--') | 158 | '--') |
| 153 | 159 | ||
| 154 | def UploadForReview(self, people, auto_topic=False, draft=False): | 160 | def UploadForReview(self, people, auto_topic=False, draft=False, dest_branch=None): |
| 155 | self.project.UploadForReview(self.name, | 161 | self.project.UploadForReview(self.name, |
| 156 | people, | 162 | people, |
| 157 | auto_topic=auto_topic, | 163 | auto_topic=auto_topic, |
| 158 | draft=draft) | 164 | draft=draft, |
| 165 | dest_branch=dest_branch) | ||
| 159 | 166 | ||
| 160 | def GetPublishedRefs(self): | 167 | def GetPublishedRefs(self): |
| 161 | refs = {} | 168 | refs = {} |
| @@ -361,7 +368,7 @@ class RepoHook(object): | |||
| 361 | 'Do you want to allow this script to run ' | 368 | 'Do you want to allow this script to run ' |
| 362 | '(yes/yes-never-ask-again/NO)? ') % ( | 369 | '(yes/yes-never-ask-again/NO)? ') % ( |
| 363 | self._GetMustVerb(), self._script_fullpath) | 370 | self._GetMustVerb(), self._script_fullpath) |
| 364 | response = raw_input(prompt).lower() | 371 | response = input(prompt).lower() |
| 365 | print() | 372 | print() |
| 366 | 373 | ||
| 367 | # User is doing a one-time approval. | 374 | # User is doing a one-time approval. |
| @@ -488,9 +495,11 @@ class Project(object): | |||
| 488 | groups = None, | 495 | groups = None, |
| 489 | sync_c = False, | 496 | sync_c = False, |
| 490 | sync_s = False, | 497 | sync_s = False, |
| 498 | clone_depth = None, | ||
| 491 | upstream = None, | 499 | upstream = None, |
| 492 | parent = None, | 500 | parent = None, |
| 493 | is_derived = False): | 501 | is_derived = False, |
| 502 | dest_branch = None): | ||
| 494 | """Init a Project object. | 503 | """Init a Project object. |
| 495 | 504 | ||
| 496 | Args: | 505 | Args: |
| @@ -510,6 +519,7 @@ class Project(object): | |||
| 510 | parent: The parent Project object. | 519 | parent: The parent Project object. |
| 511 | is_derived: False if the project was explicitly defined in the manifest; | 520 | is_derived: False if the project was explicitly defined in the manifest; |
| 512 | True if the project is a discovered submodule. | 521 | True if the project is a discovered submodule. |
| 522 | dest_branch: The branch to which to push changes for review by default. | ||
| 513 | """ | 523 | """ |
| 514 | self.manifest = manifest | 524 | self.manifest = manifest |
| 515 | self.name = name | 525 | self.name = name |
| @@ -533,6 +543,7 @@ class Project(object): | |||
| 533 | self.groups = groups | 543 | self.groups = groups |
| 534 | self.sync_c = sync_c | 544 | self.sync_c = sync_c |
| 535 | self.sync_s = sync_s | 545 | self.sync_s = sync_s |
| 546 | self.clone_depth = clone_depth | ||
| 536 | self.upstream = upstream | 547 | self.upstream = upstream |
| 537 | self.parent = parent | 548 | self.parent = parent |
| 538 | self.is_derived = is_derived | 549 | self.is_derived = is_derived |
| @@ -551,6 +562,7 @@ class Project(object): | |||
| 551 | self.work_git = None | 562 | self.work_git = None |
| 552 | self.bare_git = self._GitGetByExec(self, bare=True) | 563 | self.bare_git = self._GitGetByExec(self, bare=True) |
| 553 | self.bare_ref = GitRefs(gitdir) | 564 | self.bare_ref = GitRefs(gitdir) |
| 565 | self.dest_branch = dest_branch | ||
| 554 | 566 | ||
| 555 | # This will be filled in if a project is later identified to be the | 567 | # This will be filled in if a project is later identified to be the |
| 556 | # project containing repo hooks. | 568 | # project containing repo hooks. |
| @@ -644,7 +656,7 @@ class Project(object): | |||
| 644 | all_refs = self._allrefs | 656 | all_refs = self._allrefs |
| 645 | heads = {} | 657 | heads = {} |
| 646 | 658 | ||
| 647 | for name, ref_id in all_refs.iteritems(): | 659 | for name, ref_id in all_refs.items(): |
| 648 | if name.startswith(R_HEADS): | 660 | if name.startswith(R_HEADS): |
| 649 | name = name[len(R_HEADS):] | 661 | name = name[len(R_HEADS):] |
| 650 | b = self.GetBranch(name) | 662 | b = self.GetBranch(name) |
| @@ -653,7 +665,7 @@ class Project(object): | |||
| 653 | b.revision = ref_id | 665 | b.revision = ref_id |
| 654 | heads[name] = b | 666 | heads[name] = b |
| 655 | 667 | ||
| 656 | for name, ref_id in all_refs.iteritems(): | 668 | for name, ref_id in all_refs.items(): |
| 657 | if name.startswith(R_PUB): | 669 | if name.startswith(R_PUB): |
| 658 | name = name[len(R_PUB):] | 670 | name = name[len(R_PUB):] |
| 659 | b = heads.get(name) | 671 | b = heads.get(name) |
| @@ -672,9 +684,14 @@ class Project(object): | |||
| 672 | project_groups: "all,group1,group2" | 684 | project_groups: "all,group1,group2" |
| 673 | manifest_groups: "-group1,group2" | 685 | manifest_groups: "-group1,group2" |
| 674 | the project will be matched. | 686 | the project will be matched. |
| 687 | |||
| 688 | The special manifest group "default" will match any project that | ||
| 689 | does not have the special project group "notdefault" | ||
| 675 | """ | 690 | """ |
| 676 | expanded_manifest_groups = manifest_groups or ['all', '-notdefault'] | 691 | expanded_manifest_groups = manifest_groups or ['default'] |
| 677 | expanded_project_groups = ['all'] + (self.groups or []) | 692 | expanded_project_groups = ['all'] + (self.groups or []) |
| 693 | if not 'notdefault' in expanded_project_groups: | ||
| 694 | expanded_project_groups += ['default'] | ||
| 678 | 695 | ||
| 679 | matched = False | 696 | matched = False |
| 680 | for group in expanded_manifest_groups: | 697 | for group in expanded_manifest_groups: |
| @@ -754,10 +771,7 @@ class Project(object): | |||
| 754 | paths.extend(df.keys()) | 771 | paths.extend(df.keys()) |
| 755 | paths.extend(do) | 772 | paths.extend(do) |
| 756 | 773 | ||
| 757 | paths = list(set(paths)) | 774 | for p in sorted(set(paths)): |
| 758 | paths.sort() | ||
| 759 | |||
| 760 | for p in paths: | ||
| 761 | try: | 775 | try: |
| 762 | i = di[p] | 776 | i = di[p] |
| 763 | except KeyError: | 777 | except KeyError: |
| @@ -849,13 +863,13 @@ class Project(object): | |||
| 849 | all_refs = self._allrefs | 863 | all_refs = self._allrefs |
| 850 | heads = set() | 864 | heads = set() |
| 851 | canrm = {} | 865 | canrm = {} |
| 852 | for name, ref_id in all_refs.iteritems(): | 866 | for name, ref_id in all_refs.items(): |
| 853 | if name.startswith(R_HEADS): | 867 | if name.startswith(R_HEADS): |
| 854 | heads.add(name) | 868 | heads.add(name) |
| 855 | elif name.startswith(R_PUB): | 869 | elif name.startswith(R_PUB): |
| 856 | canrm[name] = ref_id | 870 | canrm[name] = ref_id |
| 857 | 871 | ||
| 858 | for name, ref_id in canrm.iteritems(): | 872 | for name, ref_id in canrm.items(): |
| 859 | n = name[len(R_PUB):] | 873 | n = name[len(R_PUB):] |
| 860 | if R_HEADS + n not in heads: | 874 | if R_HEADS + n not in heads: |
| 861 | self.bare_git.DeleteRef(name, ref_id) | 875 | self.bare_git.DeleteRef(name, ref_id) |
| @@ -866,14 +880,14 @@ class Project(object): | |||
| 866 | heads = {} | 880 | heads = {} |
| 867 | pubed = {} | 881 | pubed = {} |
| 868 | 882 | ||
| 869 | for name, ref_id in self._allrefs.iteritems(): | 883 | for name, ref_id in self._allrefs.items(): |
| 870 | if name.startswith(R_HEADS): | 884 | if name.startswith(R_HEADS): |
| 871 | heads[name[len(R_HEADS):]] = ref_id | 885 | heads[name[len(R_HEADS):]] = ref_id |
| 872 | elif name.startswith(R_PUB): | 886 | elif name.startswith(R_PUB): |
| 873 | pubed[name[len(R_PUB):]] = ref_id | 887 | pubed[name[len(R_PUB):]] = ref_id |
| 874 | 888 | ||
| 875 | ready = [] | 889 | ready = [] |
| 876 | for branch, ref_id in heads.iteritems(): | 890 | for branch, ref_id in heads.items(): |
| 877 | if branch in pubed and pubed[branch] == ref_id: | 891 | if branch in pubed and pubed[branch] == ref_id: |
| 878 | continue | 892 | continue |
| 879 | if selected_branch and branch != selected_branch: | 893 | if selected_branch and branch != selected_branch: |
| @@ -898,7 +912,8 @@ class Project(object): | |||
| 898 | def UploadForReview(self, branch=None, | 912 | def UploadForReview(self, branch=None, |
| 899 | people=([],[]), | 913 | people=([],[]), |
| 900 | auto_topic=False, | 914 | auto_topic=False, |
| 901 | draft=False): | 915 | draft=False, |
| 916 | dest_branch=None): | ||
| 902 | """Uploads the named branch for code review. | 917 | """Uploads the named branch for code review. |
| 903 | """ | 918 | """ |
| 904 | if branch is None: | 919 | if branch is None: |
| @@ -912,7 +927,10 @@ class Project(object): | |||
| 912 | if not branch.remote.review: | 927 | if not branch.remote.review: |
| 913 | raise GitError('remote %s has no review url' % branch.remote.name) | 928 | raise GitError('remote %s has no review url' % branch.remote.name) |
| 914 | 929 | ||
| 915 | dest_branch = branch.merge | 930 | if dest_branch is None: |
| 931 | dest_branch = self.dest_branch | ||
| 932 | if dest_branch is None: | ||
| 933 | dest_branch = branch.merge | ||
| 916 | if not dest_branch.startswith(R_HEADS): | 934 | if not dest_branch.startswith(R_HEADS): |
| 917 | dest_branch = R_HEADS + dest_branch | 935 | dest_branch = R_HEADS + dest_branch |
| 918 | 936 | ||
| @@ -977,6 +995,8 @@ class Project(object): | |||
| 977 | is_new = not self.Exists | 995 | is_new = not self.Exists |
| 978 | if is_new: | 996 | if is_new: |
| 979 | self._InitGitDir() | 997 | self._InitGitDir() |
| 998 | else: | ||
| 999 | self._UpdateHooks() | ||
| 980 | self._InitRemote() | 1000 | self._InitRemote() |
| 981 | 1001 | ||
| 982 | if is_new: | 1002 | if is_new: |
| @@ -1216,7 +1236,6 @@ class Project(object): | |||
| 1216 | cmd = ['fetch', remote.name] | 1236 | cmd = ['fetch', remote.name] |
| 1217 | cmd.append('refs/changes/%2.2d/%d/%d' \ | 1237 | cmd.append('refs/changes/%2.2d/%d/%d' \ |
| 1218 | % (change_id % 100, change_id, patch_id)) | 1238 | % (change_id % 100, change_id, patch_id)) |
| 1219 | cmd.extend(map(str, remote.fetch)) | ||
| 1220 | if GitCommand(self, cmd, bare=True).Wait() != 0: | 1239 | if GitCommand(self, cmd, bare=True).Wait() != 0: |
| 1221 | return None | 1240 | return None |
| 1222 | return DownloadedChange(self, | 1241 | return DownloadedChange(self, |
| @@ -1605,7 +1624,7 @@ class Project(object): | |||
| 1605 | ids = set(all_refs.values()) | 1624 | ids = set(all_refs.values()) |
| 1606 | tmp = set() | 1625 | tmp = set() |
| 1607 | 1626 | ||
| 1608 | for r, ref_id in GitRefs(ref_dir).all.iteritems(): | 1627 | for r, ref_id in GitRefs(ref_dir).all.items(): |
| 1609 | if r not in all_refs: | 1628 | if r not in all_refs: |
| 1610 | if r.startswith(R_TAGS) or remote.WritesTo(r): | 1629 | if r.startswith(R_TAGS) or remote.WritesTo(r): |
| 1611 | all_refs[r] = ref_id | 1630 | all_refs[r] = ref_id |
| @@ -1620,13 +1639,10 @@ class Project(object): | |||
| 1620 | ids.add(ref_id) | 1639 | ids.add(ref_id) |
| 1621 | tmp.add(r) | 1640 | tmp.add(r) |
| 1622 | 1641 | ||
| 1623 | ref_names = list(all_refs.keys()) | ||
| 1624 | ref_names.sort() | ||
| 1625 | |||
| 1626 | tmp_packed = '' | 1642 | tmp_packed = '' |
| 1627 | old_packed = '' | 1643 | old_packed = '' |
| 1628 | 1644 | ||
| 1629 | for r in ref_names: | 1645 | for r in sorted(all_refs): |
| 1630 | line = '%s %s\n' % (all_refs[r], r) | 1646 | line = '%s %s\n' % (all_refs[r], r) |
| 1631 | tmp_packed += line | 1647 | tmp_packed += line |
| 1632 | if r not in tmp: | 1648 | if r not in tmp: |
| @@ -1640,7 +1656,10 @@ class Project(object): | |||
| 1640 | 1656 | ||
| 1641 | # The --depth option only affects the initial fetch; after that we'll do | 1657 | # The --depth option only affects the initial fetch; after that we'll do |
| 1642 | # full fetches of changes. | 1658 | # full fetches of changes. |
| 1643 | depth = self.manifest.manifestProject.config.GetString('repo.depth') | 1659 | if self.clone_depth: |
| 1660 | depth = self.clone_depth | ||
| 1661 | else: | ||
| 1662 | depth = self.manifest.manifestProject.config.GetString('repo.depth') | ||
| 1644 | if depth and initial: | 1663 | if depth and initial: |
| 1645 | cmd.append('--depth=%s' % depth) | 1664 | cmd.append('--depth=%s' % depth) |
| 1646 | 1665 | ||
| @@ -1652,11 +1671,13 @@ class Project(object): | |||
| 1652 | 1671 | ||
| 1653 | if not current_branch_only: | 1672 | if not current_branch_only: |
| 1654 | # Fetch whole repo | 1673 | # Fetch whole repo |
| 1655 | if no_tags: | 1674 | # If using depth then we should not get all the tags since they may |
| 1675 | # be outside of the depth. | ||
| 1676 | if no_tags or depth: | ||
| 1656 | cmd.append('--no-tags') | 1677 | cmd.append('--no-tags') |
| 1657 | else: | 1678 | else: |
| 1658 | cmd.append('--tags') | 1679 | cmd.append('--tags') |
| 1659 | cmd.append((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')) | 1680 | cmd.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*'))) |
| 1660 | elif tag_name is not None: | 1681 | elif tag_name is not None: |
| 1661 | cmd.append('tag') | 1682 | cmd.append('tag') |
| 1662 | cmd.append(tag_name) | 1683 | cmd.append(tag_name) |
| @@ -1666,7 +1687,7 @@ class Project(object): | |||
| 1666 | branch = self.upstream | 1687 | branch = self.upstream |
| 1667 | if branch.startswith(R_HEADS): | 1688 | if branch.startswith(R_HEADS): |
| 1668 | branch = branch[len(R_HEADS):] | 1689 | branch = branch[len(R_HEADS):] |
| 1669 | cmd.append((u'+refs/heads/%s:' % branch) + remote.ToLocal('refs/heads/%s' % branch)) | 1690 | cmd.append(str((u'+refs/heads/%s:' % branch) + remote.ToLocal('refs/heads/%s' % branch))) |
| 1670 | 1691 | ||
| 1671 | ok = False | 1692 | ok = False |
| 1672 | for _i in range(2): | 1693 | for _i in range(2): |
| @@ -1700,15 +1721,14 @@ class Project(object): | |||
| 1700 | return ok | 1721 | return ok |
| 1701 | 1722 | ||
| 1702 | def _ApplyCloneBundle(self, initial=False, quiet=False): | 1723 | def _ApplyCloneBundle(self, initial=False, quiet=False): |
| 1703 | if initial and self.manifest.manifestProject.config.GetString('repo.depth'): | 1724 | if initial and (self.manifest.manifestProject.config.GetString('repo.depth') or self.clone_depth): |
| 1704 | return False | 1725 | return False |
| 1705 | 1726 | ||
| 1706 | remote = self.GetRemote(self.remote.name) | 1727 | remote = self.GetRemote(self.remote.name) |
| 1707 | bundle_url = remote.url + '/clone.bundle' | 1728 | bundle_url = remote.url + '/clone.bundle' |
| 1708 | bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url) | 1729 | bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url) |
| 1709 | if GetSchemeFromUrl(bundle_url) in ('persistent-http', 'persistent-https'): | 1730 | if GetSchemeFromUrl(bundle_url) not in ( |
| 1710 | bundle_url = bundle_url[len('persistent-'):] | 1731 | 'http', 'https', 'persistent-http', 'persistent-https'): |
| 1711 | if GetSchemeFromUrl(bundle_url) not in ('http', 'https'): | ||
| 1712 | return False | 1732 | return False |
| 1713 | 1733 | ||
| 1714 | bundle_dst = os.path.join(self.gitdir, 'clone.bundle') | 1734 | bundle_dst = os.path.join(self.gitdir, 'clone.bundle') |
| @@ -1757,9 +1777,11 @@ class Project(object): | |||
| 1757 | os.remove(tmpPath) | 1777 | os.remove(tmpPath) |
| 1758 | if 'http_proxy' in os.environ and 'darwin' == sys.platform: | 1778 | if 'http_proxy' in os.environ and 'darwin' == sys.platform: |
| 1759 | cmd += ['--proxy', os.environ['http_proxy']] | 1779 | cmd += ['--proxy', os.environ['http_proxy']] |
| 1760 | cookiefile = GitConfig.ForUser().GetString('http.cookiefile') | 1780 | cookiefile = self._GetBundleCookieFile(srcUrl) |
| 1761 | if cookiefile: | 1781 | if cookiefile: |
| 1762 | cmd += ['--cookie', cookiefile] | 1782 | cmd += ['--cookie', cookiefile] |
| 1783 | if srcUrl.startswith('persistent-'): | ||
| 1784 | srcUrl = srcUrl[len('persistent-'):] | ||
| 1763 | cmd += [srcUrl] | 1785 | cmd += [srcUrl] |
| 1764 | 1786 | ||
| 1765 | if IsTrace(): | 1787 | if IsTrace(): |
| @@ -1782,7 +1804,7 @@ class Project(object): | |||
| 1782 | return False | 1804 | return False |
| 1783 | 1805 | ||
| 1784 | if os.path.exists(tmpPath): | 1806 | if os.path.exists(tmpPath): |
| 1785 | if curlret == 0 and os.stat(tmpPath).st_size > 16: | 1807 | if curlret == 0 and self._IsValidBundle(tmpPath): |
| 1786 | os.rename(tmpPath, dstPath) | 1808 | os.rename(tmpPath, dstPath) |
| 1787 | return True | 1809 | return True |
| 1788 | else: | 1810 | else: |
| @@ -1791,6 +1813,46 @@ class Project(object): | |||
| 1791 | else: | 1813 | else: |
| 1792 | return False | 1814 | return False |
| 1793 | 1815 | ||
| 1816 | def _IsValidBundle(self, path): | ||
| 1817 | try: | ||
| 1818 | with open(path) as f: | ||
| 1819 | if f.read(16) == '# v2 git bundle\n': | ||
| 1820 | return True | ||
| 1821 | else: | ||
| 1822 | print("Invalid clone.bundle file; ignoring.", file=sys.stderr) | ||
| 1823 | return False | ||
| 1824 | except OSError: | ||
| 1825 | return False | ||
| 1826 | |||
| 1827 | def _GetBundleCookieFile(self, url): | ||
| 1828 | if url.startswith('persistent-'): | ||
| 1829 | try: | ||
| 1830 | p = subprocess.Popen( | ||
| 1831 | ['git-remote-persistent-https', '-print_config', url], | ||
| 1832 | stdin=subprocess.PIPE, stdout=subprocess.PIPE, | ||
| 1833 | stderr=subprocess.PIPE) | ||
| 1834 | p.stdin.close() # Tell subprocess it's ok to close. | ||
| 1835 | prefix = 'http.cookiefile=' | ||
| 1836 | cookiefile = None | ||
| 1837 | for line in p.stdout: | ||
| 1838 | line = line.strip() | ||
| 1839 | if line.startswith(prefix): | ||
| 1840 | cookiefile = line[len(prefix):] | ||
| 1841 | break | ||
| 1842 | if p.wait(): | ||
| 1843 | line = iter(p.stderr).next() | ||
| 1844 | if ' -print_config' in line: | ||
| 1845 | pass # Persistent proxy doesn't support -print_config. | ||
| 1846 | else: | ||
| 1847 | print(line + p.stderr.read(), file=sys.stderr) | ||
| 1848 | if cookiefile: | ||
| 1849 | return cookiefile | ||
| 1850 | except OSError as e: | ||
| 1851 | if e.errno == errno.ENOENT: | ||
| 1852 | pass # No persistent proxy. | ||
| 1853 | raise | ||
| 1854 | return GitConfig.ForUser().GetString('http.cookiefile') | ||
| 1855 | |||
| 1794 | def _Checkout(self, rev, quiet=False): | 1856 | def _Checkout(self, rev, quiet=False): |
| 1795 | cmd = ['checkout'] | 1857 | cmd = ['checkout'] |
| 1796 | if quiet: | 1858 | if quiet: |
| @@ -1841,16 +1903,17 @@ class Project(object): | |||
| 1841 | if GitCommand(self, cmd).Wait() != 0: | 1903 | if GitCommand(self, cmd).Wait() != 0: |
| 1842 | raise GitError('%s merge %s ' % (self.name, head)) | 1904 | raise GitError('%s merge %s ' % (self.name, head)) |
| 1843 | 1905 | ||
| 1844 | def _InitGitDir(self): | 1906 | def _InitGitDir(self, mirror_git=None): |
| 1845 | if not os.path.exists(self.gitdir): | 1907 | if not os.path.exists(self.gitdir): |
| 1846 | os.makedirs(self.gitdir) | 1908 | os.makedirs(self.gitdir) |
| 1847 | self.bare_git.init() | 1909 | self.bare_git.init() |
| 1848 | 1910 | ||
| 1849 | mp = self.manifest.manifestProject | 1911 | mp = self.manifest.manifestProject |
| 1850 | ref_dir = mp.config.GetString('repo.reference') | 1912 | ref_dir = mp.config.GetString('repo.reference') or '' |
| 1851 | 1913 | ||
| 1852 | if ref_dir: | 1914 | if ref_dir or mirror_git: |
| 1853 | mirror_git = os.path.join(ref_dir, self.name + '.git') | 1915 | if not mirror_git: |
| 1916 | mirror_git = os.path.join(ref_dir, self.name + '.git') | ||
| 1854 | repo_git = os.path.join(ref_dir, '.repo', 'projects', | 1917 | repo_git = os.path.join(ref_dir, '.repo', 'projects', |
| 1855 | self.relpath + '.git') | 1918 | self.relpath + '.git') |
| 1856 | 1919 | ||
| @@ -1867,11 +1930,21 @@ class Project(object): | |||
| 1867 | _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'), | 1930 | _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'), |
| 1868 | os.path.join(ref_dir, 'objects') + '\n') | 1931 | os.path.join(ref_dir, 'objects') + '\n') |
| 1869 | 1932 | ||
| 1933 | self._UpdateHooks() | ||
| 1934 | |||
| 1935 | m = self.manifest.manifestProject.config | ||
| 1936 | for key in ['user.name', 'user.email']: | ||
| 1937 | if m.Has(key, include_defaults = False): | ||
| 1938 | self.config.SetString(key, m.GetString(key)) | ||
| 1870 | if self.manifest.IsMirror: | 1939 | if self.manifest.IsMirror: |
| 1871 | self.config.SetString('core.bare', 'true') | 1940 | self.config.SetString('core.bare', 'true') |
| 1872 | else: | 1941 | else: |
| 1873 | self.config.SetString('core.bare', None) | 1942 | self.config.SetString('core.bare', None) |
| 1874 | 1943 | ||
| 1944 | def _UpdateHooks(self): | ||
| 1945 | if os.path.exists(self.gitdir): | ||
| 1946 | # Always recreate hooks since they can have been changed | ||
| 1947 | # since the latest update. | ||
| 1875 | hooks = self._gitdir_path('hooks') | 1948 | hooks = self._gitdir_path('hooks') |
| 1876 | try: | 1949 | try: |
| 1877 | to_rm = os.listdir(hooks) | 1950 | to_rm = os.listdir(hooks) |
| @@ -1881,11 +1954,6 @@ class Project(object): | |||
| 1881 | os.remove(os.path.join(hooks, old_hook)) | 1954 | os.remove(os.path.join(hooks, old_hook)) |
| 1882 | self._InitHooks() | 1955 | self._InitHooks() |
| 1883 | 1956 | ||
| 1884 | m = self.manifest.manifestProject.config | ||
| 1885 | for key in ['user.name', 'user.email']: | ||
| 1886 | if m.Has(key, include_defaults = False): | ||
| 1887 | self.config.SetString(key, m.GetString(key)) | ||
| 1888 | |||
| 1889 | def _InitHooks(self): | 1957 | def _InitHooks(self): |
| 1890 | hooks = self._gitdir_path('hooks') | 1958 | hooks = self._gitdir_path('hooks') |
| 1891 | if not os.path.exists(hooks): | 1959 | if not os.path.exists(hooks): |
| @@ -2092,6 +2160,10 @@ class Project(object): | |||
| 2092 | line = fd.read() | 2160 | line = fd.read() |
| 2093 | finally: | 2161 | finally: |
| 2094 | fd.close() | 2162 | fd.close() |
| 2163 | try: | ||
| 2164 | line = line.decode() | ||
| 2165 | except AttributeError: | ||
| 2166 | pass | ||
| 2095 | if line.startswith('ref: '): | 2167 | if line.startswith('ref: '): |
| 2096 | return line[5:-1] | 2168 | return line[5:-1] |
| 2097 | return line[:-1] | 2169 | return line[:-1] |
| @@ -2185,7 +2257,7 @@ class Project(object): | |||
| 2185 | if not git_require((1, 7, 2)): | 2257 | if not git_require((1, 7, 2)): |
| 2186 | raise ValueError('cannot set config on command line for %s()' | 2258 | raise ValueError('cannot set config on command line for %s()' |
| 2187 | % name) | 2259 | % name) |
| 2188 | for k, v in config.iteritems(): | 2260 | for k, v in config.items(): |
| 2189 | cmdv.append('-c') | 2261 | cmdv.append('-c') |
| 2190 | cmdv.append('%s=%s' % (k, v)) | 2262 | cmdv.append('%s=%s' % (k, v)) |
| 2191 | cmdv.append(name) | 2263 | cmdv.append(name) |
| @@ -2201,6 +2273,10 @@ class Project(object): | |||
| 2201 | name, | 2273 | name, |
| 2202 | p.stderr)) | 2274 | p.stderr)) |
| 2203 | r = p.stdout | 2275 | r = p.stdout |
| 2276 | try: | ||
| 2277 | r = r.decode('utf-8') | ||
| 2278 | except AttributeError: | ||
| 2279 | pass | ||
| 2204 | if r.endswith('\n') and r.index('\n') == len(r) - 1: | 2280 | if r.endswith('\n') and r.index('\n') == len(r) - 1: |
| 2205 | return r[:-1] | 2281 | return r[:-1] |
| 2206 | return r | 2282 | return r |
diff --git a/pyversion.py b/pyversion.py new file mode 100644 index 00000000..5b348d91 --- /dev/null +++ b/pyversion.py | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # | ||
| 2 | # Copyright (C) 2013 The Android Open Source Project | ||
| 3 | # | ||
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | # you may not use this file except in compliance with the License. | ||
| 6 | # You may obtain a copy of the License at | ||
| 7 | # | ||
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | # | ||
| 10 | # Unless required by applicable law or agreed to in writing, software | ||
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | # See the License for the specific language governing permissions and | ||
| 14 | # limitations under the License. | ||
| 15 | |||
| 16 | import sys | ||
| 17 | |||
| 18 | def is_python3(): | ||
| 19 | return sys.version_info[0] == 3 | ||
| @@ -2,7 +2,6 @@ | |||
| 2 | 2 | ||
| 3 | ## repo default configuration | 3 | ## repo default configuration |
| 4 | ## | 4 | ## |
| 5 | from __future__ import print_function | ||
| 6 | REPO_URL = 'https://gerrit.googlesource.com/git-repo' | 5 | REPO_URL = 'https://gerrit.googlesource.com/git-repo' |
| 7 | REPO_REV = 'stable' | 6 | REPO_REV = 'stable' |
| 8 | 7 | ||
| @@ -21,10 +20,10 @@ REPO_REV = 'stable' | |||
| 21 | # limitations under the License. | 20 | # limitations under the License. |
| 22 | 21 | ||
| 23 | # increment this whenever we make important changes to this script | 22 | # increment this whenever we make important changes to this script |
| 24 | VERSION = (1, 19) | 23 | VERSION = (1, 20) |
| 25 | 24 | ||
| 26 | # increment this if the MAINTAINER_KEYS block is modified | 25 | # increment this if the MAINTAINER_KEYS block is modified |
| 27 | KEYRING_VERSION = (1, 1) | 26 | KEYRING_VERSION = (1, 2) |
| 28 | MAINTAINER_KEYS = """ | 27 | MAINTAINER_KEYS = """ |
| 29 | 28 | ||
| 30 | Repo Maintainer <repo@android.kernel.org> | 29 | Repo Maintainer <repo@android.kernel.org> |
| @@ -73,32 +72,32 @@ TACbBS+Up3RpfYVfd63c1cDdlru13pQAn3NQy/SN858MkxN+zym86UBgOad2 | |||
| 73 | -----BEGIN PGP PUBLIC KEY BLOCK----- | 72 | -----BEGIN PGP PUBLIC KEY BLOCK----- |
| 74 | Version: GnuPG v1.4.11 (GNU/Linux) | 73 | Version: GnuPG v1.4.11 (GNU/Linux) |
| 75 | 74 | ||
| 76 | mQENBFBiLPwBCACvISTASOgFXwADw2GYRH2I2z9RvYkYoZ6ThTTNlMXbbYYKO2Wo | 75 | mQENBFHRvc8BCADFg45Xx/y6QDC+T7Y/gGc7vx0ww7qfOwIKlAZ9xG3qKunMxo+S |
| 77 | a9LQDNW0TbCEekg5UKk0FD13XOdWaqUt4Gtuvq9c43GRSjMO6NXH+0BjcQ8vUtY2 | 76 | hPCnzEl3cq+6I1Ww/ndop/HB3N3toPXRCoN8Vs4/Hc7by+SnaLFnacrm+tV5/OgT |
| 78 | /W4CYUevwdo4nQ1+1zsOCu1XYe/CReXq0fdugv3hgmRmh3sz1soo37Q44W2frxxg | 77 | V37Lzt8lhay1Kl+YfpFwHYYpIEBLFV9knyfRXS/428W2qhdzYfvB15/AasRmwmor |
| 79 | U7Rz3Da4FjgAL0RQ8qndD+LwRHXTY7H7wYM8V/3cYFZV7pSodd75q3MAXYQLf0ZV | 78 | py4NIzSs8UD/SPr1ihqNCdZM76+MQyN5HMYXW/ALZXUFG0pwluHFA7hrfPG74i8C |
| 80 | QR1XATu5l1QnXrxgHvz7MmDwb1D+jX3YPKnZveaukigQ6hDHdiVcePBiGXmk8LZC | 79 | zMiP7qvMWIl/r/jtzHioH1dRKgbod+LZsrDJ8mBaqsZaDmNJMhss9g76XvfMyLra |
| 81 | 2jQkdXeF7Su1ZYpr2nnEHLJ6vOLcCpPGb8gDABEBAAG0H0NvbmxleSBPd2VucyA8 | 80 | 9DI9/iFuBpGzeqBv0hwOGQspLRrEoyTeR6n1ABEBAAG0H0NvbmxleSBPd2VucyA8 |
| 82 | Y2NvM0BhbmRyb2lkLmNvbT6JATgEEwECACIFAlBiLPwCGwMGCwkIBwMCBhUIAgkK | 81 | Y2NvM0BhbmRyb2lkLmNvbT6JATgEEwECACIFAlHRvc8CGwMGCwkIBwMCBhUIAgkK |
| 83 | CwQWAgMBAh4BAheAAAoJEBkmlFUziHGkHVkH/2Hks2Cif5i2xPtv2IFZcjL42joU | 82 | CwQWAgMBAh4BAheAAAoJEGe35EhpKzgsP6AIAJKJmNtn4l7hkYHKHFSo3egb6RjQ |
| 84 | T7lO5XFqUYS9ZNHpGa/V0eiPt7rHoO16glR83NZtwlrq2cSN89i9HfOhMYV/qLu8 | 83 | zEIP3MFTcu8HFX1kF1ZFbrp7xqurLaE53kEkKuAAvjJDAgI8mcZHP1JyplubqjQA |
| 85 | fLCHcV2muw+yCB5s5bxnI5UkToiNZyBNqFkcOt/Kbj9Hpy68A1kmc6myVEaUYebq | 84 | xvv84gK+OGP3Xk+QK1ZjUQSbjOpjEiSZpRhWcHci3dgOUH4blJfByHw25hlgHowd |
| 86 | 2Chx/f3xuEthan099t746v1K+/6SvQGDNctHuaMr9cWdxZtHjdRf31SQRc99Phe5 | 85 | a/2PrNKZVcJ92YienaxxGjcXEUcd0uYEG2+rwllQigFcnMFDhr9B71MfalRHjFKE |
| 87 | w+ZGR/ebxNDKRK9mKgZT8wVFHlXerJsRqWIqtx1fsW1UgLgbpcpe2MChm6B5wTu0 | 86 | fmdoypqLrri61YBc59P88Rw2/WUpTQjgNubSqa3A2+CKdaRyaRw+2fdF4TdR0h8W |
| 88 | s1ltzox3l4q71FyRRPUJxXyvGkDLZWpK7EpiHSCOYq/KP3HkKeXU3xqHpcG5AQ0E | 87 | zbg+lbaPtJHsV+3mJC7fq26MiJDRJa5ZztpMn8su20gbLgi2ShBOaHAYDDi5AQ0E |
| 89 | UGIs/AEIAKzO/7lO9cB6dshmZYo8Vy/b7aGicThE+ChcDSfhvyOXVdEM2GKAjsR+ | 88 | UdG9zwEIAMoOBq+QLNozAhxOOl5GL3StTStGRgPRXINfmViTsihrqGCWBBUfXlUE |
| 90 | rlBWbTFX3It301p2HwZPFEi9nEvJxVlqqBiW0bPmNMkDRR55l2vbWg35wwkg6RyE | 89 | OytC0mYcrDUQev/8ToVoyqw+iGSwDkcSXkrEUCKFtHV/GECWtk1keyHgR10YKI1R |
| 91 | Bc5/TQjhXI2w8IvlimoGoUff4t3JmMOnWrnKSvL+5iuRj12p9WmanCHzw3Ee7ztf | 90 | mquSXoubWGqPeG1PAI74XWaRx8UrL8uCXUtmD8Q5J7mDjKR5NpxaXrwlA0bKsf2E |
| 92 | /aU/q+FTpr3DLerb6S8xbv86ySgnJT6o5CyL2DCWRtnYQyGVi0ZmLzEouAYiO0hs | 91 | Gp9tu1kKauuToZhWHMRMqYSOGikQJwWSFYKT1KdNcOXLQF6+bfoJ6sjVYdwfmNQL |
| 93 | z0AAu28Mj+12g2WwePRz6gfM9rHtI37ylYW3oT/9M9mO9ei/Bc/1D7Dz6qNV+0vg | 92 | Ixn8QVhoTDedcqClSWB17VDEFDFa7MmqXZz2qtM3X1R/MUMHqPtegQzBGNhRdnI2 |
| 94 | uSVJxM2Bl6GalHPZLhHntFEdIA6EdoUAEQEAAYkBHwQYAQIACQUCUGIs/AIbDAAK | 93 | V45+1Nnx/uuCxDbeI4RbHzujnxDiq70AEQEAAYkBHwQYAQIACQUCUdG9zwIbDAAK |
| 95 | CRAZJpRVM4hxpNfkB/0W/hP5WK/NETXBlWXXW7JPaWO2c5kGwD0lnj5RRmridyo1 | 94 | CRBnt+RIaSs4LNVeB/0Y2pZ8I7gAAcEM0Xw8drr4omg2fUoK1J33ozlA/RxeA/lJ |
| 96 | vbm5PdM91jOsDQYqRu6YOoYBnDnEhB2wL2bPh34HWwwrA+LwB8hlcAV2z1bdwyfl | 95 | I3KnyCDTpXuIeBKPGkdL8uMATC9Z8DnBBajRlftNDVZS3Hz4G09G9QpMojvJkFJV |
| 97 | 3R823fReKN3QcvLHzmvZPrF4Rk97M9UIyKS0RtnfTWykRgDWHIsrtQPoNwsXrWoT | 96 | By+01Flw/X+eeN8NpqSuLV4W+AjEO8at/VvgKr1AFvBRdZ7GkpI1o6DgPe7ZqX+1 |
| 98 | 9LrM2v+1+9mp3vuXnE473/NHxmiWEQH9Ez+O/mOxQ7rSOlqGRiKq/IBZCfioJOtV | 97 | dzQZt3e13W0rVBb/bUgx9iSLoeWP3aq/k+/GRGOR+S6F6BBSl0SQ2EF2+dIywb1x |
| 99 | fTQeIu/yASZnsLBqr6SJEGwYBoWcyjG++k4fyw8ocOAo4uGDYbxgN7yYfNQ0OH7o | 98 | JuinEP+AwLAUZ1Bsx9ISC0Agpk2VeHXPL3FGhroEmoMvBzO0kTFGyoeT7PR/BfKv |
| 100 | V6pfUgqKLWa/aK7/N1ZHnPdFLD8Xt0Dmy4BPwrKC | 99 | +H/g3HsL2LOB9uoIm8/5p2TTU5ttYCXMHhQZ81AY |
| 101 | =O7am | 100 | =AUp4 |
| 102 | -----END PGP PUBLIC KEY BLOCK----- | 101 | -----END PGP PUBLIC KEY BLOCK----- |
| 103 | """ | 102 | """ |
| 104 | 103 | ||
| @@ -108,6 +107,7 @@ repodir = '.repo' # name of repo's private directory | |||
| 108 | S_repo = 'repo' # special repo repository | 107 | S_repo = 'repo' # special repo repository |
| 109 | S_manifests = 'manifests' # special manifest repository | 108 | S_manifests = 'manifests' # special manifest repository |
| 110 | REPO_MAIN = S_repo + '/main.py' # main script | 109 | REPO_MAIN = S_repo + '/main.py' # main script |
| 110 | MIN_PYTHON_VERSION = (2, 6) # minimum supported python version | ||
| 111 | 111 | ||
| 112 | 112 | ||
| 113 | import optparse | 113 | import optparse |
| @@ -116,19 +116,38 @@ import re | |||
| 116 | import stat | 116 | import stat |
| 117 | import subprocess | 117 | import subprocess |
| 118 | import sys | 118 | import sys |
| 119 | try: | 119 | |
| 120 | import urllib2 | 120 | if sys.version_info[0] == 3: |
| 121 | except ImportError: | ||
| 122 | # For python3 | ||
| 123 | import urllib.request | 121 | import urllib.request |
| 124 | import urllib.error | 122 | import urllib.error |
| 125 | else: | 123 | else: |
| 126 | # For python2 | ||
| 127 | import imp | 124 | import imp |
| 125 | import urllib2 | ||
| 128 | urllib = imp.new_module('urllib') | 126 | urllib = imp.new_module('urllib') |
| 129 | urllib.request = urllib2 | 127 | urllib.request = urllib2 |
| 130 | urllib.error = urllib2 | 128 | urllib.error = urllib2 |
| 131 | 129 | ||
| 130 | |||
| 131 | def _print(*objects, **kwargs): | ||
| 132 | sep = kwargs.get('sep', ' ') | ||
| 133 | end = kwargs.get('end', '\n') | ||
| 134 | out = kwargs.get('file', sys.stdout) | ||
| 135 | out.write(sep.join(objects) + end) | ||
| 136 | |||
| 137 | |||
| 138 | # Python version check | ||
| 139 | ver = sys.version_info | ||
| 140 | if ver[0] == 3: | ||
| 141 | _print('error: Python 3 support is not fully implemented in repo yet.\n' | ||
| 142 | 'Please use Python 2.6 - 2.7 instead.', | ||
| 143 | file=sys.stderr) | ||
| 144 | sys.exit(1) | ||
| 145 | if (ver[0], ver[1]) < MIN_PYTHON_VERSION: | ||
| 146 | _print('error: Python version %s unsupported.\n' | ||
| 147 | 'Please use Python 2.6 - 2.7 instead.' | ||
| 148 | % sys.version.split(' ')[0], file=sys.stderr) | ||
| 149 | sys.exit(1) | ||
| 150 | |||
| 132 | home_dot_repo = os.path.expanduser('~/.repoconfig') | 151 | home_dot_repo = os.path.expanduser('~/.repoconfig') |
| 133 | gpg_dir = os.path.join(home_dot_repo, 'gnupg') | 152 | gpg_dir = os.path.join(home_dot_repo, 'gnupg') |
| 134 | 153 | ||
| @@ -164,7 +183,8 @@ group.add_option('--depth', type='int', default=None, | |||
| 164 | help='create a shallow clone with given depth; see git clone') | 183 | help='create a shallow clone with given depth; see git clone') |
| 165 | group.add_option('-g', '--groups', | 184 | group.add_option('-g', '--groups', |
| 166 | dest='groups', default='default', | 185 | dest='groups', default='default', |
| 167 | help='restrict manifest projects to ones with a specified group', | 186 | help='restrict manifest projects to ones with specified ' |
| 187 | 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]', | ||
| 168 | metavar='GROUP') | 188 | metavar='GROUP') |
| 169 | group.add_option('-p', '--platform', | 189 | group.add_option('-p', '--platform', |
| 170 | dest='platform', default="auto", | 190 | dest='platform', default="auto", |
| @@ -217,15 +237,15 @@ def _Init(args): | |||
| 217 | if branch.startswith('refs/heads/'): | 237 | if branch.startswith('refs/heads/'): |
| 218 | branch = branch[len('refs/heads/'):] | 238 | branch = branch[len('refs/heads/'):] |
| 219 | if branch.startswith('refs/'): | 239 | if branch.startswith('refs/'): |
| 220 | print("fatal: invalid branch name '%s'" % branch, file=sys.stderr) | 240 | _print("fatal: invalid branch name '%s'" % branch, file=sys.stderr) |
| 221 | raise CloneFailure() | 241 | raise CloneFailure() |
| 222 | 242 | ||
| 223 | if not os.path.isdir(repodir): | 243 | if not os.path.isdir(repodir): |
| 224 | try: | 244 | try: |
| 225 | os.mkdir(repodir) | 245 | os.mkdir(repodir) |
| 226 | except OSError as e: | 246 | except OSError as e: |
| 227 | print('fatal: cannot make %s directory: %s' | 247 | _print('fatal: cannot make %s directory: %s' |
| 228 | % (repodir, e.strerror), file=sys.stderr) | 248 | % (repodir, e.strerror), file=sys.stderr) |
| 229 | # Don't raise CloneFailure; that would delete the | 249 | # Don't raise CloneFailure; that would delete the |
| 230 | # name. Instead exit immediately. | 250 | # name. Instead exit immediately. |
| 231 | # | 251 | # |
| @@ -249,8 +269,8 @@ def _Init(args): | |||
| 249 | _Checkout(dst, branch, rev, opt.quiet) | 269 | _Checkout(dst, branch, rev, opt.quiet) |
| 250 | except CloneFailure: | 270 | except CloneFailure: |
| 251 | if opt.quiet: | 271 | if opt.quiet: |
| 252 | print('fatal: repo init failed; run without --quiet to see why', | 272 | _print('fatal: repo init failed; run without --quiet to see why', |
| 253 | file=sys.stderr) | 273 | file=sys.stderr) |
| 254 | raise | 274 | raise |
| 255 | 275 | ||
| 256 | 276 | ||
| @@ -259,12 +279,12 @@ def _CheckGitVersion(): | |||
| 259 | try: | 279 | try: |
| 260 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) | 280 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
| 261 | except OSError as e: | 281 | except OSError as e: |
| 262 | print(file=sys.stderr) | 282 | _print(file=sys.stderr) |
| 263 | print("fatal: '%s' is not available" % GIT, file=sys.stderr) | 283 | _print("fatal: '%s' is not available" % GIT, file=sys.stderr) |
| 264 | print('fatal: %s' % e, file=sys.stderr) | 284 | _print('fatal: %s' % e, file=sys.stderr) |
| 265 | print(file=sys.stderr) | 285 | _print(file=sys.stderr) |
| 266 | print('Please make sure %s is installed and in your path.' % GIT, | 286 | _print('Please make sure %s is installed and in your path.' % GIT, |
| 267 | file=sys.stderr) | 287 | file=sys.stderr) |
| 268 | raise CloneFailure() | 288 | raise CloneFailure() |
| 269 | 289 | ||
| 270 | ver_str = proc.stdout.read().strip() | 290 | ver_str = proc.stdout.read().strip() |
| @@ -272,14 +292,14 @@ def _CheckGitVersion(): | |||
| 272 | proc.wait() | 292 | proc.wait() |
| 273 | 293 | ||
| 274 | if not ver_str.startswith('git version '): | 294 | if not ver_str.startswith('git version '): |
| 275 | print('error: "%s" unsupported' % ver_str, file=sys.stderr) | 295 | _print('error: "%s" unsupported' % ver_str, file=sys.stderr) |
| 276 | raise CloneFailure() | 296 | raise CloneFailure() |
| 277 | 297 | ||
| 278 | ver_str = ver_str[len('git version '):].strip() | 298 | ver_str = ver_str[len('git version '):].strip() |
| 279 | ver_act = tuple(map(int, ver_str.split('.')[0:3])) | 299 | ver_act = tuple(map(int, ver_str.split('.')[0:3])) |
| 280 | if ver_act < MIN_GIT_VERSION: | 300 | if ver_act < MIN_GIT_VERSION: |
| 281 | need = '.'.join(map(str, MIN_GIT_VERSION)) | 301 | need = '.'.join(map(str, MIN_GIT_VERSION)) |
| 282 | print('fatal: git %s or later required' % need, file=sys.stderr) | 302 | _print('fatal: git %s or later required' % need, file=sys.stderr) |
| 283 | raise CloneFailure() | 303 | raise CloneFailure() |
| 284 | 304 | ||
| 285 | 305 | ||
| @@ -306,16 +326,16 @@ def SetupGnuPG(quiet): | |||
| 306 | try: | 326 | try: |
| 307 | os.mkdir(home_dot_repo) | 327 | os.mkdir(home_dot_repo) |
| 308 | except OSError as e: | 328 | except OSError as e: |
| 309 | print('fatal: cannot make %s directory: %s' | 329 | _print('fatal: cannot make %s directory: %s' |
| 310 | % (home_dot_repo, e.strerror), file=sys.stderr) | 330 | % (home_dot_repo, e.strerror), file=sys.stderr) |
| 311 | sys.exit(1) | 331 | sys.exit(1) |
| 312 | 332 | ||
| 313 | if not os.path.isdir(gpg_dir): | 333 | if not os.path.isdir(gpg_dir): |
| 314 | try: | 334 | try: |
| 315 | os.mkdir(gpg_dir, stat.S_IRWXU) | 335 | os.mkdir(gpg_dir, stat.S_IRWXU) |
| 316 | except OSError as e: | 336 | except OSError as e: |
| 317 | print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror), | 337 | _print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror), |
| 318 | file=sys.stderr) | 338 | file=sys.stderr) |
| 319 | sys.exit(1) | 339 | sys.exit(1) |
| 320 | 340 | ||
| 321 | env = os.environ.copy() | 341 | env = os.environ.copy() |
| @@ -328,18 +348,18 @@ def SetupGnuPG(quiet): | |||
| 328 | stdin = subprocess.PIPE) | 348 | stdin = subprocess.PIPE) |
| 329 | except OSError as e: | 349 | except OSError as e: |
| 330 | if not quiet: | 350 | if not quiet: |
| 331 | print('warning: gpg (GnuPG) is not available.', file=sys.stderr) | 351 | _print('warning: gpg (GnuPG) is not available.', file=sys.stderr) |
| 332 | print('warning: Installing it is strongly encouraged.', file=sys.stderr) | 352 | _print('warning: Installing it is strongly encouraged.', file=sys.stderr) |
| 333 | print(file=sys.stderr) | 353 | _print(file=sys.stderr) |
| 334 | return False | 354 | return False |
| 335 | 355 | ||
| 336 | proc.stdin.write(MAINTAINER_KEYS) | 356 | proc.stdin.write(MAINTAINER_KEYS) |
| 337 | proc.stdin.close() | 357 | proc.stdin.close() |
| 338 | 358 | ||
| 339 | if proc.wait() != 0: | 359 | if proc.wait() != 0: |
| 340 | print('fatal: registering repo maintainer keys failed', file=sys.stderr) | 360 | _print('fatal: registering repo maintainer keys failed', file=sys.stderr) |
| 341 | sys.exit(1) | 361 | sys.exit(1) |
| 342 | print() | 362 | _print() |
| 343 | 363 | ||
| 344 | fd = open(os.path.join(home_dot_repo, 'keyring-version'), 'w') | 364 | fd = open(os.path.join(home_dot_repo, 'keyring-version'), 'w') |
| 345 | fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n') | 365 | fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n') |
| @@ -381,7 +401,7 @@ def _InitHttp(): | |||
| 381 | 401 | ||
| 382 | def _Fetch(url, local, src, quiet): | 402 | def _Fetch(url, local, src, quiet): |
| 383 | if not quiet: | 403 | if not quiet: |
| 384 | print('Get %s' % url, file=sys.stderr) | 404 | _print('Get %s' % url, file=sys.stderr) |
| 385 | 405 | ||
| 386 | cmd = [GIT, 'fetch'] | 406 | cmd = [GIT, 'fetch'] |
| 387 | if quiet: | 407 | if quiet: |
| @@ -430,16 +450,16 @@ def _DownloadBundle(url, local, quiet): | |||
| 430 | except urllib.error.HTTPError as e: | 450 | except urllib.error.HTTPError as e: |
| 431 | if e.code in [403, 404]: | 451 | if e.code in [403, 404]: |
| 432 | return False | 452 | return False |
| 433 | print('fatal: Cannot get %s' % url, file=sys.stderr) | 453 | _print('fatal: Cannot get %s' % url, file=sys.stderr) |
| 434 | print('fatal: HTTP error %s' % e.code, file=sys.stderr) | 454 | _print('fatal: HTTP error %s' % e.code, file=sys.stderr) |
| 435 | raise CloneFailure() | 455 | raise CloneFailure() |
| 436 | except urllib.error.URLError as e: | 456 | except urllib.error.URLError as e: |
| 437 | print('fatal: Cannot get %s' % url, file=sys.stderr) | 457 | _print('fatal: Cannot get %s' % url, file=sys.stderr) |
| 438 | print('fatal: error %s' % e.reason, file=sys.stderr) | 458 | _print('fatal: error %s' % e.reason, file=sys.stderr) |
| 439 | raise CloneFailure() | 459 | raise CloneFailure() |
| 440 | try: | 460 | try: |
| 441 | if not quiet: | 461 | if not quiet: |
| 442 | print('Get %s' % url, file=sys.stderr) | 462 | _print('Get %s' % url, file=sys.stderr) |
| 443 | while True: | 463 | while True: |
| 444 | buf = r.read(8192) | 464 | buf = r.read(8192) |
| 445 | if buf == '': | 465 | if buf == '': |
| @@ -463,23 +483,23 @@ def _Clone(url, local, quiet): | |||
| 463 | try: | 483 | try: |
| 464 | os.mkdir(local) | 484 | os.mkdir(local) |
| 465 | except OSError as e: | 485 | except OSError as e: |
| 466 | print('fatal: cannot make %s directory: %s' % (local, e.strerror), | 486 | _print('fatal: cannot make %s directory: %s' % (local, e.strerror), |
| 467 | file=sys.stderr) | 487 | file=sys.stderr) |
| 468 | raise CloneFailure() | 488 | raise CloneFailure() |
| 469 | 489 | ||
| 470 | cmd = [GIT, 'init', '--quiet'] | 490 | cmd = [GIT, 'init', '--quiet'] |
| 471 | try: | 491 | try: |
| 472 | proc = subprocess.Popen(cmd, cwd = local) | 492 | proc = subprocess.Popen(cmd, cwd = local) |
| 473 | except OSError as e: | 493 | except OSError as e: |
| 474 | print(file=sys.stderr) | 494 | _print(file=sys.stderr) |
| 475 | print("fatal: '%s' is not available" % GIT, file=sys.stderr) | 495 | _print("fatal: '%s' is not available" % GIT, file=sys.stderr) |
| 476 | print('fatal: %s' % e, file=sys.stderr) | 496 | _print('fatal: %s' % e, file=sys.stderr) |
| 477 | print(file=sys.stderr) | 497 | _print(file=sys.stderr) |
| 478 | print('Please make sure %s is installed and in your path.' % GIT, | 498 | _print('Please make sure %s is installed and in your path.' % GIT, |
| 479 | file=sys.stderr) | 499 | file=sys.stderr) |
| 480 | raise CloneFailure() | 500 | raise CloneFailure() |
| 481 | if proc.wait() != 0: | 501 | if proc.wait() != 0: |
| 482 | print('fatal: could not create %s' % local, file=sys.stderr) | 502 | _print('fatal: could not create %s' % local, file=sys.stderr) |
| 483 | raise CloneFailure() | 503 | raise CloneFailure() |
| 484 | 504 | ||
| 485 | _InitHttp() | 505 | _InitHttp() |
| @@ -507,18 +527,18 @@ def _Verify(cwd, branch, quiet): | |||
| 507 | proc.stderr.close() | 527 | proc.stderr.close() |
| 508 | 528 | ||
| 509 | if proc.wait() != 0 or not cur: | 529 | if proc.wait() != 0 or not cur: |
| 510 | print(file=sys.stderr) | 530 | _print(file=sys.stderr) |
| 511 | print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr) | 531 | _print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr) |
| 512 | raise CloneFailure() | 532 | raise CloneFailure() |
| 513 | 533 | ||
| 514 | m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur) | 534 | m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur) |
| 515 | if m: | 535 | if m: |
| 516 | cur = m.group(1) | 536 | cur = m.group(1) |
| 517 | if not quiet: | 537 | if not quiet: |
| 518 | print(file=sys.stderr) | 538 | _print(file=sys.stderr) |
| 519 | print("info: Ignoring branch '%s'; using tagged release '%s'" | 539 | _print("info: Ignoring branch '%s'; using tagged release '%s'" |
| 520 | % (branch, cur), file=sys.stderr) | 540 | % (branch, cur), file=sys.stderr) |
| 521 | print(file=sys.stderr) | 541 | _print(file=sys.stderr) |
| 522 | 542 | ||
| 523 | env = os.environ.copy() | 543 | env = os.environ.copy() |
| 524 | env['GNUPGHOME'] = gpg_dir.encode() | 544 | env['GNUPGHOME'] = gpg_dir.encode() |
| @@ -536,10 +556,10 @@ def _Verify(cwd, branch, quiet): | |||
| 536 | proc.stderr.close() | 556 | proc.stderr.close() |
| 537 | 557 | ||
| 538 | if proc.wait() != 0: | 558 | if proc.wait() != 0: |
| 539 | print(file=sys.stderr) | 559 | _print(file=sys.stderr) |
| 540 | print(out, file=sys.stderr) | 560 | _print(out, file=sys.stderr) |
| 541 | print(err, file=sys.stderr) | 561 | _print(err, file=sys.stderr) |
| 542 | print(file=sys.stderr) | 562 | _print(file=sys.stderr) |
| 543 | raise CloneFailure() | 563 | raise CloneFailure() |
| 544 | return '%s^0' % cur | 564 | return '%s^0' % cur |
| 545 | 565 | ||
| @@ -606,7 +626,7 @@ def _ParseArguments(args): | |||
| 606 | 626 | ||
| 607 | 627 | ||
| 608 | def _Usage(): | 628 | def _Usage(): |
| 609 | print( | 629 | _print( |
| 610 | """usage: repo COMMAND [ARGS] | 630 | """usage: repo COMMAND [ARGS] |
| 611 | 631 | ||
| 612 | repo is not yet installed. Use "repo init" to install it here. | 632 | repo is not yet installed. Use "repo init" to install it here. |
| @@ -627,23 +647,23 @@ def _Help(args): | |||
| 627 | init_optparse.print_help() | 647 | init_optparse.print_help() |
| 628 | sys.exit(0) | 648 | sys.exit(0) |
| 629 | else: | 649 | else: |
| 630 | print("error: '%s' is not a bootstrap command.\n" | 650 | _print("error: '%s' is not a bootstrap command.\n" |
| 631 | ' For access to online help, install repo ("repo init").' | 651 | ' For access to online help, install repo ("repo init").' |
| 632 | % args[0], file=sys.stderr) | 652 | % args[0], file=sys.stderr) |
| 633 | else: | 653 | else: |
| 634 | _Usage() | 654 | _Usage() |
| 635 | sys.exit(1) | 655 | sys.exit(1) |
| 636 | 656 | ||
| 637 | 657 | ||
| 638 | def _NotInstalled(): | 658 | def _NotInstalled(): |
| 639 | print('error: repo is not installed. Use "repo init" to install it here.', | 659 | _print('error: repo is not installed. Use "repo init" to install it here.', |
| 640 | file=sys.stderr) | 660 | file=sys.stderr) |
| 641 | sys.exit(1) | 661 | sys.exit(1) |
| 642 | 662 | ||
| 643 | 663 | ||
| 644 | def _NoCommands(cmd): | 664 | def _NoCommands(cmd): |
| 645 | print("""error: command '%s' requires repo to be installed first. | 665 | _print("""error: command '%s' requires repo to be installed first. |
| 646 | Use "repo init" to install it here.""" % cmd, file=sys.stderr) | 666 | Use "repo init" to install it here.""" % cmd, file=sys.stderr) |
| 647 | sys.exit(1) | 667 | sys.exit(1) |
| 648 | 668 | ||
| 649 | 669 | ||
| @@ -680,7 +700,7 @@ def _SetDefaultsTo(gitdir): | |||
| 680 | proc.stderr.close() | 700 | proc.stderr.close() |
| 681 | 701 | ||
| 682 | if proc.wait() != 0: | 702 | if proc.wait() != 0: |
| 683 | print('fatal: %s has no current branch' % gitdir, file=sys.stderr) | 703 | _print('fatal: %s has no current branch' % gitdir, file=sys.stderr) |
| 684 | sys.exit(1) | 704 | sys.exit(1) |
| 685 | 705 | ||
| 686 | 706 | ||
| @@ -729,8 +749,8 @@ def main(orig_args): | |||
| 729 | try: | 749 | try: |
| 730 | os.execv(sys.executable, me) | 750 | os.execv(sys.executable, me) |
| 731 | except OSError as e: | 751 | except OSError as e: |
| 732 | print("fatal: unable to start %s" % repo_main, file=sys.stderr) | 752 | _print("fatal: unable to start %s" % repo_main, file=sys.stderr) |
| 733 | print("fatal: %s" % e, file=sys.stderr) | 753 | _print("fatal: %s" % e, file=sys.stderr) |
| 734 | sys.exit(148) | 754 | sys.exit(148) |
| 735 | 755 | ||
| 736 | 756 | ||
diff --git a/subcmds/__init__.py b/subcmds/__init__.py index 1fac802e..84efb4de 100644 --- a/subcmds/__init__.py +++ b/subcmds/__init__.py | |||
| @@ -38,8 +38,8 @@ for py in os.listdir(my_dir): | |||
| 38 | try: | 38 | try: |
| 39 | cmd = getattr(mod, clsn)() | 39 | cmd = getattr(mod, clsn)() |
| 40 | except AttributeError: | 40 | except AttributeError: |
| 41 | raise SyntaxError, '%s/%s does not define class %s' % ( | 41 | raise SyntaxError('%s/%s does not define class %s' % ( |
| 42 | __name__, py, clsn) | 42 | __name__, py, clsn)) |
| 43 | 43 | ||
| 44 | name = name.replace('_', '-') | 44 | name = name.replace('_', '-') |
| 45 | cmd.NAME = name | 45 | cmd.NAME = name |
diff --git a/subcmds/branches.py b/subcmds/branches.py index 06d45abe..c2e7c4b9 100644 --- a/subcmds/branches.py +++ b/subcmds/branches.py | |||
| @@ -98,14 +98,13 @@ is shown, then the branch appears in all projects. | |||
| 98 | project_cnt = len(projects) | 98 | project_cnt = len(projects) |
| 99 | 99 | ||
| 100 | for project in projects: | 100 | for project in projects: |
| 101 | for name, b in project.GetBranches().iteritems(): | 101 | for name, b in project.GetBranches().items(): |
| 102 | b.project = project | 102 | b.project = project |
| 103 | if name not in all_branches: | 103 | if name not in all_branches: |
| 104 | all_branches[name] = BranchInfo(name) | 104 | all_branches[name] = BranchInfo(name) |
| 105 | all_branches[name].add(b) | 105 | all_branches[name].add(b) |
| 106 | 106 | ||
| 107 | names = all_branches.keys() | 107 | names = list(sorted(all_branches)) |
| 108 | names.sort() | ||
| 109 | 108 | ||
| 110 | if not names: | 109 | if not names: |
| 111 | print(' (no branches)', file=sys.stderr) | 110 | print(' (no branches)', file=sys.stderr) |
diff --git a/subcmds/cherry_pick.py b/subcmds/cherry_pick.py index 01b97e07..520e4c32 100644 --- a/subcmds/cherry_pick.py +++ b/subcmds/cherry_pick.py | |||
| @@ -81,7 +81,7 @@ change id will be added. | |||
| 81 | sys.exit(1) | 81 | sys.exit(1) |
| 82 | 82 | ||
| 83 | else: | 83 | else: |
| 84 | print('NOTE: When committing (please see above) and editing the commit' | 84 | print('NOTE: When committing (please see above) and editing the commit ' |
| 85 | 'message, please remove the old Change-Id-line and add:') | 85 | 'message, please remove the old Change-Id-line and add:') |
| 86 | print(self._GetReference(sha1), file=sys.stderr) | 86 | print(self._GetReference(sha1), file=sys.stderr) |
| 87 | print(file=sys.stderr) | 87 | print(file=sys.stderr) |
diff --git a/subcmds/forall.py b/subcmds/forall.py index 4c1c9ff8..e2a420a9 100644 --- a/subcmds/forall.py +++ b/subcmds/forall.py | |||
| @@ -42,10 +42,14 @@ class Forall(Command, MirrorSafeCommand): | |||
| 42 | helpSummary = "Run a shell command in each project" | 42 | helpSummary = "Run a shell command in each project" |
| 43 | helpUsage = """ | 43 | helpUsage = """ |
| 44 | %prog [<project>...] -c <command> [<arg>...] | 44 | %prog [<project>...] -c <command> [<arg>...] |
| 45 | %prog -r str1 [str2] ... -c <command> [<arg>...]" | ||
| 45 | """ | 46 | """ |
| 46 | helpDescription = """ | 47 | helpDescription = """ |
| 47 | Executes the same shell command in each project. | 48 | Executes the same shell command in each project. |
| 48 | 49 | ||
| 50 | The -r option allows running the command only on projects matching | ||
| 51 | regex or wildcard expression. | ||
| 52 | |||
| 49 | Output Formatting | 53 | Output Formatting |
| 50 | ----------------- | 54 | ----------------- |
| 51 | 55 | ||
| @@ -103,6 +107,9 @@ without iterating through the remaining projects. | |||
| 103 | setattr(parser.values, option.dest, list(parser.rargs)) | 107 | setattr(parser.values, option.dest, list(parser.rargs)) |
| 104 | while parser.rargs: | 108 | while parser.rargs: |
| 105 | del parser.rargs[0] | 109 | del parser.rargs[0] |
| 110 | p.add_option('-r', '--regex', | ||
| 111 | dest='regex', action='store_true', | ||
| 112 | help="Execute the command only on projects matching regex or wildcard expression") | ||
| 106 | p.add_option('-c', '--command', | 113 | p.add_option('-c', '--command', |
| 107 | help='Command (and arguments) to execute', | 114 | help='Command (and arguments) to execute', |
| 108 | dest='command', | 115 | dest='command', |
| @@ -166,7 +173,12 @@ without iterating through the remaining projects. | |||
| 166 | rc = 0 | 173 | rc = 0 |
| 167 | first = True | 174 | first = True |
| 168 | 175 | ||
| 169 | for project in self.GetProjects(args): | 176 | if not opt.regex: |
| 177 | projects = self.GetProjects(args) | ||
| 178 | else: | ||
| 179 | projects = self.FindProjects(args) | ||
| 180 | |||
| 181 | for project in projects: | ||
| 170 | env = os.environ.copy() | 182 | env = os.environ.copy() |
| 171 | def setenv(name, val): | 183 | def setenv(name, val): |
| 172 | if val is None: | 184 | if val is None: |
| @@ -248,7 +260,12 @@ without iterating through the remaining projects. | |||
| 248 | first = False | 260 | first = False |
| 249 | else: | 261 | else: |
| 250 | out.nl() | 262 | out.nl() |
| 251 | out.project('project %s/', project.relpath) | 263 | |
| 264 | if mirror: | ||
| 265 | project_header_path = project.name | ||
| 266 | else: | ||
| 267 | project_header_path = project.relpath | ||
| 268 | out.project('project %s/', project_header_path) | ||
| 252 | out.nl() | 269 | out.nl() |
| 253 | out.flush() | 270 | out.flush() |
| 254 | if errbuf: | 271 | if errbuf: |
diff --git a/subcmds/help.py b/subcmds/help.py index 15aab7f9..4aa3f863 100644 --- a/subcmds/help.py +++ b/subcmds/help.py | |||
| @@ -34,8 +34,7 @@ Displays detailed usage information about a command. | |||
| 34 | def _PrintAllCommands(self): | 34 | def _PrintAllCommands(self): |
| 35 | print('usage: repo COMMAND [ARGS]') | 35 | print('usage: repo COMMAND [ARGS]') |
| 36 | print('The complete list of recognized repo commands are:') | 36 | print('The complete list of recognized repo commands are:') |
| 37 | commandNames = self.commands.keys() | 37 | commandNames = list(sorted(self.commands)) |
| 38 | commandNames.sort() | ||
| 39 | 38 | ||
| 40 | maxlen = 0 | 39 | maxlen = 0 |
| 41 | for name in commandNames: | 40 | for name in commandNames: |
| @@ -49,16 +48,15 @@ Displays detailed usage information about a command. | |||
| 49 | except AttributeError: | 48 | except AttributeError: |
| 50 | summary = '' | 49 | summary = '' |
| 51 | print(fmt % (name, summary)) | 50 | print(fmt % (name, summary)) |
| 52 | print("See 'repo help <command>' for more information on a" | 51 | print("See 'repo help <command>' for more information on a " |
| 53 | 'specific command.') | 52 | 'specific command.') |
| 54 | 53 | ||
| 55 | def _PrintCommonCommands(self): | 54 | def _PrintCommonCommands(self): |
| 56 | print('usage: repo COMMAND [ARGS]') | 55 | print('usage: repo COMMAND [ARGS]') |
| 57 | print('The most commonly used repo commands are:') | 56 | print('The most commonly used repo commands are:') |
| 58 | commandNames = [name | 57 | commandNames = list(sorted([name |
| 59 | for name in self.commands.keys() | 58 | for name, command in self.commands.items() |
| 60 | if self.commands[name].common] | 59 | if command.common])) |
| 61 | commandNames.sort() | ||
| 62 | 60 | ||
| 63 | maxlen = 0 | 61 | maxlen = 0 |
| 64 | for name in commandNames: | 62 | for name in commandNames: |
diff --git a/subcmds/info.py b/subcmds/info.py index 8fb363f3..d42860ae 100644 --- a/subcmds/info.py +++ b/subcmds/info.py | |||
| @@ -27,7 +27,7 @@ class Info(PagedCommand): | |||
| 27 | helpSummary = "Get info on the manifest branch, current branch or unmerged branches" | 27 | helpSummary = "Get info on the manifest branch, current branch or unmerged branches" |
| 28 | helpUsage = "%prog [-dl] [-o [-b]] [<project>...]" | 28 | helpUsage = "%prog [-dl] [-o [-b]] [<project>...]" |
| 29 | 29 | ||
| 30 | def _Options(self, p, show_smart=True): | 30 | def _Options(self, p): |
| 31 | p.add_option('-d', '--diff', | 31 | p.add_option('-d', '--diff', |
| 32 | dest='all', action='store_true', | 32 | dest='all', action='store_true', |
| 33 | help="show full info and commit diff including remote branches") | 33 | help="show full info and commit diff including remote branches") |
| @@ -53,7 +53,10 @@ class Info(PagedCommand): | |||
| 53 | 53 | ||
| 54 | self.opt = opt | 54 | self.opt = opt |
| 55 | 55 | ||
| 56 | mergeBranch = self.manifest.manifestProject.config.GetBranch("default").merge | 56 | manifestConfig = self.manifest.manifestProject.config |
| 57 | mergeBranch = manifestConfig.GetBranch("default").merge | ||
| 58 | manifestGroups = (manifestConfig.GetString('manifest.groups') | ||
| 59 | or 'all,-notdefault') | ||
| 57 | 60 | ||
| 58 | self.heading("Manifest branch: ") | 61 | self.heading("Manifest branch: ") |
| 59 | self.headtext(self.manifest.default.revisionExpr) | 62 | self.headtext(self.manifest.default.revisionExpr) |
| @@ -61,6 +64,9 @@ class Info(PagedCommand): | |||
| 61 | self.heading("Manifest merge branch: ") | 64 | self.heading("Manifest merge branch: ") |
| 62 | self.headtext(mergeBranch) | 65 | self.headtext(mergeBranch) |
| 63 | self.out.nl() | 66 | self.out.nl() |
| 67 | self.heading("Manifest groups: ") | ||
| 68 | self.headtext(manifestGroups) | ||
| 69 | self.out.nl() | ||
| 64 | 70 | ||
| 65 | self.printSeparator() | 71 | self.printSeparator() |
| 66 | 72 | ||
| @@ -157,7 +163,7 @@ class Info(PagedCommand): | |||
| 157 | all_branches = [] | 163 | all_branches = [] |
| 158 | for project in self.GetProjects(args): | 164 | for project in self.GetProjects(args): |
| 159 | br = [project.GetUploadableBranch(x) | 165 | br = [project.GetUploadableBranch(x) |
| 160 | for x in project.GetBranches().keys()] | 166 | for x in project.GetBranches()] |
| 161 | br = [x for x in br if x] | 167 | br = [x for x in br if x] |
| 162 | if self.opt.current_branch: | 168 | if self.opt.current_branch: |
| 163 | br = [x for x in br if x.name == project.CurrentBranch] | 169 | br = [x for x in br if x.name == project.CurrentBranch] |
diff --git a/subcmds/init.py b/subcmds/init.py index 11312601..a44fb7a9 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
| @@ -20,6 +20,15 @@ import re | |||
| 20 | import shutil | 20 | import shutil |
| 21 | import sys | 21 | import sys |
| 22 | 22 | ||
| 23 | from pyversion import is_python3 | ||
| 24 | if is_python3(): | ||
| 25 | import urllib.parse | ||
| 26 | else: | ||
| 27 | import imp | ||
| 28 | import urlparse | ||
| 29 | urllib = imp.new_module('urllib') | ||
| 30 | urllib.parse = urlparse.urlparse | ||
| 31 | |||
| 23 | from color import Coloring | 32 | from color import Coloring |
| 24 | from command import InteractiveCommand, MirrorSafeCommand | 33 | from command import InteractiveCommand, MirrorSafeCommand |
| 25 | from error import ManifestParseError | 34 | from error import ManifestParseError |
| @@ -91,8 +100,9 @@ to update the working directory files. | |||
| 91 | dest='depth', | 100 | dest='depth', |
| 92 | help='create a shallow clone with given depth; see git clone') | 101 | help='create a shallow clone with given depth; see git clone') |
| 93 | g.add_option('-g', '--groups', | 102 | g.add_option('-g', '--groups', |
| 94 | dest='groups', default='all,-notdefault', | 103 | dest='groups', default='default', |
| 95 | help='restrict manifest projects to ones with a specified group', | 104 | help='restrict manifest projects to ones with specified ' |
| 105 | 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]', | ||
| 96 | metavar='GROUP') | 106 | metavar='GROUP') |
| 97 | g.add_option('-p', '--platform', | 107 | g.add_option('-p', '--platform', |
| 98 | dest='platform', default='auto', | 108 | dest='platform', default='auto', |
| @@ -134,7 +144,19 @@ to update the working directory files. | |||
| 134 | if not opt.quiet: | 144 | if not opt.quiet: |
| 135 | print('Get %s' % GitConfig.ForUser().UrlInsteadOf(opt.manifest_url), | 145 | print('Get %s' % GitConfig.ForUser().UrlInsteadOf(opt.manifest_url), |
| 136 | file=sys.stderr) | 146 | file=sys.stderr) |
| 137 | m._InitGitDir() | 147 | |
| 148 | # The manifest project object doesn't keep track of the path on the | ||
| 149 | # server where this git is located, so let's save that here. | ||
| 150 | mirrored_manifest_git = None | ||
| 151 | if opt.reference: | ||
| 152 | manifest_git_path = urllib.parse(opt.manifest_url).path[1:] | ||
| 153 | mirrored_manifest_git = os.path.join(opt.reference, manifest_git_path) | ||
| 154 | if not mirrored_manifest_git.endswith(".git"): | ||
| 155 | mirrored_manifest_git += ".git" | ||
| 156 | if not os.path.exists(mirrored_manifest_git): | ||
| 157 | mirrored_manifest_git = os.path.join(opt.reference + '/.repo/manifests.git') | ||
| 158 | |||
| 159 | m._InitGitDir(mirror_git=mirrored_manifest_git) | ||
| 138 | 160 | ||
| 139 | if opt.manifest_branch: | 161 | if opt.manifest_branch: |
| 140 | m.revisionExpr = opt.manifest_branch | 162 | m.revisionExpr = opt.manifest_branch |
| @@ -169,7 +191,7 @@ to update the working directory files. | |||
| 169 | 191 | ||
| 170 | groups = [x for x in groups if x] | 192 | groups = [x for x in groups if x] |
| 171 | groupstr = ','.join(groups) | 193 | groupstr = ','.join(groups) |
| 172 | if opt.platform == 'auto' and groupstr == 'all,-notdefault,platform-' + platform.system().lower(): | 194 | if opt.platform == 'auto' and groupstr == 'default,platform-' + platform.system().lower(): |
| 173 | groupstr = None | 195 | groupstr = None |
| 174 | m.config.SetString('manifest.groups', groupstr) | 196 | m.config.SetString('manifest.groups', groupstr) |
| 175 | 197 | ||
diff --git a/subcmds/list.py b/subcmds/list.py index 0d5c27f7..945c28d8 100644 --- a/subcmds/list.py +++ b/subcmds/list.py | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | 16 | from __future__ import print_function |
| 17 | import re | 17 | import sys |
| 18 | 18 | ||
| 19 | from command import Command, MirrorSafeCommand | 19 | from command import Command, MirrorSafeCommand |
| 20 | 20 | ||
| @@ -31,13 +31,19 @@ List all projects; pass '.' to list the project for the cwd. | |||
| 31 | This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'. | 31 | This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'. |
| 32 | """ | 32 | """ |
| 33 | 33 | ||
| 34 | def _Options(self, p, show_smart=True): | 34 | def _Options(self, p): |
| 35 | p.add_option('-r', '--regex', | 35 | p.add_option('-r', '--regex', |
| 36 | dest='regex', action='store_true', | 36 | dest='regex', action='store_true', |
| 37 | help="Filter the project list based on regex or wildcard matching of strings") | 37 | help="Filter the project list based on regex or wildcard matching of strings") |
| 38 | p.add_option('-f', '--fullpath', | 38 | p.add_option('-f', '--fullpath', |
| 39 | dest='fullpath', action='store_true', | 39 | dest='fullpath', action='store_true', |
| 40 | help="Display the full work tree path instead of the relative path") | 40 | help="Display the full work tree path instead of the relative path") |
| 41 | p.add_option('-n', '--name-only', | ||
| 42 | dest='name_only', action='store_true', | ||
| 43 | help="Display only the name of the repository") | ||
| 44 | p.add_option('-p', '--path-only', | ||
| 45 | dest='path_only', action='store_true', | ||
| 46 | help="Display only the path of the repository") | ||
| 41 | 47 | ||
| 42 | def Execute(self, opt, args): | 48 | def Execute(self, opt, args): |
| 43 | """List all projects and the associated directories. | 49 | """List all projects and the associated directories. |
| @@ -50,6 +56,11 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'. | |||
| 50 | opt: The options. | 56 | opt: The options. |
| 51 | args: Positional args. Can be a list of projects to list, or empty. | 57 | args: Positional args. Can be a list of projects to list, or empty. |
| 52 | """ | 58 | """ |
| 59 | |||
| 60 | if opt.fullpath and opt.name_only: | ||
| 61 | print('error: cannot combine -f and -n', file=sys.stderr) | ||
| 62 | sys.exit(1) | ||
| 63 | |||
| 53 | if not opt.regex: | 64 | if not opt.regex: |
| 54 | projects = self.GetProjects(args) | 65 | projects = self.GetProjects(args) |
| 55 | else: | 66 | else: |
| @@ -62,18 +73,12 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'. | |||
| 62 | 73 | ||
| 63 | lines = [] | 74 | lines = [] |
| 64 | for project in projects: | 75 | for project in projects: |
| 65 | lines.append("%s : %s" % (_getpath(project), project.name)) | 76 | if opt.name_only and not opt.path_only: |
| 77 | lines.append("%s" % ( project.name)) | ||
| 78 | elif opt.path_only and not opt.name_only: | ||
| 79 | lines.append("%s" % (_getpath(project))) | ||
| 80 | else: | ||
| 81 | lines.append("%s : %s" % (_getpath(project), project.name)) | ||
| 66 | 82 | ||
| 67 | lines.sort() | 83 | lines.sort() |
| 68 | print('\n'.join(lines)) | 84 | print('\n'.join(lines)) |
| 69 | |||
| 70 | def FindProjects(self, args): | ||
| 71 | result = [] | ||
| 72 | for project in self.GetProjects(''): | ||
| 73 | for arg in args: | ||
| 74 | pattern = re.compile(r'%s' % arg, re.IGNORECASE) | ||
| 75 | if pattern.search(project.name) or pattern.search(project.relpath): | ||
| 76 | result.append(project) | ||
| 77 | break | ||
| 78 | result.sort(key=lambda project: project.relpath) | ||
| 79 | return result | ||
diff --git a/subcmds/overview.py b/subcmds/overview.py index 418459ae..eed8cf20 100644 --- a/subcmds/overview.py +++ b/subcmds/overview.py | |||
| @@ -42,7 +42,7 @@ are displayed. | |||
| 42 | all_branches = [] | 42 | all_branches = [] |
| 43 | for project in self.GetProjects(args): | 43 | for project in self.GetProjects(args): |
| 44 | br = [project.GetUploadableBranch(x) | 44 | br = [project.GetUploadableBranch(x) |
| 45 | for x in project.GetBranches().keys()] | 45 | for x in project.GetBranches()] |
| 46 | br = [x for x in br if x] | 46 | br = [x for x in br if x] |
| 47 | if opt.current_branch: | 47 | if opt.current_branch: |
| 48 | br = [x for x in br if x.name == project.CurrentBranch] | 48 | br = [x for x in br if x.name == project.CurrentBranch] |
diff --git a/subcmds/rebase.py b/subcmds/rebase.py index 06cda22c..b9a7774d 100644 --- a/subcmds/rebase.py +++ b/subcmds/rebase.py | |||
| @@ -68,7 +68,7 @@ branch but need to incorporate new upstream changes "underneath" them. | |||
| 68 | cb = project.CurrentBranch | 68 | cb = project.CurrentBranch |
| 69 | if not cb: | 69 | if not cb: |
| 70 | if one_project: | 70 | if one_project: |
| 71 | print("error: project %s has a detatched HEAD" % project.relpath, | 71 | print("error: project %s has a detached HEAD" % project.relpath, |
| 72 | file=sys.stderr) | 72 | file=sys.stderr) |
| 73 | return -1 | 73 | return -1 |
| 74 | # ignore branches with detatched HEADs | 74 | # ignore branches with detatched HEADs |
diff --git a/subcmds/stage.py b/subcmds/stage.py index ff15ee0c..28849764 100644 --- a/subcmds/stage.py +++ b/subcmds/stage.py | |||
| @@ -49,7 +49,7 @@ The '%prog' command stages files to prepare the next commit. | |||
| 49 | self.Usage() | 49 | self.Usage() |
| 50 | 50 | ||
| 51 | def _Interactive(self, opt, args): | 51 | def _Interactive(self, opt, args): |
| 52 | all_projects = filter(lambda x: x.IsDirty(), self.GetProjects(args)) | 52 | all_projects = [p for p in self.GetProjects(args) if p.IsDirty()] |
| 53 | if not all_projects: | 53 | if not all_projects: |
| 54 | print('no projects have uncommitted modifications', file=sys.stderr) | 54 | print('no projects have uncommitted modifications', file=sys.stderr) |
| 55 | return | 55 | return |
| @@ -98,9 +98,9 @@ The '%prog' command stages files to prepare the next commit. | |||
| 98 | _AddI(all_projects[a_index - 1]) | 98 | _AddI(all_projects[a_index - 1]) |
| 99 | continue | 99 | continue |
| 100 | 100 | ||
| 101 | p = filter(lambda x: x.name == a or x.relpath == a, all_projects) | 101 | projects = [p for p in all_projects if a in [p.name, p.relpath]] |
| 102 | if len(p) == 1: | 102 | if len(projects) == 1: |
| 103 | _AddI(p[0]) | 103 | _AddI(projects[0]) |
| 104 | continue | 104 | continue |
| 105 | print('Bye.') | 105 | print('Bye.') |
| 106 | 106 | ||
diff --git a/subcmds/status.py b/subcmds/status.py index cce00c81..41c4429a 100644 --- a/subcmds/status.py +++ b/subcmds/status.py | |||
| @@ -21,10 +21,16 @@ except ImportError: | |||
| 21 | import dummy_threading as _threading | 21 | import dummy_threading as _threading |
| 22 | 22 | ||
| 23 | import glob | 23 | import glob |
| 24 | |||
| 25 | from pyversion import is_python3 | ||
| 26 | if is_python3(): | ||
| 27 | import io | ||
| 28 | else: | ||
| 29 | import StringIO as io | ||
| 30 | |||
| 24 | import itertools | 31 | import itertools |
| 25 | import os | 32 | import os |
| 26 | import sys | 33 | import sys |
| 27 | import StringIO | ||
| 28 | 34 | ||
| 29 | from color import Coloring | 35 | from color import Coloring |
| 30 | 36 | ||
| @@ -142,7 +148,7 @@ the following meanings: | |||
| 142 | for project in all_projects: | 148 | for project in all_projects: |
| 143 | sem.acquire() | 149 | sem.acquire() |
| 144 | 150 | ||
| 145 | class BufList(StringIO.StringIO): | 151 | class BufList(io.StringIO): |
| 146 | def dump(self, ostream): | 152 | def dump(self, ostream): |
| 147 | for entry in self.buflist: | 153 | for entry in self.buflist: |
| 148 | ostream.write(entry) | 154 | ostream.write(entry) |
| @@ -182,7 +188,7 @@ the following meanings: | |||
| 182 | try: | 188 | try: |
| 183 | os.chdir(self.manifest.topdir) | 189 | os.chdir(self.manifest.topdir) |
| 184 | 190 | ||
| 185 | outstring = StringIO.StringIO() | 191 | outstring = io.StringIO() |
| 186 | self._FindOrphans(glob.glob('.*') + \ | 192 | self._FindOrphans(glob.glob('.*') + \ |
| 187 | glob.glob('*'), \ | 193 | glob.glob('*'), \ |
| 188 | proj_dirs, proj_dirs_parents, outstring) | 194 | proj_dirs, proj_dirs_parents, outstring) |
diff --git a/subcmds/sync.py b/subcmds/sync.py index 5c369a74..e9d52b7b 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
| @@ -24,8 +24,19 @@ import socket | |||
| 24 | import subprocess | 24 | import subprocess |
| 25 | import sys | 25 | import sys |
| 26 | import time | 26 | import time |
| 27 | import urlparse | 27 | |
| 28 | import xmlrpclib | 28 | from pyversion import is_python3 |
| 29 | if is_python3(): | ||
| 30 | import urllib.parse | ||
| 31 | import xmlrpc.client | ||
| 32 | else: | ||
| 33 | import imp | ||
| 34 | import urlparse | ||
| 35 | import xmlrpclib | ||
| 36 | urllib = imp.new_module('urllib') | ||
| 37 | urllib.parse = urlparse | ||
| 38 | xmlrpc = imp.new_module('xmlrpc') | ||
| 39 | xmlrpc.client = xmlrpclib | ||
| 29 | 40 | ||
| 30 | try: | 41 | try: |
| 31 | import threading as _threading | 42 | import threading as _threading |
| @@ -228,6 +239,9 @@ later is required to fix a server side protocol bug. | |||
| 228 | # We'll set to true once we've locked the lock. | 239 | # We'll set to true once we've locked the lock. |
| 229 | did_lock = False | 240 | did_lock = False |
| 230 | 241 | ||
| 242 | if not opt.quiet: | ||
| 243 | print('Fetching project %s' % project.name) | ||
| 244 | |||
| 231 | # Encapsulate everything in a try/except/finally so that: | 245 | # Encapsulate everything in a try/except/finally so that: |
| 232 | # - We always set err_event in the case of an exception. | 246 | # - We always set err_event in the case of an exception. |
| 233 | # - We always make sure we call sem.release(). | 247 | # - We always make sure we call sem.release(). |
| @@ -274,6 +288,8 @@ later is required to fix a server side protocol bug. | |||
| 274 | if self.jobs == 1: | 288 | if self.jobs == 1: |
| 275 | for project in projects: | 289 | for project in projects: |
| 276 | pm.update() | 290 | pm.update() |
| 291 | if not opt.quiet: | ||
| 292 | print('Fetching project %s' % project.name) | ||
| 277 | if project.Sync_NetworkHalf( | 293 | if project.Sync_NetworkHalf( |
| 278 | quiet=opt.quiet, | 294 | quiet=opt.quiet, |
| 279 | current_branch_only=opt.current_branch_only, | 295 | current_branch_only=opt.current_branch_only, |
| @@ -372,6 +388,13 @@ later is required to fix a server side protocol bug. | |||
| 372 | print('\nerror: Exited sync due to gc errors', file=sys.stderr) | 388 | print('\nerror: Exited sync due to gc errors', file=sys.stderr) |
| 373 | sys.exit(1) | 389 | sys.exit(1) |
| 374 | 390 | ||
| 391 | def _ReloadManifest(self, manifest_name=None): | ||
| 392 | if manifest_name: | ||
| 393 | # Override calls _Unload already | ||
| 394 | self.manifest.Override(manifest_name) | ||
| 395 | else: | ||
| 396 | self.manifest._Unload() | ||
| 397 | |||
| 375 | def UpdateProjectList(self): | 398 | def UpdateProjectList(self): |
| 376 | new_project_paths = [] | 399 | new_project_paths = [] |
| 377 | for project in self.GetProjects(None, missing_ok=True): | 400 | for project in self.GetProjects(None, missing_ok=True): |
| @@ -406,7 +429,7 @@ later is required to fix a server side protocol bug. | |||
| 406 | groups = None) | 429 | groups = None) |
| 407 | 430 | ||
| 408 | if project.IsDirty(): | 431 | if project.IsDirty(): |
| 409 | print('error: Cannot remove project "%s": uncommitted changes' | 432 | print('error: Cannot remove project "%s": uncommitted changes ' |
| 410 | 'are present' % project.relpath, file=sys.stderr) | 433 | 'are present' % project.relpath, file=sys.stderr) |
| 411 | print(' commit changes, then run sync again', | 434 | print(' commit changes, then run sync again', |
| 412 | file=sys.stderr) | 435 | file=sys.stderr) |
| @@ -464,13 +487,17 @@ later is required to fix a server side protocol bug. | |||
| 464 | if opt.manifest_name: | 487 | if opt.manifest_name: |
| 465 | self.manifest.Override(opt.manifest_name) | 488 | self.manifest.Override(opt.manifest_name) |
| 466 | 489 | ||
| 490 | manifest_name = opt.manifest_name | ||
| 491 | |||
| 467 | if opt.smart_sync or opt.smart_tag: | 492 | if opt.smart_sync or opt.smart_tag: |
| 468 | if not self.manifest.manifest_server: | 493 | if not self.manifest.manifest_server: |
| 469 | print('error: cannot smart sync: no manifest server defined in' | 494 | print('error: cannot smart sync: no manifest server defined in ' |
| 470 | 'manifest', file=sys.stderr) | 495 | 'manifest', file=sys.stderr) |
| 471 | sys.exit(1) | 496 | sys.exit(1) |
| 472 | 497 | ||
| 473 | manifest_server = self.manifest.manifest_server | 498 | manifest_server = self.manifest.manifest_server |
| 499 | if not opt.quiet: | ||
| 500 | print('Using manifest server %s' % manifest_server) | ||
| 474 | 501 | ||
| 475 | if not '@' in manifest_server: | 502 | if not '@' in manifest_server: |
| 476 | username = None | 503 | username = None |
| @@ -486,7 +513,7 @@ later is required to fix a server side protocol bug. | |||
| 486 | file=sys.stderr) | 513 | file=sys.stderr) |
| 487 | else: | 514 | else: |
| 488 | try: | 515 | try: |
| 489 | parse_result = urlparse.urlparse(manifest_server) | 516 | parse_result = urllib.parse.urlparse(manifest_server) |
| 490 | if parse_result.hostname: | 517 | if parse_result.hostname: |
| 491 | username, _account, password = \ | 518 | username, _account, password = \ |
| 492 | info.authenticators(parse_result.hostname) | 519 | info.authenticators(parse_result.hostname) |
| @@ -504,7 +531,7 @@ later is required to fix a server side protocol bug. | |||
| 504 | 1) | 531 | 1) |
| 505 | 532 | ||
| 506 | try: | 533 | try: |
| 507 | server = xmlrpclib.Server(manifest_server) | 534 | server = xmlrpc.client.Server(manifest_server) |
| 508 | if opt.smart_sync: | 535 | if opt.smart_sync: |
| 509 | p = self.manifest.manifestProject | 536 | p = self.manifest.manifestProject |
| 510 | b = p.GetBranch(p.CurrentBranch) | 537 | b = p.GetBranch(p.CurrentBranch) |
| @@ -513,8 +540,7 @@ later is required to fix a server side protocol bug. | |||
| 513 | branch = branch[len(R_HEADS):] | 540 | branch = branch[len(R_HEADS):] |
| 514 | 541 | ||
| 515 | env = os.environ.copy() | 542 | env = os.environ.copy() |
| 516 | if (env.has_key('TARGET_PRODUCT') and | 543 | if 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env: |
| 517 | env.has_key('TARGET_BUILD_VARIANT')): | ||
| 518 | target = '%s-%s' % (env['TARGET_PRODUCT'], | 544 | target = '%s-%s' % (env['TARGET_PRODUCT'], |
| 519 | env['TARGET_BUILD_VARIANT']) | 545 | env['TARGET_BUILD_VARIANT']) |
| 520 | [success, manifest_str] = server.GetApprovedManifest(branch, target) | 546 | [success, manifest_str] = server.GetApprovedManifest(branch, target) |
| @@ -538,15 +564,16 @@ later is required to fix a server side protocol bug. | |||
| 538 | print('error: cannot write manifest to %s' % manifest_path, | 564 | print('error: cannot write manifest to %s' % manifest_path, |
| 539 | file=sys.stderr) | 565 | file=sys.stderr) |
| 540 | sys.exit(1) | 566 | sys.exit(1) |
| 541 | self.manifest.Override(manifest_name) | 567 | self._ReloadManifest(manifest_name) |
| 542 | else: | 568 | else: |
| 543 | print('error: %s' % manifest_str, file=sys.stderr) | 569 | print('error: manifest server RPC call failed: %s' % |
| 570 | manifest_str, file=sys.stderr) | ||
| 544 | sys.exit(1) | 571 | sys.exit(1) |
| 545 | except (socket.error, IOError, xmlrpclib.Fault) as e: | 572 | except (socket.error, IOError, xmlrpc.client.Fault) as e: |
| 546 | print('error: cannot connect to manifest server %s:\n%s' | 573 | print('error: cannot connect to manifest server %s:\n%s' |
| 547 | % (self.manifest.manifest_server, e), file=sys.stderr) | 574 | % (self.manifest.manifest_server, e), file=sys.stderr) |
| 548 | sys.exit(1) | 575 | sys.exit(1) |
| 549 | except xmlrpclib.ProtocolError as e: | 576 | except xmlrpc.client.ProtocolError as e: |
| 550 | print('error: cannot connect to manifest server %s:\n%d %s' | 577 | print('error: cannot connect to manifest server %s:\n%d %s' |
| 551 | % (self.manifest.manifest_server, e.errcode, e.errmsg), | 578 | % (self.manifest.manifest_server, e.errcode, e.errmsg), |
| 552 | file=sys.stderr) | 579 | file=sys.stderr) |
| @@ -571,7 +598,7 @@ later is required to fix a server side protocol bug. | |||
| 571 | mp.Sync_LocalHalf(syncbuf) | 598 | mp.Sync_LocalHalf(syncbuf) |
| 572 | if not syncbuf.Finish(): | 599 | if not syncbuf.Finish(): |
| 573 | sys.exit(1) | 600 | sys.exit(1) |
| 574 | self.manifest._Unload() | 601 | self._ReloadManifest(manifest_name) |
| 575 | if opt.jobs is None: | 602 | if opt.jobs is None: |
| 576 | self.jobs = self.manifest.default.sync_j | 603 | self.jobs = self.manifest.default.sync_j |
| 577 | all_projects = self.GetProjects(args, | 604 | all_projects = self.GetProjects(args, |
| @@ -596,7 +623,7 @@ later is required to fix a server side protocol bug. | |||
| 596 | # Iteratively fetch missing and/or nested unregistered submodules | 623 | # Iteratively fetch missing and/or nested unregistered submodules |
| 597 | previously_missing_set = set() | 624 | previously_missing_set = set() |
| 598 | while True: | 625 | while True: |
| 599 | self.manifest._Unload() | 626 | self._ReloadManifest(manifest_name) |
| 600 | all_projects = self.GetProjects(args, | 627 | all_projects = self.GetProjects(args, |
| 601 | missing_ok=True, | 628 | missing_ok=True, |
| 602 | submodules_ok=opt.fetch_submodules) | 629 | submodules_ok=opt.fetch_submodules) |
diff --git a/subcmds/upload.py b/subcmds/upload.py index e314032a..8d801e08 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
| @@ -21,19 +21,26 @@ import sys | |||
| 21 | from command import InteractiveCommand | 21 | from command import InteractiveCommand |
| 22 | from editor import Editor | 22 | from editor import Editor |
| 23 | from error import HookError, UploadError | 23 | from error import HookError, UploadError |
| 24 | from git_command import GitCommand | ||
| 24 | from project import RepoHook | 25 | from project import RepoHook |
| 25 | 26 | ||
| 27 | from pyversion import is_python3 | ||
| 28 | if not is_python3(): | ||
| 29 | # pylint:disable=W0622 | ||
| 30 | input = raw_input | ||
| 31 | # pylint:enable=W0622 | ||
| 32 | |||
| 26 | UNUSUAL_COMMIT_THRESHOLD = 5 | 33 | UNUSUAL_COMMIT_THRESHOLD = 5 |
| 27 | 34 | ||
| 28 | def _ConfirmManyUploads(multiple_branches=False): | 35 | def _ConfirmManyUploads(multiple_branches=False): |
| 29 | if multiple_branches: | 36 | if multiple_branches: |
| 30 | print('ATTENTION: One or more branches has an unusually high number' | 37 | print('ATTENTION: One or more branches has an unusually high number ' |
| 31 | 'of commits.') | 38 | 'of commits.') |
| 32 | else: | 39 | else: |
| 33 | print('ATTENTION: You are uploading an unusually high number of commits.') | 40 | print('ATTENTION: You are uploading an unusually high number of commits.') |
| 34 | print('YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across' | 41 | print('YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across ' |
| 35 | 'branches?)') | 42 | 'branches?)') |
| 36 | answer = raw_input("If you are sure you intend to do this, type 'yes': ").strip() | 43 | answer = input("If you are sure you intend to do this, type 'yes': ").strip() |
| 37 | return answer == "yes" | 44 | return answer == "yes" |
| 38 | 45 | ||
| 39 | def _die(fmt, *args): | 46 | def _die(fmt, *args): |
| @@ -140,6 +147,10 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 140 | p.add_option('-d', '--draft', | 147 | p.add_option('-d', '--draft', |
| 141 | action='store_true', dest='draft', default=False, | 148 | action='store_true', dest='draft', default=False, |
| 142 | help='If specified, upload as a draft.') | 149 | help='If specified, upload as a draft.') |
| 150 | p.add_option('-D', '--destination', '--dest', | ||
| 151 | type='string', action='store', dest='dest_branch', | ||
| 152 | metavar='BRANCH', | ||
| 153 | help='Submit for review on this target branch.') | ||
| 143 | 154 | ||
| 144 | # Options relating to upload hook. Note that verify and no-verify are NOT | 155 | # Options relating to upload hook. Note that verify and no-verify are NOT |
| 145 | # opposites of each other, which is why they store to different locations. | 156 | # opposites of each other, which is why they store to different locations. |
| @@ -179,7 +190,8 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 179 | date = branch.date | 190 | date = branch.date |
| 180 | commit_list = branch.commits | 191 | commit_list = branch.commits |
| 181 | 192 | ||
| 182 | print('Upload project %s/ to remote branch %s:' % (project.relpath, project.revisionExpr)) | 193 | destination = opt.dest_branch or project.dest_branch or project.revisionExpr |
| 194 | print('Upload project %s/ to remote branch %s:' % (project.relpath, destination)) | ||
| 183 | print(' branch %s (%2d commit%s, %s):' % ( | 195 | print(' branch %s (%2d commit%s, %s):' % ( |
| 184 | name, | 196 | name, |
| 185 | len(commit_list), | 197 | len(commit_list), |
| @@ -213,18 +225,21 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 213 | 225 | ||
| 214 | b = {} | 226 | b = {} |
| 215 | for branch in avail: | 227 | for branch in avail: |
| 228 | if branch is None: | ||
| 229 | continue | ||
| 216 | name = branch.name | 230 | name = branch.name |
| 217 | date = branch.date | 231 | date = branch.date |
| 218 | commit_list = branch.commits | 232 | commit_list = branch.commits |
| 219 | 233 | ||
| 220 | if b: | 234 | if b: |
| 221 | script.append('#') | 235 | script.append('#') |
| 236 | destination = opt.dest_branch or project.dest_branch or project.revisionExpr | ||
| 222 | script.append('# branch %s (%2d commit%s, %s) to remote branch %s:' % ( | 237 | script.append('# branch %s (%2d commit%s, %s) to remote branch %s:' % ( |
| 223 | name, | 238 | name, |
| 224 | len(commit_list), | 239 | len(commit_list), |
| 225 | len(commit_list) != 1 and 's' or '', | 240 | len(commit_list) != 1 and 's' or '', |
| 226 | date, | 241 | date, |
| 227 | project.revisionExpr)) | 242 | destination)) |
| 228 | for commit in commit_list: | 243 | for commit in commit_list: |
| 229 | script.append('# %s' % commit) | 244 | script.append('# %s' % commit) |
| 230 | b[name] = branch | 245 | b[name] = branch |
| @@ -330,7 +345,21 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 330 | key = 'review.%s.uploadtopic' % branch.project.remote.review | 345 | key = 'review.%s.uploadtopic' % branch.project.remote.review |
| 331 | opt.auto_topic = branch.project.config.GetBoolean(key) | 346 | opt.auto_topic = branch.project.config.GetBoolean(key) |
| 332 | 347 | ||
| 333 | branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft) | 348 | destination = opt.dest_branch or branch.project.dest_branch |
| 349 | |||
| 350 | # Make sure our local branch is not setup to track a different remote branch | ||
| 351 | merge_branch = self._GetMergeBranch(branch.project) | ||
| 352 | full_dest = 'refs/heads/%s' % destination | ||
| 353 | if not opt.dest_branch and merge_branch and merge_branch != full_dest: | ||
| 354 | print('merge branch %s does not match destination branch %s' | ||
| 355 | % (merge_branch, full_dest)) | ||
| 356 | print('skipping upload.') | ||
| 357 | print('Please use `--destination %s` if this is intentional' | ||
| 358 | % destination) | ||
| 359 | branch.uploaded = False | ||
| 360 | continue | ||
| 361 | |||
| 362 | branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft, dest_branch=destination) | ||
| 334 | branch.uploaded = True | 363 | branch.uploaded = True |
| 335 | except UploadError as e: | 364 | except UploadError as e: |
| 336 | branch.error = e | 365 | branch.error = e |
| @@ -364,6 +393,21 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 364 | if have_errors: | 393 | if have_errors: |
| 365 | sys.exit(1) | 394 | sys.exit(1) |
| 366 | 395 | ||
| 396 | def _GetMergeBranch(self, project): | ||
| 397 | p = GitCommand(project, | ||
| 398 | ['rev-parse', '--abbrev-ref', 'HEAD'], | ||
| 399 | capture_stdout = True, | ||
| 400 | capture_stderr = True) | ||
| 401 | p.Wait() | ||
| 402 | local_branch = p.stdout.strip() | ||
| 403 | p = GitCommand(project, | ||
| 404 | ['config', '--get', 'branch.%s.merge' % local_branch], | ||
| 405 | capture_stdout = True, | ||
| 406 | capture_stderr = True) | ||
| 407 | p.Wait() | ||
| 408 | merge_branch = p.stdout.strip() | ||
| 409 | return merge_branch | ||
| 410 | |||
| 367 | def Execute(self, opt, args): | 411 | def Execute(self, opt, args): |
| 368 | project_list = self.GetProjects(args) | 412 | project_list = self.GetProjects(args) |
| 369 | pending = [] | 413 | pending = [] |
