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 = [] |