summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.project2
-rw-r--r--.pydevproject2
-rw-r--r--.pylintrc2
-rw-r--r--command.py15
-rw-r--r--docs/manifest-format.txt12
-rw-r--r--git_config.py17
-rw-r--r--git_refs.py9
-rwxr-xr-xmain.py7
-rw-r--r--manifest_xml.py81
-rw-r--r--project.py69
-rwxr-xr-xrepo17
-rw-r--r--subcmds/__init__.py4
-rw-r--r--subcmds/branches.py5
-rw-r--r--subcmds/forall.py14
-rw-r--r--subcmds/help.py10
-rw-r--r--subcmds/info.py2
-rw-r--r--subcmds/init.py7
-rw-r--r--subcmds/list.py31
-rw-r--r--subcmds/overview.py2
-rw-r--r--subcmds/stage.py8
-rw-r--r--subcmds/status.py11
-rw-r--r--subcmds/sync.py47
-rw-r--r--subcmds/upload.py7
23 files changed, 257 insertions, 124 deletions
diff --git a/.project b/.project
index 67e4a0f1..3aefb86b 100644
--- a/.project
+++ b/.project
@@ -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>
diff --git a/.pylintrc b/.pylintrc
index 9e8882ee..10b842da 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -53,7 +53,7 @@ load-plugins=
53enable=RP0004 53enable=RP0004
54 54
55# Disable the message(s) with the given id(s). 55# Disable the message(s) with the given id(s).
56disable=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 56disable=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
57 57
58[REPORTS] 58[REPORTS]
59 59
diff --git a/command.py b/command.py
index 96d7848f..287f4d30 100644
--- a/command.py
+++ b/command.py
@@ -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..59f6a2fd 100644
--- a/docs/manifest-format.txt
+++ b/docs/manifest-format.txt
@@ -56,6 +56,8 @@ following DTD:
56 <!ATTLIST project sync-c CDATA #IMPLIED> 56 <!ATTLIST project sync-c CDATA #IMPLIED>
57 <!ATTLIST project sync-s CDATA #IMPLIED> 57 <!ATTLIST project sync-s CDATA #IMPLIED>
58 <!ATTLIST project upstream CDATA #IMPLIED> 58 <!ATTLIST project upstream CDATA #IMPLIED>
59 <!ATTLIST project clone-depth CDATA #IMPLIED>
60 <!ATTLIST project force-path CDATA #IMPLIED>
59 61
60 <!ELEMENT annotation (EMPTY)> 62 <!ELEMENT annotation (EMPTY)>
61 <!ATTLIST annotation name CDATA #REQUIRED> 63 <!ATTLIST annotation name CDATA #REQUIRED>
@@ -222,6 +224,16 @@ Attribute `upstream`: Name of the Git branch in which a sha1
222can be found. Used when syncing a revision locked manifest in 224can be found. Used when syncing a revision locked manifest in
223-c mode to avoid having to sync the entire ref space. 225-c mode to avoid having to sync the entire ref space.
224 226
227Attribute `clone-depth`: Set the depth to use when fetching this
228project. If specified, this value will override any value given
229to repo init with the --depth option on the command line.
230
231Attribute `force-path`: Set to true to force this project to create the
232local mirror repository according to its `path` attribute (if supplied)
233rather than the `name` attribute. This attribute only applies to the
234local mirrors syncing, it will be ignored when syncing the projects in a
235client working directory.
236
225Element annotation 237Element annotation
226------------------ 238------------------
227 239
diff --git a/git_config.py b/git_config.py
index 56cc6a24..9524df9b 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
16from __future__ import print_function 16from __future__ import print_function
17import cPickle 17
18import os 18import os
19import pickle
19import re 20import re
20import subprocess 21import subprocess
21import sys 22import sys
@@ -262,7 +263,7 @@ class GitConfig(object):
262 Trace(': unpickle %s', self.file) 263 Trace(': unpickle %s', self.file)
263 fd = open(self._pickle, 'rb') 264 fd = open(self._pickle, 'rb')
264 try: 265 try:
265 return cPickle.load(fd) 266 return pickle.load(fd)
266 finally: 267 finally:
267 fd.close() 268 fd.close()
268 except EOFError: 269 except EOFError:
@@ -271,7 +272,7 @@ class GitConfig(object):
271 except IOError: 272 except IOError:
272 os.remove(self._pickle) 273 os.remove(self._pickle)
273 return None 274 return None
274 except cPickle.PickleError: 275 except pickle.PickleError:
275 os.remove(self._pickle) 276 os.remove(self._pickle)
276 return None 277 return None
277 278
@@ -279,13 +280,13 @@ class GitConfig(object):
279 try: 280 try:
280 fd = open(self._pickle, 'wb') 281 fd = open(self._pickle, 'wb')
281 try: 282 try:
282 cPickle.dump(cache, fd, cPickle.HIGHEST_PROTOCOL) 283 pickle.dump(cache, fd, pickle.HIGHEST_PROTOCOL)
283 finally: 284 finally:
284 fd.close() 285 fd.close()
285 except IOError: 286 except IOError:
286 if os.path.exists(self._pickle): 287 if os.path.exists(self._pickle):
287 os.remove(self._pickle) 288 os.remove(self._pickle)
288 except cPickle.PickleError: 289 except pickle.PickleError:
289 if os.path.exists(self._pickle): 290 if os.path.exists(self._pickle):
290 os.remove(self._pickle) 291 os.remove(self._pickle)
291 292
@@ -537,8 +538,8 @@ class Remote(object):
537 self.url = self._Get('url') 538 self.url = self._Get('url')
538 self.review = self._Get('review') 539 self.review = self._Get('review')
539 self.projectname = self._Get('projectname') 540 self.projectname = self._Get('projectname')
540 self.fetch = map(RefSpec.FromString, 541 self.fetch = list(map(RefSpec.FromString,
541 self._Get('fetch', all_keys=True)) 542 self._Get('fetch', all_keys=True)))
542 self._review_url = None 543 self._review_url = None
543 544
544 def _InsteadOf(self): 545 def _InsteadOf(self):
@@ -657,7 +658,7 @@ class Remote(object):
657 self._Set('url', self.url) 658 self._Set('url', self.url)
658 self._Set('review', self.review) 659 self._Set('review', self.review)
659 self._Set('projectname', self.projectname) 660 self._Set('projectname', self.projectname)
660 self._Set('fetch', map(str, self.fetch)) 661 self._Set('fetch', list(map(str, self.fetch)))
661 662
662 def _Set(self, key, value): 663 def _Set(self, key, value):
663 key = 'remote.%s.%s' % (self.name, key) 664 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]
diff --git a/main.py b/main.py
index 9cc2639a..49d24823 100755
--- a/main.py
+++ b/main.py
@@ -50,6 +50,11 @@ from pager import RunPager
50 50
51from subcmds import all_commands 51from subcmds import all_commands
52 52
53try:
54 input = raw_input
55except NameError:
56 pass
57
53global_options = optparse.OptionParser( 58global_options = optparse.OptionParser(
54 usage="repo [-p|--paginate|--no-pager] COMMAND [ARGS]" 59 usage="repo [-p|--paginate|--no-pager] COMMAND [ARGS]"
55 ) 60 )
@@ -286,7 +291,7 @@ def _AddPasswordFromUserInput(handler, msg, req):
286 if user is None: 291 if user is None:
287 print(msg) 292 print(msg)
288 try: 293 try:
289 user = raw_input('User: ') 294 user = input('User: ')
290 password = getpass.getpass() 295 password = getpass.getpass()
291 except KeyboardInterrupt: 296 except KeyboardInterrupt:
292 return 297 return
diff --git a/manifest_xml.py b/manifest_xml.py
index 50417c60..cc441dc8 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -18,7 +18,15 @@ import itertools
18import os 18import os
19import re 19import re
20import sys 20import sys
21import urlparse 21try:
22 # For python3
23 import urllib.parse
24except ImportError:
25 # For python2
26 import imp
27 import urlparse
28 urllib = imp.new_module('urllib')
29 urllib.parse = urlparse
22import xml.dom.minidom 30import xml.dom.minidom
23 31
24from git_config import GitConfig 32from git_config import GitConfig
@@ -30,8 +38,8 @@ MANIFEST_FILE_NAME = 'manifest.xml'
30LOCAL_MANIFEST_NAME = 'local_manifest.xml' 38LOCAL_MANIFEST_NAME = 'local_manifest.xml'
31LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' 39LOCAL_MANIFESTS_DIR_NAME = 'local_manifests'
32 40
33urlparse.uses_relative.extend(['ssh', 'git']) 41urllib.parse.uses_relative.extend(['ssh', 'git'])
34urlparse.uses_netloc.extend(['ssh', 'git']) 42urllib.parse.uses_netloc.extend(['ssh', 'git'])
35 43
36class _Default(object): 44class _Default(object):
37 """Project defaults within the manifest.""" 45 """Project defaults within the manifest."""
@@ -73,7 +81,7 @@ class _XmlRemote(object):
73 # ie, if manifestUrl is of the form <hostname:port> 81 # ie, if manifestUrl is of the form <hostname:port>
74 if manifestUrl.find(':') != manifestUrl.find('/') - 1: 82 if manifestUrl.find(':') != manifestUrl.find('/') - 1:
75 manifestUrl = 'gopher://' + manifestUrl 83 manifestUrl = 'gopher://' + manifestUrl
76 url = urlparse.urljoin(manifestUrl, url) 84 url = urllib.parse.urljoin(manifestUrl, url)
77 url = re.sub(r'^gopher://', '', url) 85 url = re.sub(r'^gopher://', '', url)
78 if p: 86 if p:
79 url = 'persistent-' + url 87 url = 'persistent-' + url
@@ -82,8 +90,6 @@ class _XmlRemote(object):
82 def ToRemoteSpec(self, projectName): 90 def ToRemoteSpec(self, projectName):
83 url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName 91 url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName
84 remoteName = self.name 92 remoteName = self.name
85 if self.remoteAlias:
86 remoteName = self.remoteAlias
87 return RemoteSpec(remoteName, url, self.reviewUrl) 93 return RemoteSpec(remoteName, url, self.reviewUrl)
88 94
89class XmlManifest(object): 95class XmlManifest(object):
@@ -164,10 +170,8 @@ class XmlManifest(object):
164 notice_element.appendChild(doc.createTextNode(indented_notice)) 170 notice_element.appendChild(doc.createTextNode(indented_notice))
165 171
166 d = self.default 172 d = self.default
167 sort_remotes = list(self.remotes.keys())
168 sort_remotes.sort()
169 173
170 for r in sort_remotes: 174 for r in sorted(self.remotes):
171 self._RemoteToXml(self.remotes[r], doc, root) 175 self._RemoteToXml(self.remotes[r], doc, root)
172 if self.remotes: 176 if self.remotes:
173 root.appendChild(doc.createTextNode('')) 177 root.appendChild(doc.createTextNode(''))
@@ -259,12 +263,11 @@ class XmlManifest(object):
259 e.setAttribute('sync-s', 'true') 263 e.setAttribute('sync-s', 'true')
260 264
261 if p.subprojects: 265 if p.subprojects:
262 sort_projects = [subp.name for subp in p.subprojects] 266 sort_projects = list(sorted([subp.name for subp in p.subprojects]))
263 sort_projects.sort()
264 output_projects(p, e, sort_projects) 267 output_projects(p, e, sort_projects)
265 268
266 sort_projects = [key for key in self.projects.keys() 269 sort_projects = list(sorted([key for key, value in self.projects.items()
267 if not self.projects[key].parent] 270 if not value.parent]))
268 sort_projects.sort() 271 sort_projects.sort()
269 output_projects(None, root, sort_projects) 272 output_projects(None, root, sort_projects)
270 273
@@ -388,9 +391,8 @@ class XmlManifest(object):
388 name = self._reqatt(node, 'name') 391 name = self._reqatt(node, 'name')
389 fp = os.path.join(include_root, name) 392 fp = os.path.join(include_root, name)
390 if not os.path.isfile(fp): 393 if not os.path.isfile(fp):
391 raise ManifestParseError, \ 394 raise ManifestParseError("include %s doesn't exist or isn't a file"
392 "include %s doesn't exist or isn't a file" % \ 395 % (name,))
393 (name,)
394 try: 396 try:
395 nodes.extend(self._ParseManifestXml(fp, include_root)) 397 nodes.extend(self._ParseManifestXml(fp, include_root))
396 # should isolate this to the exact exception, but that's 398 # should isolate this to the exact exception, but that's
@@ -496,7 +498,7 @@ class XmlManifest(object):
496 name = None 498 name = None
497 m_url = m.GetRemote(m.remote.name).url 499 m_url = m.GetRemote(m.remote.name).url
498 if m_url.endswith('/.git'): 500 if m_url.endswith('/.git'):
499 raise ManifestParseError, 'refusing to mirror %s' % m_url 501 raise ManifestParseError('refusing to mirror %s' % m_url)
500 502
501 if self._default and self._default.remote: 503 if self._default and self._default.remote:
502 url = self._default.remote.resolvedFetchUrl 504 url = self._default.remote.resolvedFetchUrl
@@ -590,7 +592,7 @@ class XmlManifest(object):
590 592
591 # Figure out minimum indentation, skipping the first line (the same line 593 # Figure out minimum indentation, skipping the first line (the same line
592 # as the <notice> tag)... 594 # as the <notice> tag)...
593 minIndent = sys.maxint 595 minIndent = sys.maxsize
594 lines = notice.splitlines() 596 lines = notice.splitlines()
595 for line in lines[1:]: 597 for line in lines[1:]:
596 lstrippedLine = line.lstrip() 598 lstrippedLine = line.lstrip()
@@ -629,25 +631,22 @@ class XmlManifest(object):
629 if remote is None: 631 if remote is None:
630 remote = self._default.remote 632 remote = self._default.remote
631 if remote is None: 633 if remote is None:
632 raise ManifestParseError, \ 634 raise ManifestParseError("no remote for project %s within %s" %
633 "no remote for project %s within %s" % \ 635 (name, self.manifestFile))
634 (name, self.manifestFile)
635 636
636 revisionExpr = node.getAttribute('revision') 637 revisionExpr = node.getAttribute('revision')
637 if not revisionExpr: 638 if not revisionExpr:
638 revisionExpr = self._default.revisionExpr 639 revisionExpr = self._default.revisionExpr
639 if not revisionExpr: 640 if not revisionExpr:
640 raise ManifestParseError, \ 641 raise ManifestParseError("no revision for project %s within %s" %
641 "no revision for project %s within %s" % \ 642 (name, self.manifestFile))
642 (name, self.manifestFile)
643 643
644 path = node.getAttribute('path') 644 path = node.getAttribute('path')
645 if not path: 645 if not path:
646 path = name 646 path = name
647 if path.startswith('/'): 647 if path.startswith('/'):
648 raise ManifestParseError, \ 648 raise ManifestParseError("project %s path cannot be absolute in %s" %
649 "project %s path cannot be absolute in %s" % \ 649 (name, self.manifestFile))
650 (name, self.manifestFile)
651 650
652 rebase = node.getAttribute('rebase') 651 rebase = node.getAttribute('rebase')
653 if not rebase: 652 if not rebase:
@@ -667,6 +666,16 @@ class XmlManifest(object):
667 else: 666 else:
668 sync_s = sync_s.lower() in ("yes", "true", "1") 667 sync_s = sync_s.lower() in ("yes", "true", "1")
669 668
669 clone_depth = node.getAttribute('clone-depth')
670 if clone_depth:
671 try:
672 clone_depth = int(clone_depth)
673 if clone_depth <= 0:
674 raise ValueError()
675 except ValueError:
676 raise ManifestParseError('invalid clone-depth %s in %s' %
677 (clone_depth, self.manifestFile))
678
670 upstream = node.getAttribute('upstream') 679 upstream = node.getAttribute('upstream')
671 680
672 groups = '' 681 groups = ''
@@ -682,6 +691,10 @@ class XmlManifest(object):
682 default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath] 691 default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath]
683 groups.extend(set(default_groups).difference(groups)) 692 groups.extend(set(default_groups).difference(groups))
684 693
694 if self.IsMirror and node.hasAttribute('force-path'):
695 if node.getAttribute('force-path').lower() in ("yes", "true", "1"):
696 gitdir = os.path.join(self.topdir, '%s.git' % path)
697
685 project = Project(manifest = self, 698 project = Project(manifest = self,
686 name = name, 699 name = name,
687 remote = remote.ToRemoteSpec(name), 700 remote = remote.ToRemoteSpec(name),
@@ -694,6 +707,7 @@ class XmlManifest(object):
694 groups = groups, 707 groups = groups,
695 sync_c = sync_c, 708 sync_c = sync_c,
696 sync_s = sync_s, 709 sync_s = sync_s,
710 clone_depth = clone_depth,
697 upstream = upstream, 711 upstream = upstream,
698 parent = parent) 712 parent = parent)
699 713
@@ -751,7 +765,8 @@ class XmlManifest(object):
751 except ManifestParseError: 765 except ManifestParseError:
752 keep = "true" 766 keep = "true"
753 if keep != "true" and keep != "false": 767 if keep != "true" and keep != "false":
754 raise ManifestParseError, "optional \"keep\" attribute must be \"true\" or \"false\"" 768 raise ManifestParseError('optional "keep" attribute must be '
769 '"true" or "false"')
755 project.AddAnnotation(name, value, keep) 770 project.AddAnnotation(name, value, keep)
756 771
757 def _get_remote(self, node): 772 def _get_remote(self, node):
@@ -761,9 +776,8 @@ class XmlManifest(object):
761 776
762 v = self._remotes.get(name) 777 v = self._remotes.get(name)
763 if not v: 778 if not v:
764 raise ManifestParseError, \ 779 raise ManifestParseError("remote %s not defined in %s" %
765 "remote %s not defined in %s" % \ 780 (name, self.manifestFile))
766 (name, self.manifestFile)
767 return v 781 return v
768 782
769 def _reqatt(self, node, attname): 783 def _reqatt(self, node, attname):
@@ -772,7 +786,6 @@ class XmlManifest(object):
772 """ 786 """
773 v = node.getAttribute(attname) 787 v = node.getAttribute(attname)
774 if not v: 788 if not v:
775 raise ManifestParseError, \ 789 raise ManifestParseError("no %s in <%s> within %s" %
776 "no %s in <%s> within %s" % \ 790 (attname, node.nodeName, self.manifestFile))
777 (attname, node.nodeName, self.manifestFile)
778 return v 791 return v
diff --git a/project.py b/project.py
index 792ad4c4..516d3b6c 100644
--- a/project.py
+++ b/project.py
@@ -36,6 +36,11 @@ from trace import IsTrace, Trace
36 36
37from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M 37from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
38 38
39try:
40 input = raw_input
41except NameError:
42 pass
43
39def _lwrite(path, content): 44def _lwrite(path, content):
40 lock = '%s.lock' % path 45 lock = '%s.lock' % path
41 46
@@ -78,7 +83,7 @@ def _ProjectHooks():
78 if _project_hook_list is None: 83 if _project_hook_list is None:
79 d = os.path.abspath(os.path.dirname(__file__)) 84 d = os.path.abspath(os.path.dirname(__file__))
80 d = os.path.join(d , 'hooks') 85 d = os.path.join(d , 'hooks')
81 _project_hook_list = map(lambda x: os.path.join(d, x), os.listdir(d)) 86 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
82 return _project_hook_list 87 return _project_hook_list
83 88
84 89
@@ -361,7 +366,7 @@ class RepoHook(object):
361 'Do you want to allow this script to run ' 366 'Do you want to allow this script to run '
362 '(yes/yes-never-ask-again/NO)? ') % ( 367 '(yes/yes-never-ask-again/NO)? ') % (
363 self._GetMustVerb(), self._script_fullpath) 368 self._GetMustVerb(), self._script_fullpath)
364 response = raw_input(prompt).lower() 369 response = input(prompt).lower()
365 print() 370 print()
366 371
367 # User is doing a one-time approval. 372 # User is doing a one-time approval.
@@ -488,6 +493,7 @@ class Project(object):
488 groups = None, 493 groups = None,
489 sync_c = False, 494 sync_c = False,
490 sync_s = False, 495 sync_s = False,
496 clone_depth = None,
491 upstream = None, 497 upstream = None,
492 parent = None, 498 parent = None,
493 is_derived = False): 499 is_derived = False):
@@ -533,6 +539,7 @@ class Project(object):
533 self.groups = groups 539 self.groups = groups
534 self.sync_c = sync_c 540 self.sync_c = sync_c
535 self.sync_s = sync_s 541 self.sync_s = sync_s
542 self.clone_depth = clone_depth
536 self.upstream = upstream 543 self.upstream = upstream
537 self.parent = parent 544 self.parent = parent
538 self.is_derived = is_derived 545 self.is_derived = is_derived
@@ -644,7 +651,7 @@ class Project(object):
644 all_refs = self._allrefs 651 all_refs = self._allrefs
645 heads = {} 652 heads = {}
646 653
647 for name, ref_id in all_refs.iteritems(): 654 for name, ref_id in all_refs.items():
648 if name.startswith(R_HEADS): 655 if name.startswith(R_HEADS):
649 name = name[len(R_HEADS):] 656 name = name[len(R_HEADS):]
650 b = self.GetBranch(name) 657 b = self.GetBranch(name)
@@ -653,7 +660,7 @@ class Project(object):
653 b.revision = ref_id 660 b.revision = ref_id
654 heads[name] = b 661 heads[name] = b
655 662
656 for name, ref_id in all_refs.iteritems(): 663 for name, ref_id in all_refs.items():
657 if name.startswith(R_PUB): 664 if name.startswith(R_PUB):
658 name = name[len(R_PUB):] 665 name = name[len(R_PUB):]
659 b = heads.get(name) 666 b = heads.get(name)
@@ -672,9 +679,14 @@ class Project(object):
672 project_groups: "all,group1,group2" 679 project_groups: "all,group1,group2"
673 manifest_groups: "-group1,group2" 680 manifest_groups: "-group1,group2"
674 the project will be matched. 681 the project will be matched.
682
683 The special manifest group "default" will match any project that
684 does not have the special project group "notdefault"
675 """ 685 """
676 expanded_manifest_groups = manifest_groups or ['all', '-notdefault'] 686 expanded_manifest_groups = manifest_groups or ['default']
677 expanded_project_groups = ['all'] + (self.groups or []) 687 expanded_project_groups = ['all'] + (self.groups or [])
688 if not 'notdefault' in expanded_project_groups:
689 expanded_project_groups += ['default']
678 690
679 matched = False 691 matched = False
680 for group in expanded_manifest_groups: 692 for group in expanded_manifest_groups:
@@ -754,10 +766,7 @@ class Project(object):
754 paths.extend(df.keys()) 766 paths.extend(df.keys())
755 paths.extend(do) 767 paths.extend(do)
756 768
757 paths = list(set(paths)) 769 for p in sorted(set(paths)):
758 paths.sort()
759
760 for p in paths:
761 try: 770 try:
762 i = di[p] 771 i = di[p]
763 except KeyError: 772 except KeyError:
@@ -849,13 +858,13 @@ class Project(object):
849 all_refs = self._allrefs 858 all_refs = self._allrefs
850 heads = set() 859 heads = set()
851 canrm = {} 860 canrm = {}
852 for name, ref_id in all_refs.iteritems(): 861 for name, ref_id in all_refs.items():
853 if name.startswith(R_HEADS): 862 if name.startswith(R_HEADS):
854 heads.add(name) 863 heads.add(name)
855 elif name.startswith(R_PUB): 864 elif name.startswith(R_PUB):
856 canrm[name] = ref_id 865 canrm[name] = ref_id
857 866
858 for name, ref_id in canrm.iteritems(): 867 for name, ref_id in canrm.items():
859 n = name[len(R_PUB):] 868 n = name[len(R_PUB):]
860 if R_HEADS + n not in heads: 869 if R_HEADS + n not in heads:
861 self.bare_git.DeleteRef(name, ref_id) 870 self.bare_git.DeleteRef(name, ref_id)
@@ -866,14 +875,14 @@ class Project(object):
866 heads = {} 875 heads = {}
867 pubed = {} 876 pubed = {}
868 877
869 for name, ref_id in self._allrefs.iteritems(): 878 for name, ref_id in self._allrefs.items():
870 if name.startswith(R_HEADS): 879 if name.startswith(R_HEADS):
871 heads[name[len(R_HEADS):]] = ref_id 880 heads[name[len(R_HEADS):]] = ref_id
872 elif name.startswith(R_PUB): 881 elif name.startswith(R_PUB):
873 pubed[name[len(R_PUB):]] = ref_id 882 pubed[name[len(R_PUB):]] = ref_id
874 883
875 ready = [] 884 ready = []
876 for branch, ref_id in heads.iteritems(): 885 for branch, ref_id in heads.items():
877 if branch in pubed and pubed[branch] == ref_id: 886 if branch in pubed and pubed[branch] == ref_id:
878 continue 887 continue
879 if selected_branch and branch != selected_branch: 888 if selected_branch and branch != selected_branch:
@@ -1218,7 +1227,7 @@ class Project(object):
1218 cmd = ['fetch', remote.name] 1227 cmd = ['fetch', remote.name]
1219 cmd.append('refs/changes/%2.2d/%d/%d' \ 1228 cmd.append('refs/changes/%2.2d/%d/%d' \
1220 % (change_id % 100, change_id, patch_id)) 1229 % (change_id % 100, change_id, patch_id))
1221 cmd.extend(map(str, remote.fetch)) 1230 cmd.extend(list(map(str, remote.fetch)))
1222 if GitCommand(self, cmd, bare=True).Wait() != 0: 1231 if GitCommand(self, cmd, bare=True).Wait() != 0:
1223 return None 1232 return None
1224 return DownloadedChange(self, 1233 return DownloadedChange(self,
@@ -1607,7 +1616,7 @@ class Project(object):
1607 ids = set(all_refs.values()) 1616 ids = set(all_refs.values())
1608 tmp = set() 1617 tmp = set()
1609 1618
1610 for r, ref_id in GitRefs(ref_dir).all.iteritems(): 1619 for r, ref_id in GitRefs(ref_dir).all.items():
1611 if r not in all_refs: 1620 if r not in all_refs:
1612 if r.startswith(R_TAGS) or remote.WritesTo(r): 1621 if r.startswith(R_TAGS) or remote.WritesTo(r):
1613 all_refs[r] = ref_id 1622 all_refs[r] = ref_id
@@ -1622,13 +1631,10 @@ class Project(object):
1622 ids.add(ref_id) 1631 ids.add(ref_id)
1623 tmp.add(r) 1632 tmp.add(r)
1624 1633
1625 ref_names = list(all_refs.keys())
1626 ref_names.sort()
1627
1628 tmp_packed = '' 1634 tmp_packed = ''
1629 old_packed = '' 1635 old_packed = ''
1630 1636
1631 for r in ref_names: 1637 for r in sorted(all_refs):
1632 line = '%s %s\n' % (all_refs[r], r) 1638 line = '%s %s\n' % (all_refs[r], r)
1633 tmp_packed += line 1639 tmp_packed += line
1634 if r not in tmp: 1640 if r not in tmp:
@@ -1642,7 +1648,10 @@ class Project(object):
1642 1648
1643 # The --depth option only affects the initial fetch; after that we'll do 1649 # The --depth option only affects the initial fetch; after that we'll do
1644 # full fetches of changes. 1650 # full fetches of changes.
1645 depth = self.manifest.manifestProject.config.GetString('repo.depth') 1651 if self.clone_depth:
1652 depth = self.clone_depth
1653 else:
1654 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1646 if depth and initial: 1655 if depth and initial:
1647 cmd.append('--depth=%s' % depth) 1656 cmd.append('--depth=%s' % depth)
1648 1657
@@ -1654,11 +1663,13 @@ class Project(object):
1654 1663
1655 if not current_branch_only: 1664 if not current_branch_only:
1656 # Fetch whole repo 1665 # Fetch whole repo
1657 if no_tags: 1666 # If using depth then we should not get all the tags since they may
1667 # be outside of the depth.
1668 if no_tags or depth:
1658 cmd.append('--no-tags') 1669 cmd.append('--no-tags')
1659 else: 1670 else:
1660 cmd.append('--tags') 1671 cmd.append('--tags')
1661 cmd.append((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')) 1672 cmd.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
1662 elif tag_name is not None: 1673 elif tag_name is not None:
1663 cmd.append('tag') 1674 cmd.append('tag')
1664 cmd.append(tag_name) 1675 cmd.append(tag_name)
@@ -1668,7 +1679,7 @@ class Project(object):
1668 branch = self.upstream 1679 branch = self.upstream
1669 if branch.startswith(R_HEADS): 1680 if branch.startswith(R_HEADS):
1670 branch = branch[len(R_HEADS):] 1681 branch = branch[len(R_HEADS):]
1671 cmd.append((u'+refs/heads/%s:' % branch) + remote.ToLocal('refs/heads/%s' % branch)) 1682 cmd.append(str((u'+refs/heads/%s:' % branch) + remote.ToLocal('refs/heads/%s' % branch)))
1672 1683
1673 ok = False 1684 ok = False
1674 for _i in range(2): 1685 for _i in range(2):
@@ -1702,7 +1713,7 @@ class Project(object):
1702 return ok 1713 return ok
1703 1714
1704 def _ApplyCloneBundle(self, initial=False, quiet=False): 1715 def _ApplyCloneBundle(self, initial=False, quiet=False):
1705 if initial and self.manifest.manifestProject.config.GetString('repo.depth'): 1716 if initial and (self.manifest.manifestProject.config.GetString('repo.depth') or self.clone_depth):
1706 return False 1717 return False
1707 1718
1708 remote = self.GetRemote(self.remote.name) 1719 remote = self.GetRemote(self.remote.name)
@@ -2099,6 +2110,10 @@ class Project(object):
2099 line = fd.read() 2110 line = fd.read()
2100 finally: 2111 finally:
2101 fd.close() 2112 fd.close()
2113 try:
2114 line = line.decode()
2115 except AttributeError:
2116 pass
2102 if line.startswith('ref: '): 2117 if line.startswith('ref: '):
2103 return line[5:-1] 2118 return line[5:-1]
2104 return line[:-1] 2119 return line[:-1]
@@ -2192,7 +2207,7 @@ class Project(object):
2192 if not git_require((1, 7, 2)): 2207 if not git_require((1, 7, 2)):
2193 raise ValueError('cannot set config on command line for %s()' 2208 raise ValueError('cannot set config on command line for %s()'
2194 % name) 2209 % name)
2195 for k, v in config.iteritems(): 2210 for k, v in config.items():
2196 cmdv.append('-c') 2211 cmdv.append('-c')
2197 cmdv.append('%s=%s' % (k, v)) 2212 cmdv.append('%s=%s' % (k, v))
2198 cmdv.append(name) 2213 cmdv.append(name)
@@ -2208,6 +2223,10 @@ class Project(object):
2208 name, 2223 name,
2209 p.stderr)) 2224 p.stderr))
2210 r = p.stdout 2225 r = p.stdout
2226 try:
2227 r = r.decode()
2228 except AttributeError:
2229 pass
2211 if r.endswith('\n') and r.index('\n') == len(r) - 1: 2230 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2212 return r[:-1] 2231 return r[:-1]
2213 return r 2232 return r
diff --git a/repo b/repo
index 6b374f72..791e40cb 100755
--- a/repo
+++ b/repo
@@ -108,6 +108,7 @@ repodir = '.repo' # name of repo's private directory
108S_repo = 'repo' # special repo repository 108S_repo = 'repo' # special repo repository
109S_manifests = 'manifests' # special manifest repository 109S_manifests = 'manifests' # special manifest repository
110REPO_MAIN = S_repo + '/main.py' # main script 110REPO_MAIN = S_repo + '/main.py' # main script
111MIN_PYTHON_VERSION = (2, 6) # minimum supported python version
111 112
112 113
113import optparse 114import optparse
@@ -129,6 +130,19 @@ else:
129 urllib.request = urllib2 130 urllib.request = urllib2
130 urllib.error = urllib2 131 urllib.error = urllib2
131 132
133# Python version check
134ver = sys.version_info
135if ver[0] == 3:
136 print('error: Python 3 support is not fully implemented in repo yet.\n'
137 'Please use Python 2.6 - 2.7 instead.',
138 file=sys.stderr)
139 sys.exit(1)
140if (ver[0], ver[1]) < MIN_PYTHON_VERSION:
141 print('error: Python version %s unsupported.\n'
142 'Please use Python 2.6 - 2.7 instead.'
143 % sys.version.split(' ')[0], file=sys.stderr)
144 sys.exit(1)
145
132home_dot_repo = os.path.expanduser('~/.repoconfig') 146home_dot_repo = os.path.expanduser('~/.repoconfig')
133gpg_dir = os.path.join(home_dot_repo, 'gnupg') 147gpg_dir = os.path.join(home_dot_repo, 'gnupg')
134 148
@@ -164,7 +178,8 @@ group.add_option('--depth', type='int', default=None,
164 help='create a shallow clone with given depth; see git clone') 178 help='create a shallow clone with given depth; see git clone')
165group.add_option('-g', '--groups', 179group.add_option('-g', '--groups',
166 dest='groups', default='default', 180 dest='groups', default='default',
167 help='restrict manifest projects to ones with a specified group', 181 help='restrict manifest projects to ones with specified '
182 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]',
168 metavar='GROUP') 183 metavar='GROUP')
169group.add_option('-p', '--platform', 184group.add_option('-p', '--platform',
170 dest='platform', default="auto", 185 dest='platform', default="auto",
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/forall.py b/subcmds/forall.py
index 4c1c9ff8..7d5f7794 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 = """
47Executes the same shell command in each project. 48Executes the same shell command in each project.
48 49
50The -r option allows running the command only on projects matching
51regex or wildcard expression.
52
49Output Formatting 53Output 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:
diff --git a/subcmds/help.py b/subcmds/help.py
index 78428825..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:
@@ -55,10 +54,9 @@ Displays detailed usage information about a command.
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 325874b5..c10e56cd 100644
--- a/subcmds/info.py
+++ b/subcmds/info.py
@@ -163,7 +163,7 @@ class Info(PagedCommand):
163 all_branches = [] 163 all_branches = []
164 for project in self.GetProjects(args): 164 for project in self.GetProjects(args):
165 br = [project.GetUploadableBranch(x) 165 br = [project.GetUploadableBranch(x)
166 for x in project.GetBranches().keys()] 166 for x in project.GetBranches()]
167 br = [x for x in br if x] 167 br = [x for x in br if x]
168 if self.opt.current_branch: 168 if self.opt.current_branch:
169 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..29730cc4 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -91,8 +91,9 @@ to update the working directory files.
91 dest='depth', 91 dest='depth',
92 help='create a shallow clone with given depth; see git clone') 92 help='create a shallow clone with given depth; see git clone')
93 g.add_option('-g', '--groups', 93 g.add_option('-g', '--groups',
94 dest='groups', default='all,-notdefault', 94 dest='groups', default='default',
95 help='restrict manifest projects to ones with a specified group', 95 help='restrict manifest projects to ones with specified '
96 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]',
96 metavar='GROUP') 97 metavar='GROUP')
97 g.add_option('-p', '--platform', 98 g.add_option('-p', '--platform',
98 dest='platform', default='auto', 99 dest='platform', default='auto',
@@ -169,7 +170,7 @@ to update the working directory files.
169 170
170 groups = [x for x in groups if x] 171 groups = [x for x in groups if x]
171 groupstr = ','.join(groups) 172 groupstr = ','.join(groups)
172 if opt.platform == 'auto' and groupstr == 'all,-notdefault,platform-' + platform.system().lower(): 173 if opt.platform == 'auto' and groupstr == 'default,platform-' + platform.system().lower():
173 groupstr = None 174 groupstr = None
174 m.config.SetString('manifest.groups', groupstr) 175 m.config.SetString('manifest.groups', groupstr)
175 176
diff --git a/subcmds/list.py b/subcmds/list.py
index 0d5c27f7..a3358245 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
16from __future__ import print_function 16from __future__ import print_function
17import re 17import sys
18 18
19from command import Command, MirrorSafeCommand 19from command import Command, MirrorSafeCommand
20 20
@@ -38,6 +38,12 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'.
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/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..9810337f 100644
--- a/subcmds/status.py
+++ b/subcmds/status.py
@@ -21,10 +21,15 @@ except ImportError:
21 import dummy_threading as _threading 21 import dummy_threading as _threading
22 22
23import glob 23import glob
24try:
25 # For python2
26 import StringIO as io
27except ImportError:
28 # For python3
29 import io
24import itertools 30import itertools
25import os 31import os
26import sys 32import sys
27import StringIO
28 33
29from color import Coloring 34from color import Coloring
30 35
@@ -142,7 +147,7 @@ the following meanings:
142 for project in all_projects: 147 for project in all_projects:
143 sem.acquire() 148 sem.acquire()
144 149
145 class BufList(StringIO.StringIO): 150 class BufList(io.StringIO):
146 def dump(self, ostream): 151 def dump(self, ostream):
147 for entry in self.buflist: 152 for entry in self.buflist:
148 ostream.write(entry) 153 ostream.write(entry)
@@ -182,7 +187,7 @@ the following meanings:
182 try: 187 try:
183 os.chdir(self.manifest.topdir) 188 os.chdir(self.manifest.topdir)
184 189
185 outstring = StringIO.StringIO() 190 outstring = io.StringIO()
186 self._FindOrphans(glob.glob('.*') + \ 191 self._FindOrphans(glob.glob('.*') + \
187 glob.glob('*'), \ 192 glob.glob('*'), \
188 proj_dirs, proj_dirs_parents, outstring) 193 proj_dirs, proj_dirs_parents, outstring)
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 6c903ff4..8fb94885 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -24,8 +24,24 @@ import socket
24import subprocess 24import subprocess
25import sys 25import sys
26import time 26import time
27import urlparse 27try:
28import xmlrpclib 28 # For python3
29 import urllib.parse
30except ImportError:
31 # For python2
32 import imp
33 import urlparse
34 urllib = imp.new_module('urllib')
35 urllib.parse = urlparse
36try:
37 # For python3
38 import xmlrpc.client
39except ImportError:
40 # For python2
41 import imp
42 import xmlrpclib
43 xmlrpc = imp.new_module('xmlrpc')
44 xmlrpc.client = xmlrpclib
29 45
30try: 46try:
31 import threading as _threading 47 import threading as _threading
@@ -228,6 +244,9 @@ later is required to fix a server side protocol bug.
228 # We'll set to true once we've locked the lock. 244 # We'll set to true once we've locked the lock.
229 did_lock = False 245 did_lock = False
230 246
247 if not opt.quiet:
248 print('Fetching project %s' % project.name)
249
231 # Encapsulate everything in a try/except/finally so that: 250 # Encapsulate everything in a try/except/finally so that:
232 # - We always set err_event in the case of an exception. 251 # - We always set err_event in the case of an exception.
233 # - We always make sure we call sem.release(). 252 # - We always make sure we call sem.release().
@@ -274,6 +293,8 @@ later is required to fix a server side protocol bug.
274 if self.jobs == 1: 293 if self.jobs == 1:
275 for project in projects: 294 for project in projects:
276 pm.update() 295 pm.update()
296 if not opt.quiet:
297 print('Fetching project %s' % project.name)
277 if project.Sync_NetworkHalf( 298 if project.Sync_NetworkHalf(
278 quiet=opt.quiet, 299 quiet=opt.quiet,
279 current_branch_only=opt.current_branch_only, 300 current_branch_only=opt.current_branch_only,
@@ -372,6 +393,13 @@ later is required to fix a server side protocol bug.
372 print('\nerror: Exited sync due to gc errors', file=sys.stderr) 393 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
373 sys.exit(1) 394 sys.exit(1)
374 395
396 def _ReloadManifest(self, manifest_name=None):
397 if manifest_name:
398 # Override calls _Unload already
399 self.manifest.Override(manifest_name)
400 else:
401 self.manifest._Unload()
402
375 def UpdateProjectList(self): 403 def UpdateProjectList(self):
376 new_project_paths = [] 404 new_project_paths = []
377 for project in self.GetProjects(None, missing_ok=True): 405 for project in self.GetProjects(None, missing_ok=True):
@@ -486,7 +514,7 @@ later is required to fix a server side protocol bug.
486 file=sys.stderr) 514 file=sys.stderr)
487 else: 515 else:
488 try: 516 try:
489 parse_result = urlparse.urlparse(manifest_server) 517 parse_result = urllib.parse(manifest_server)
490 if parse_result.hostname: 518 if parse_result.hostname:
491 username, _account, password = \ 519 username, _account, password = \
492 info.authenticators(parse_result.hostname) 520 info.authenticators(parse_result.hostname)
@@ -504,7 +532,7 @@ later is required to fix a server side protocol bug.
504 1) 532 1)
505 533
506 try: 534 try:
507 server = xmlrpclib.Server(manifest_server) 535 server = xmlrpc.client.Server(manifest_server)
508 if opt.smart_sync: 536 if opt.smart_sync:
509 p = self.manifest.manifestProject 537 p = self.manifest.manifestProject
510 b = p.GetBranch(p.CurrentBranch) 538 b = p.GetBranch(p.CurrentBranch)
@@ -513,8 +541,7 @@ later is required to fix a server side protocol bug.
513 branch = branch[len(R_HEADS):] 541 branch = branch[len(R_HEADS):]
514 542
515 env = os.environ.copy() 543 env = os.environ.copy()
516 if (env.has_key('TARGET_PRODUCT') and 544 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'], 545 target = '%s-%s' % (env['TARGET_PRODUCT'],
519 env['TARGET_BUILD_VARIANT']) 546 env['TARGET_BUILD_VARIANT'])
520 [success, manifest_str] = server.GetApprovedManifest(branch, target) 547 [success, manifest_str] = server.GetApprovedManifest(branch, target)
@@ -542,11 +569,11 @@ later is required to fix a server side protocol bug.
542 else: 569 else:
543 print('error: %s' % manifest_str, file=sys.stderr) 570 print('error: %s' % 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(opt.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(opt.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 48ee685c..a34938e5 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -23,6 +23,11 @@ from editor import Editor
23from error import HookError, UploadError 23from error import HookError, UploadError
24from project import RepoHook 24from project import RepoHook
25 25
26try:
27 input = raw_input
28except NameError:
29 pass
30
26UNUSUAL_COMMIT_THRESHOLD = 5 31UNUSUAL_COMMIT_THRESHOLD = 5
27 32
28def _ConfirmManyUploads(multiple_branches=False): 33def _ConfirmManyUploads(multiple_branches=False):
@@ -33,7 +38,7 @@ def _ConfirmManyUploads(multiple_branches=False):
33 print('ATTENTION: You are uploading an unusually high number of commits.') 38 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 ' 39 print('YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across '
35 'branches?)') 40 'branches?)')
36 answer = raw_input("If you are sure you intend to do this, type 'yes': ").strip() 41 answer = input("If you are sure you intend to do this, type 'yes': ").strip()
37 return answer == "yes" 42 return answer == "yes"
38 43
39def _die(fmt, *args): 44def _die(fmt, *args):