summaryrefslogtreecommitdiffstats
path: root/subcmds
diff options
context:
space:
mode:
Diffstat (limited to 'subcmds')
-rw-r--r--subcmds/abandon.py15
-rw-r--r--subcmds/branches.py3
-rw-r--r--subcmds/checkout.py8
-rw-r--r--subcmds/cherry_pick.py21
-rw-r--r--subcmds/download.py27
-rw-r--r--subcmds/forall.py18
-rw-r--r--subcmds/grep.py24
-rw-r--r--subcmds/help.py35
-rw-r--r--subcmds/info.py195
-rw-r--r--subcmds/init.py79
-rw-r--r--subcmds/list.py3
-rw-r--r--subcmds/manifest.py7
-rw-r--r--subcmds/overview.py10
-rw-r--r--subcmds/prune.py5
-rw-r--r--subcmds/rebase.py14
-rw-r--r--subcmds/selfupdate.py3
-rw-r--r--subcmds/stage.py7
-rw-r--r--subcmds/start.py10
-rw-r--r--subcmds/status.py74
-rw-r--r--subcmds/sync.py323
-rw-r--r--subcmds/upload.py81
-rw-r--r--subcmds/version.py13
22 files changed, 664 insertions, 311 deletions
diff --git a/subcmds/abandon.py b/subcmds/abandon.py
index e17ab2b6..b94ccdd3 100644
--- a/subcmds/abandon.py
+++ b/subcmds/abandon.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import sys 17import sys
17from command import Command 18from command import Command
18from git_command import git 19from git_command import git
@@ -36,7 +37,7 @@ It is equivalent to "git branch -D <branchname>".
36 37
37 nb = args[0] 38 nb = args[0]
38 if not git.check_ref_format('heads/%s' % nb): 39 if not git.check_ref_format('heads/%s' % nb):
39 print >>sys.stderr, "error: '%s' is not a valid name" % nb 40 print("error: '%s' is not a valid name" % nb, file=sys.stderr)
40 sys.exit(1) 41 sys.exit(1)
41 42
42 nb = args[0] 43 nb = args[0]
@@ -58,13 +59,13 @@ It is equivalent to "git branch -D <branchname>".
58 59
59 if err: 60 if err:
60 for p in err: 61 for p in err:
61 print >>sys.stderr,\ 62 print("error: %s/: cannot abandon %s" % (p.relpath, nb),
62 "error: %s/: cannot abandon %s" \ 63 file=sys.stderr)
63 % (p.relpath, nb)
64 sys.exit(1) 64 sys.exit(1)
65 elif not success: 65 elif not success:
66 print >>sys.stderr, 'error: no project has branch %s' % nb 66 print('error: no project has branch %s' % nb, file=sys.stderr)
67 sys.exit(1) 67 sys.exit(1)
68 else: 68 else:
69 print >>sys.stderr, 'Abandoned in %d project(s):\n %s' % ( 69 print('Abandoned in %d project(s):\n %s'
70 len(success), '\n '.join(p.relpath for p in success)) 70 % (len(success), '\n '.join(p.relpath for p in success)),
71 file=sys.stderr)
diff --git a/subcmds/branches.py b/subcmds/branches.py
index a7ba3d6d..06d45abe 100644
--- a/subcmds/branches.py
+++ b/subcmds/branches.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import sys 17import sys
17from color import Coloring 18from color import Coloring
18from command import Command 19from command import Command
@@ -107,7 +108,7 @@ is shown, then the branch appears in all projects.
107 names.sort() 108 names.sort()
108 109
109 if not names: 110 if not names:
110 print >>sys.stderr, ' (no branches)' 111 print(' (no branches)', file=sys.stderr)
111 return 112 return
112 113
113 width = 25 114 width = 25
diff --git a/subcmds/checkout.py b/subcmds/checkout.py
index bfbe9921..cbbca106 100644
--- a/subcmds/checkout.py
+++ b/subcmds/checkout.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import sys 17import sys
17from command import Command 18from command import Command
18from progress import Progress 19from progress import Progress
@@ -55,10 +56,9 @@ The command is equivalent to:
55 56
56 if err: 57 if err:
57 for p in err: 58 for p in err:
58 print >>sys.stderr,\ 59 print("error: %s/: cannot checkout %s" % (p.relpath, nb),
59 "error: %s/: cannot checkout %s" \ 60 file=sys.stderr)
60 % (p.relpath, nb)
61 sys.exit(1) 61 sys.exit(1)
62 elif not success: 62 elif not success:
63 print >>sys.stderr, 'error: no project has branch %s' % nb 63 print('error: no project has branch %s' % nb, file=sys.stderr)
64 sys.exit(1) 64 sys.exit(1)
diff --git a/subcmds/cherry_pick.py b/subcmds/cherry_pick.py
index 7a6d4c20..01b97e07 100644
--- a/subcmds/cherry_pick.py
+++ b/subcmds/cherry_pick.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import re 17import re
17import sys 18import sys
18from command import Command 19from command import Command
@@ -46,13 +47,13 @@ change id will be added.
46 capture_stdout = True, 47 capture_stdout = True,
47 capture_stderr = True) 48 capture_stderr = True)
48 if p.Wait() != 0: 49 if p.Wait() != 0:
49 print >>sys.stderr, p.stderr 50 print(p.stderr, file=sys.stderr)
50 sys.exit(1) 51 sys.exit(1)
51 sha1 = p.stdout.strip() 52 sha1 = p.stdout.strip()
52 53
53 p = GitCommand(None, ['cat-file', 'commit', sha1], capture_stdout=True) 54 p = GitCommand(None, ['cat-file', 'commit', sha1], capture_stdout=True)
54 if p.Wait() != 0: 55 if p.Wait() != 0:
55 print >>sys.stderr, "error: Failed to retrieve old commit message" 56 print("error: Failed to retrieve old commit message", file=sys.stderr)
56 sys.exit(1) 57 sys.exit(1)
57 old_msg = self._StripHeader(p.stdout) 58 old_msg = self._StripHeader(p.stdout)
58 59
@@ -62,8 +63,8 @@ change id will be added.
62 capture_stderr = True) 63 capture_stderr = True)
63 status = p.Wait() 64 status = p.Wait()
64 65
65 print >>sys.stdout, p.stdout 66 print(p.stdout, file=sys.stdout)
66 print >>sys.stderr, p.stderr 67 print(p.stderr, file=sys.stderr)
67 68
68 if status == 0: 69 if status == 0:
69 # The cherry-pick was applied correctly. We just need to edit the 70 # The cherry-pick was applied correctly. We just need to edit the
@@ -76,16 +77,14 @@ change id will be added.
76 capture_stderr = True) 77 capture_stderr = True)
77 p.stdin.write(new_msg) 78 p.stdin.write(new_msg)
78 if p.Wait() != 0: 79 if p.Wait() != 0:
79 print >>sys.stderr, "error: Failed to update commit message" 80 print("error: Failed to update commit message", file=sys.stderr)
80 sys.exit(1) 81 sys.exit(1)
81 82
82 else: 83 else:
83 print >>sys.stderr, """\ 84 print('NOTE: When committing (please see above) and editing the commit'
84NOTE: When committing (please see above) and editing the commit message, 85 'message, please remove the old Change-Id-line and add:')
85please remove the old Change-Id-line and add: 86 print(self._GetReference(sha1), file=sys.stderr)
86""" 87 print(file=sys.stderr)
87 print >>sys.stderr, self._GetReference(sha1)
88 print >>sys.stderr
89 88
90 def _IsChangeId(self, line): 89 def _IsChangeId(self, line):
91 return CHANGE_ID_RE.match(line) 90 return CHANGE_ID_RE.match(line)
diff --git a/subcmds/download.py b/subcmds/download.py
index 0abe90d0..471e88b5 100644
--- a/subcmds/download.py
+++ b/subcmds/download.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import re 17import re
17import sys 18import sys
18 19
@@ -32,13 +33,13 @@ makes it available in your project's local working directory.
32""" 33"""
33 34
34 def _Options(self, p): 35 def _Options(self, p):
35 p.add_option('-c','--cherry-pick', 36 p.add_option('-c', '--cherry-pick',
36 dest='cherrypick', action='store_true', 37 dest='cherrypick', action='store_true',
37 help="cherry-pick instead of checkout") 38 help="cherry-pick instead of checkout")
38 p.add_option('-r','--revert', 39 p.add_option('-r', '--revert',
39 dest='revert', action='store_true', 40 dest='revert', action='store_true',
40 help="revert instead of checkout") 41 help="revert instead of checkout")
41 p.add_option('-f','--ff-only', 42 p.add_option('-f', '--ff-only',
42 dest='ffonly', action='store_true', 43 dest='ffonly', action='store_true',
43 help="force fast-forward merge") 44 help="force fast-forward merge")
44 45
@@ -68,23 +69,23 @@ makes it available in your project's local working directory.
68 for project, change_id, ps_id in self._ParseChangeIds(args): 69 for project, change_id, ps_id in self._ParseChangeIds(args):
69 dl = project.DownloadPatchSet(change_id, ps_id) 70 dl = project.DownloadPatchSet(change_id, ps_id)
70 if not dl: 71 if not dl:
71 print >>sys.stderr, \ 72 print('[%s] change %d/%d not found'
72 '[%s] change %d/%d not found' \ 73 % (project.name, change_id, ps_id),
73 % (project.name, change_id, ps_id) 74 file=sys.stderr)
74 sys.exit(1) 75 sys.exit(1)
75 76
76 if not opt.revert and not dl.commits: 77 if not opt.revert and not dl.commits:
77 print >>sys.stderr, \ 78 print('[%s] change %d/%d has already been merged'
78 '[%s] change %d/%d has already been merged' \ 79 % (project.name, change_id, ps_id),
79 % (project.name, change_id, ps_id) 80 file=sys.stderr)
80 continue 81 continue
81 82
82 if len(dl.commits) > 1: 83 if len(dl.commits) > 1:
83 print >>sys.stderr, \ 84 print('[%s] %d/%d depends on %d unmerged changes:' \
84 '[%s] %d/%d depends on %d unmerged changes:' \ 85 % (project.name, change_id, ps_id, len(dl.commits)),
85 % (project.name, change_id, ps_id, len(dl.commits)) 86 file=sys.stderr)
86 for c in dl.commits: 87 for c in dl.commits:
87 print >>sys.stderr, ' %s' % (c) 88 print(' %s' % (c), file=sys.stderr)
88 if opt.cherrypick: 89 if opt.cherrypick:
89 project._CherryPick(dl.commit) 90 project._CherryPick(dl.commit)
90 elif opt.revert: 91 elif opt.revert:
diff --git a/subcmds/forall.py b/subcmds/forall.py
index b633b7d4..4c1c9ff8 100644
--- a/subcmds/forall.py
+++ b/subcmds/forall.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import fcntl 17import fcntl
17import re 18import re
18import os 19import os
@@ -92,6 +93,9 @@ following <command>.
92 93
93Unless -p is used, stdin, stdout, stderr are inherited from the 94Unless -p is used, stdin, stdout, stderr are inherited from the
94terminal and are not redirected. 95terminal and are not redirected.
96
97If -e is used, when a command exits unsuccessfully, '%prog' will abort
98without iterating through the remaining projects.
95""" 99"""
96 100
97 def _Options(self, p): 101 def _Options(self, p):
@@ -104,6 +108,9 @@ terminal and are not redirected.
104 dest='command', 108 dest='command',
105 action='callback', 109 action='callback',
106 callback=cmd) 110 callback=cmd)
111 p.add_option('-e', '--abort-on-errors',
112 dest='abort_on_errors', action='store_true',
113 help='Abort if a command exits unsuccessfully')
107 114
108 g = p.add_option_group('Output') 115 g = p.add_option_group('Output')
109 g.add_option('-p', 116 g.add_option('-p',
@@ -183,7 +190,7 @@ terminal and are not redirected.
183 if not os.path.exists(cwd): 190 if not os.path.exists(cwd):
184 if (opt.project_header and opt.verbose) \ 191 if (opt.project_header and opt.verbose) \
185 or not opt.project_header: 192 or not opt.project_header:
186 print >>sys.stderr, 'skipping %s/' % project.relpath 193 print('skipping %s/' % project.relpath, file=sys.stderr)
187 continue 194 continue
188 195
189 if opt.project_header: 196 if opt.project_header:
@@ -254,7 +261,12 @@ terminal and are not redirected.
254 s.dest.flush() 261 s.dest.flush()
255 262
256 r = p.wait() 263 r = p.wait()
257 if r != 0 and r != rc: 264 if r != 0:
258 rc = r 265 if r != rc:
266 rc = r
267 if opt.abort_on_errors:
268 print("error: %s: Aborting due to previous error" % project.relpath,
269 file=sys.stderr)
270 sys.exit(r)
259 if rc != 0: 271 if rc != 0:
260 sys.exit(rc) 272 sys.exit(rc)
diff --git a/subcmds/grep.py b/subcmds/grep.py
index 0dc8f9f6..dd391cfa 100644
--- a/subcmds/grep.py
+++ b/subcmds/grep.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import sys 17import sys
17from color import Coloring 18from color import Coloring
18from command import PagedCommand 19from command import PagedCommand
@@ -51,7 +52,7 @@ Examples
51 52
52Look for a line that has '#define' and either 'MAX_PATH or 'PATH_MAX': 53Look for a line that has '#define' and either 'MAX_PATH or 'PATH_MAX':
53 54
54 repo grep -e '#define' --and -\( -e MAX_PATH -e PATH_MAX \) 55 repo grep -e '#define' --and -\\( -e MAX_PATH -e PATH_MAX \\)
55 56
56Look for a line that has 'NODE' or 'Unexpected' in files that 57Look for a line that has 'NODE' or 'Unexpected' in files that
57contain a line that matches both expressions: 58contain a line that matches both expressions:
@@ -84,7 +85,7 @@ contain a line that matches both expressions:
84 g.add_option('--cached', 85 g.add_option('--cached',
85 action='callback', callback=carry, 86 action='callback', callback=carry,
86 help='Search the index, instead of the work tree') 87 help='Search the index, instead of the work tree')
87 g.add_option('-r','--revision', 88 g.add_option('-r', '--revision',
88 dest='revision', action='append', metavar='TREEish', 89 dest='revision', action='append', metavar='TREEish',
89 help='Search TREEish, instead of the work tree') 90 help='Search TREEish, instead of the work tree')
90 91
@@ -96,7 +97,7 @@ contain a line that matches both expressions:
96 g.add_option('-i', '--ignore-case', 97 g.add_option('-i', '--ignore-case',
97 action='callback', callback=carry, 98 action='callback', callback=carry,
98 help='Ignore case differences') 99 help='Ignore case differences')
99 g.add_option('-a','--text', 100 g.add_option('-a', '--text',
100 action='callback', callback=carry, 101 action='callback', callback=carry,
101 help="Process binary files as if they were text") 102 help="Process binary files as if they were text")
102 g.add_option('-I', 103 g.add_option('-I',
@@ -125,7 +126,7 @@ contain a line that matches both expressions:
125 g.add_option('--and', '--or', '--not', 126 g.add_option('--and', '--or', '--not',
126 action='callback', callback=carry, 127 action='callback', callback=carry,
127 help='Boolean operators to combine patterns') 128 help='Boolean operators to combine patterns')
128 g.add_option('-(','-)', 129 g.add_option('-(', '-)',
129 action='callback', callback=carry, 130 action='callback', callback=carry,
130 help='Boolean operator grouping') 131 help='Boolean operator grouping')
131 132
@@ -145,10 +146,10 @@ contain a line that matches both expressions:
145 action='callback', callback=carry, 146 action='callback', callback=carry,
146 metavar='CONTEXT', type='str', 147 metavar='CONTEXT', type='str',
147 help='Show CONTEXT lines after match') 148 help='Show CONTEXT lines after match')
148 g.add_option('-l','--name-only','--files-with-matches', 149 g.add_option('-l', '--name-only', '--files-with-matches',
149 action='callback', callback=carry, 150 action='callback', callback=carry,
150 help='Show only file names containing matching lines') 151 help='Show only file names containing matching lines')
151 g.add_option('-L','--files-without-match', 152 g.add_option('-L', '--files-without-match',
152 action='callback', callback=carry, 153 action='callback', callback=carry,
153 help='Show only file names not containing matching lines') 154 help='Show only file names not containing matching lines')
154 155
@@ -157,9 +158,9 @@ contain a line that matches both expressions:
157 out = GrepColoring(self.manifest.manifestProject.config) 158 out = GrepColoring(self.manifest.manifestProject.config)
158 159
159 cmd_argv = ['grep'] 160 cmd_argv = ['grep']
160 if out.is_on and git_require((1,6,3)): 161 if out.is_on and git_require((1, 6, 3)):
161 cmd_argv.append('--color') 162 cmd_argv.append('--color')
162 cmd_argv.extend(getattr(opt,'cmd_argv',[])) 163 cmd_argv.extend(getattr(opt, 'cmd_argv', []))
163 164
164 if '-e' not in cmd_argv: 165 if '-e' not in cmd_argv:
165 if not args: 166 if not args:
@@ -178,8 +179,7 @@ contain a line that matches both expressions:
178 have_rev = False 179 have_rev = False
179 if opt.revision: 180 if opt.revision:
180 if '--cached' in cmd_argv: 181 if '--cached' in cmd_argv:
181 print >>sys.stderr,\ 182 print('fatal: cannot combine --cached and --revision', file=sys.stderr)
182 'fatal: cannot combine --cached and --revision'
183 sys.exit(1) 183 sys.exit(1)
184 have_rev = True 184 have_rev = True
185 cmd_argv.extend(opt.revision) 185 cmd_argv.extend(opt.revision)
@@ -230,13 +230,13 @@ contain a line that matches both expressions:
230 out.nl() 230 out.nl()
231 else: 231 else:
232 for line in r: 232 for line in r:
233 print line 233 print(line)
234 234
235 if have_match: 235 if have_match:
236 sys.exit(0) 236 sys.exit(0)
237 elif have_rev and bad_rev: 237 elif have_rev and bad_rev:
238 for r in opt.revision: 238 for r in opt.revision:
239 print >>sys.stderr, "error: can't search revision %s" % r 239 print("error: can't search revision %s" % r, file=sys.stderr)
240 sys.exit(1) 240 sys.exit(1)
241 else: 241 else:
242 sys.exit(1) 242 sys.exit(1)
diff --git a/subcmds/help.py b/subcmds/help.py
index 375d04d2..15aab7f9 100644
--- a/subcmds/help.py
+++ b/subcmds/help.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import re 17import re
17import sys 18import sys
18from formatter import AbstractFormatter, DumbWriter 19from formatter import AbstractFormatter, DumbWriter
@@ -31,10 +32,8 @@ Displays detailed usage information about a command.
31""" 32"""
32 33
33 def _PrintAllCommands(self): 34 def _PrintAllCommands(self):
34 print 'usage: repo COMMAND [ARGS]' 35 print('usage: repo COMMAND [ARGS]')
35 print """ 36 print('The complete list of recognized repo commands are:')
36The complete list of recognized repo commands are:
37"""
38 commandNames = self.commands.keys() 37 commandNames = self.commands.keys()
39 commandNames.sort() 38 commandNames.sort()
40 39
@@ -49,17 +48,14 @@ The complete list of recognized repo commands are:
49 summary = command.helpSummary.strip() 48 summary = command.helpSummary.strip()
50 except AttributeError: 49 except AttributeError:
51 summary = '' 50 summary = ''
52 print fmt % (name, summary) 51 print(fmt % (name, summary))
53 print """ 52 print("See 'repo help <command>' for more information on a"
54See 'repo help <command>' for more information on a specific command. 53 'specific command.')
55"""
56 54
57 def _PrintCommonCommands(self): 55 def _PrintCommonCommands(self):
58 print 'usage: repo COMMAND [ARGS]' 56 print('usage: repo COMMAND [ARGS]')
59 print """ 57 print('The most commonly used repo commands are:')
60The most commonly used repo commands are: 58 commandNames = [name
61"""
62 commandNames = [name
63 for name in self.commands.keys() 59 for name in self.commands.keys()
64 if self.commands[name].common] 60 if self.commands[name].common]
65 commandNames.sort() 61 commandNames.sort()
@@ -75,11 +71,10 @@ The most commonly used repo commands are:
75 summary = command.helpSummary.strip() 71 summary = command.helpSummary.strip()
76 except AttributeError: 72 except AttributeError:
77 summary = '' 73 summary = ''
78 print fmt % (name, summary) 74 print(fmt % (name, summary))
79 print """ 75 print(
80See 'repo help <command>' for more information on a specific command. 76"See 'repo help <command>' for more information on a specific command.\n"
81See 'repo help --all' for a complete list of recognized commands. 77"See 'repo help --all' for a complete list of recognized commands.")
82"""
83 78
84 def _PrintCommandHelp(self, cmd): 79 def _PrintCommandHelp(self, cmd):
85 class _Out(Coloring): 80 class _Out(Coloring):
@@ -131,7 +126,7 @@ See 'repo help --all' for a complete list of recognized commands.
131 126
132 p('%s', title) 127 p('%s', title)
133 self.nl() 128 self.nl()
134 p('%s', ''.ljust(len(title),section_type[0])) 129 p('%s', ''.ljust(len(title), section_type[0]))
135 self.nl() 130 self.nl()
136 continue 131 continue
137 132
@@ -162,7 +157,7 @@ See 'repo help --all' for a complete list of recognized commands.
162 try: 157 try:
163 cmd = self.commands[name] 158 cmd = self.commands[name]
164 except KeyError: 159 except KeyError:
165 print >>sys.stderr, "repo: '%s' is not a repo command." % name 160 print("repo: '%s' is not a repo command." % name, file=sys.stderr)
166 sys.exit(1) 161 sys.exit(1)
167 162
168 cmd.manifest = self.manifest 163 cmd.manifest = self.manifest
diff --git a/subcmds/info.py b/subcmds/info.py
new file mode 100644
index 00000000..a6eba889
--- /dev/null
+++ b/subcmds/info.py
@@ -0,0 +1,195 @@
1#
2# Copyright (C) 2012 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
16from command import PagedCommand
17from color import Coloring
18from error import NoSuchProjectError
19from git_refs import R_M
20
21class _Coloring(Coloring):
22 def __init__(self, config):
23 Coloring.__init__(self, config, "status")
24
25class Info(PagedCommand):
26 common = True
27 helpSummary = "Get info on the manifest branch, current branch or unmerged branches"
28 helpUsage = "%prog [-dl] [-o [-b]] [<project>...]"
29
30 def _Options(self, p, show_smart=True):
31 p.add_option('-d', '--diff',
32 dest='all', action='store_true',
33 help="show full info and commit diff including remote branches")
34 p.add_option('-o', '--overview',
35 dest='overview', action='store_true',
36 help='show overview of all local commits')
37 p.add_option('-b', '--current-branch',
38 dest="current_branch", action="store_true",
39 help="consider only checked out branches")
40 p.add_option('-l', '--local-only',
41 dest="local", action="store_true",
42 help="Disable all remote operations")
43
44
45 def Execute(self, opt, args):
46 self.out = _Coloring(self.manifest.globalConfig)
47 self.heading = self.out.printer('heading', attr = 'bold')
48 self.headtext = self.out.printer('headtext', fg = 'yellow')
49 self.redtext = self.out.printer('redtext', fg = 'red')
50 self.sha = self.out.printer("sha", fg = 'yellow')
51 self.text = self.out.printer('text')
52 self.dimtext = self.out.printer('dimtext', attr = 'dim')
53
54 self.opt = opt
55
56 mergeBranch = self.manifest.manifestProject.config.GetBranch("default").merge
57
58 self.heading("Manifest branch: ")
59 self.headtext(self.manifest.default.revisionExpr)
60 self.out.nl()
61 self.heading("Manifest merge branch: ")
62 self.headtext(mergeBranch)
63 self.out.nl()
64
65 self.printSeparator()
66
67 if not opt.overview:
68 self.printDiffInfo(args)
69 else:
70 self.printCommitOverview(args)
71
72 def printSeparator(self):
73 self.text("----------------------------")
74 self.out.nl()
75
76 def printDiffInfo(self, args):
77 try:
78 projs = self.GetProjects(args)
79 except NoSuchProjectError:
80 return
81
82 for p in projs:
83 self.heading("Project: ")
84 self.headtext(p.name)
85 self.out.nl()
86
87 self.heading("Mount path: ")
88 self.headtext(p.worktree)
89 self.out.nl()
90
91 self.heading("Current revision: ")
92 self.headtext(p.revisionExpr)
93 self.out.nl()
94
95 localBranches = p.GetBranches().keys()
96 self.heading("Local Branches: ")
97 self.redtext(str(len(localBranches)))
98 if len(localBranches) > 0:
99 self.text(" [")
100 self.text(", ".join(localBranches))
101 self.text("]")
102 self.out.nl()
103
104 if self.opt.all:
105 self.findRemoteLocalDiff(p)
106
107 self.printSeparator()
108
109 def findRemoteLocalDiff(self, project):
110 #Fetch all the latest commits
111 if not self.opt.local:
112 project.Sync_NetworkHalf(quiet=True, current_branch_only=True)
113
114 logTarget = R_M + self.manifest.manifestProject.config.GetBranch("default").merge
115
116 bareTmp = project.bare_git._bare
117 project.bare_git._bare = False
118 localCommits = project.bare_git.rev_list(
119 '--abbrev=8',
120 '--abbrev-commit',
121 '--pretty=oneline',
122 logTarget + "..",
123 '--')
124
125 originCommits = project.bare_git.rev_list(
126 '--abbrev=8',
127 '--abbrev-commit',
128 '--pretty=oneline',
129 ".." + logTarget,
130 '--')
131 project.bare_git._bare = bareTmp
132
133 self.heading("Local Commits: ")
134 self.redtext(str(len(localCommits)))
135 self.dimtext(" (on current branch)")
136 self.out.nl()
137
138 for c in localCommits:
139 split = c.split()
140 self.sha(split[0] + " ")
141 self.text(" ".join(split[1:]))
142 self.out.nl()
143
144 self.printSeparator()
145
146 self.heading("Remote Commits: ")
147 self.redtext(str(len(originCommits)))
148 self.out.nl()
149
150 for c in originCommits:
151 split = c.split()
152 self.sha(split[0] + " ")
153 self.text(" ".join(split[1:]))
154 self.out.nl()
155
156 def printCommitOverview(self, args):
157 all_branches = []
158 for project in self.GetProjects(args):
159 br = [project.GetUploadableBranch(x)
160 for x in project.GetBranches().keys()]
161 br = [x for x in br if x]
162 if self.opt.current_branch:
163 br = [x for x in br if x.name == project.CurrentBranch]
164 all_branches.extend(br)
165
166 if not all_branches:
167 return
168
169 self.out.nl()
170 self.heading('Projects Overview')
171 project = None
172
173 for branch in all_branches:
174 if project != branch.project:
175 project = branch.project
176 self.out.nl()
177 self.headtext(project.relpath)
178 self.out.nl()
179
180 commits = branch.commits
181 date = branch.date
182 self.text('%s %-33s (%2d commit%s, %s)' % (
183 branch.name == project.CurrentBranch and '*' or ' ',
184 branch.name,
185 len(commits),
186 len(commits) != 1 and 's' or '',
187 date))
188 self.out.nl()
189
190 for commit in commits:
191 split = commit.split()
192 self.text('{0:38}{1} '.format('','-'))
193 self.sha(split[0] + " ")
194 self.text(" ".join(split[1:]))
195 self.out.nl()
diff --git a/subcmds/init.py b/subcmds/init.py
index b6b98076..11312601 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import os 17import os
17import platform 18import platform
18import re 19import re
@@ -117,18 +118,22 @@ to update the working directory files.
117 dest='config_name', action="store_true", default=False, 118 dest='config_name', action="store_true", default=False,
118 help='Always prompt for name/e-mail') 119 help='Always prompt for name/e-mail')
119 120
121 def _RegisteredEnvironmentOptions(self):
122 return {'REPO_MANIFEST_URL': 'manifest_url',
123 'REPO_MIRROR_LOCATION': 'reference'}
124
120 def _SyncManifest(self, opt): 125 def _SyncManifest(self, opt):
121 m = self.manifest.manifestProject 126 m = self.manifest.manifestProject
122 is_new = not m.Exists 127 is_new = not m.Exists
123 128
124 if is_new: 129 if is_new:
125 if not opt.manifest_url: 130 if not opt.manifest_url:
126 print >>sys.stderr, 'fatal: manifest url (-u) is required.' 131 print('fatal: manifest url (-u) is required.', file=sys.stderr)
127 sys.exit(1) 132 sys.exit(1)
128 133
129 if not opt.quiet: 134 if not opt.quiet:
130 print >>sys.stderr, 'Get %s' \ 135 print('Get %s' % GitConfig.ForUser().UrlInsteadOf(opt.manifest_url),
131 % GitConfig.ForUser().UrlInsteadOf(opt.manifest_url) 136 file=sys.stderr)
132 m._InitGitDir() 137 m._InitGitDir()
133 138
134 if opt.manifest_branch: 139 if opt.manifest_branch:
@@ -147,7 +152,7 @@ to update the working directory files.
147 r.ResetFetch() 152 r.ResetFetch()
148 r.Save() 153 r.Save()
149 154
150 groups = re.split('[,\s]+', opt.groups) 155 groups = re.split(r'[,\s]+', opt.groups)
151 all_platforms = ['linux', 'darwin'] 156 all_platforms = ['linux', 'darwin']
152 platformize = lambda x: 'platform-' + x 157 platformize = lambda x: 'platform-' + x
153 if opt.platform == 'auto': 158 if opt.platform == 'auto':
@@ -159,7 +164,7 @@ to update the working directory files.
159 elif opt.platform in all_platforms: 164 elif opt.platform in all_platforms:
160 groups.extend(platformize(opt.platform)) 165 groups.extend(platformize(opt.platform))
161 elif opt.platform != 'none': 166 elif opt.platform != 'none':
162 print >>sys.stderr, 'fatal: invalid platform flag' 167 print('fatal: invalid platform flag', file=sys.stderr)
163 sys.exit(1) 168 sys.exit(1)
164 169
165 groups = [x for x in groups if x] 170 groups = [x for x in groups if x]
@@ -175,12 +180,15 @@ to update the working directory files.
175 if is_new: 180 if is_new:
176 m.config.SetString('repo.mirror', 'true') 181 m.config.SetString('repo.mirror', 'true')
177 else: 182 else:
178 print >>sys.stderr, 'fatal: --mirror not supported on existing client' 183 print('fatal: --mirror is only supported when initializing a new '
184 'workspace.', file=sys.stderr)
185 print('Either delete the .repo folder in this workspace, or initialize '
186 'in another location.', file=sys.stderr)
179 sys.exit(1) 187 sys.exit(1)
180 188
181 if not m.Sync_NetworkHalf(is_new=is_new): 189 if not m.Sync_NetworkHalf(is_new=is_new):
182 r = m.GetRemote(m.remote.name) 190 r = m.GetRemote(m.remote.name)
183 print >>sys.stderr, 'fatal: cannot obtain manifest %s' % r.url 191 print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
184 192
185 # Better delete the manifest git dir if we created it; otherwise next 193 # Better delete the manifest git dir if we created it; otherwise next
186 # time (when user fixes problems) we won't go through the "is_new" logic. 194 # time (when user fixes problems) we won't go through the "is_new" logic.
@@ -197,19 +205,19 @@ to update the working directory files.
197 205
198 if is_new or m.CurrentBranch is None: 206 if is_new or m.CurrentBranch is None:
199 if not m.StartBranch('default'): 207 if not m.StartBranch('default'):
200 print >>sys.stderr, 'fatal: cannot create default in manifest' 208 print('fatal: cannot create default in manifest', file=sys.stderr)
201 sys.exit(1) 209 sys.exit(1)
202 210
203 def _LinkManifest(self, name): 211 def _LinkManifest(self, name):
204 if not name: 212 if not name:
205 print >>sys.stderr, 'fatal: manifest name (-m) is required.' 213 print('fatal: manifest name (-m) is required.', file=sys.stderr)
206 sys.exit(1) 214 sys.exit(1)
207 215
208 try: 216 try:
209 self.manifest.Link(name) 217 self.manifest.Link(name)
210 except ManifestParseError as e: 218 except ManifestParseError as e:
211 print >>sys.stderr, "fatal: manifest '%s' not available" % name 219 print("fatal: manifest '%s' not available" % name, file=sys.stderr)
212 print >>sys.stderr, 'fatal: %s' % str(e) 220 print('fatal: %s' % str(e), file=sys.stderr)
213 sys.exit(1) 221 sys.exit(1)
214 222
215 def _Prompt(self, prompt, value): 223 def _Prompt(self, prompt, value):
@@ -231,24 +239,24 @@ to update the working directory files.
231 mp.config.SetString('user.name', gc.GetString('user.name')) 239 mp.config.SetString('user.name', gc.GetString('user.name'))
232 mp.config.SetString('user.email', gc.GetString('user.email')) 240 mp.config.SetString('user.email', gc.GetString('user.email'))
233 241
234 print '' 242 print()
235 print 'Your identity is: %s <%s>' % (mp.config.GetString('user.name'), 243 print('Your identity is: %s <%s>' % (mp.config.GetString('user.name'),
236 mp.config.GetString('user.email')) 244 mp.config.GetString('user.email')))
237 print 'If you want to change this, please re-run \'repo init\' with --config-name' 245 print('If you want to change this, please re-run \'repo init\' with --config-name')
238 return False 246 return False
239 247
240 def _ConfigureUser(self): 248 def _ConfigureUser(self):
241 mp = self.manifest.manifestProject 249 mp = self.manifest.manifestProject
242 250
243 while True: 251 while True:
244 print '' 252 print()
245 name = self._Prompt('Your Name', mp.UserName) 253 name = self._Prompt('Your Name', mp.UserName)
246 email = self._Prompt('Your Email', mp.UserEmail) 254 email = self._Prompt('Your Email', mp.UserEmail)
247 255
248 print '' 256 print()
249 print 'Your identity is: %s <%s>' % (name, email) 257 print('Your identity is: %s <%s>' % (name, email))
250 sys.stdout.write('is this correct [y/N]? ') 258 sys.stdout.write('is this correct [y/N]? ')
251 a = sys.stdin.readline().strip() 259 a = sys.stdin.readline().strip().lower()
252 if a in ('yes', 'y', 't', 'true'): 260 if a in ('yes', 'y', 't', 'true'):
253 break 261 break
254 262
@@ -274,17 +282,17 @@ to update the working directory files.
274 self._on = True 282 self._on = True
275 out = _Test() 283 out = _Test()
276 284
277 print '' 285 print()
278 print "Testing colorized output (for 'repo diff', 'repo status'):" 286 print("Testing colorized output (for 'repo diff', 'repo status'):")
279 287
280 for c in ['black','red','green','yellow','blue','magenta','cyan']: 288 for c in ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan']:
281 out.write(' ') 289 out.write(' ')
282 out.printer(fg=c)(' %-6s ', c) 290 out.printer(fg=c)(' %-6s ', c)
283 out.write(' ') 291 out.write(' ')
284 out.printer(fg='white', bg='black')(' %s ' % 'white') 292 out.printer(fg='white', bg='black')(' %s ' % 'white')
285 out.nl() 293 out.nl()
286 294
287 for c in ['bold','dim','ul','reverse']: 295 for c in ['bold', 'dim', 'ul', 'reverse']:
288 out.write(' ') 296 out.write(' ')
289 out.printer(fg='black', attr=c)(' %-6s ', c) 297 out.printer(fg='black', attr=c)(' %-6s ', c)
290 out.nl() 298 out.nl()
@@ -313,6 +321,23 @@ to update the working directory files.
313 # We store the depth in the main manifest project. 321 # We store the depth in the main manifest project.
314 self.manifest.manifestProject.config.SetString('repo.depth', depth) 322 self.manifest.manifestProject.config.SetString('repo.depth', depth)
315 323
324 def _DisplayResult(self):
325 if self.manifest.IsMirror:
326 init_type = 'mirror '
327 else:
328 init_type = ''
329
330 print()
331 print('repo %shas been initialized in %s'
332 % (init_type, self.manifest.topdir))
333
334 current_dir = os.getcwd()
335 if current_dir != self.manifest.topdir:
336 print('If this is not the directory in which you want to initialize '
337 'repo, please run:')
338 print(' rm -r %s/.repo' % self.manifest.topdir)
339 print('and try again.')
340
316 def Execute(self, opt, args): 341 def Execute(self, opt, args):
317 git_require(MIN_GIT_VERSION, fail=True) 342 git_require(MIN_GIT_VERSION, fail=True)
318 343
@@ -329,10 +354,4 @@ to update the working directory files.
329 354
330 self._ConfigureDepth(opt) 355 self._ConfigureDepth(opt)
331 356
332 if self.manifest.IsMirror: 357 self._DisplayResult()
333 init_type = 'mirror '
334 else:
335 init_type = ''
336
337 print ''
338 print 'repo %sinitialized in %s' % (init_type, self.manifest.topdir)
diff --git a/subcmds/list.py b/subcmds/list.py
index 6058a755..0d5c27f7 100644
--- a/subcmds/list.py
+++ b/subcmds/list.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import re 17import re
17 18
18from command import Command, MirrorSafeCommand 19from command import Command, MirrorSafeCommand
@@ -64,7 +65,7 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'.
64 lines.append("%s : %s" % (_getpath(project), project.name)) 65 lines.append("%s : %s" % (_getpath(project), project.name))
65 66
66 lines.sort() 67 lines.sort()
67 print '\n'.join(lines) 68 print('\n'.join(lines))
68 69
69 def FindProjects(self, args): 70 def FindProjects(self, args):
70 result = [] 71 result = []
diff --git a/subcmds/manifest.py b/subcmds/manifest.py
index 5592a37d..5ceeb12f 100644
--- a/subcmds/manifest.py
+++ b/subcmds/manifest.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import os 17import os
17import sys 18import sys
18 19
@@ -69,7 +70,7 @@ in a Git repository for use during future 'repo init' invocations.
69 peg_rev_upstream = opt.peg_rev_upstream) 70 peg_rev_upstream = opt.peg_rev_upstream)
70 fd.close() 71 fd.close()
71 if opt.output_file != '-': 72 if opt.output_file != '-':
72 print >>sys.stderr, 'Saved manifest to %s' % opt.output_file 73 print('Saved manifest to %s' % opt.output_file, file=sys.stderr)
73 74
74 def Execute(self, opt, args): 75 def Execute(self, opt, args):
75 if args: 76 if args:
@@ -79,6 +80,6 @@ in a Git repository for use during future 'repo init' invocations.
79 self._Output(opt) 80 self._Output(opt)
80 return 81 return
81 82
82 print >>sys.stderr, 'error: no operation to perform' 83 print('error: no operation to perform', file=sys.stderr)
83 print >>sys.stderr, 'error: see repo help manifest' 84 print('error: see repo help manifest', file=sys.stderr)
84 sys.exit(1) 85 sys.exit(1)
diff --git a/subcmds/overview.py b/subcmds/overview.py
index a509bd9a..418459ae 100644
--- a/subcmds/overview.py
+++ b/subcmds/overview.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16from color import Coloring 17from color import Coloring
17from command import PagedCommand 18from command import PagedCommand
18 19
@@ -54,8 +55,11 @@ are displayed.
54 def __init__(self, config): 55 def __init__(self, config):
55 Coloring.__init__(self, config, 'status') 56 Coloring.__init__(self, config, 'status')
56 self.project = self.printer('header', attr='bold') 57 self.project = self.printer('header', attr='bold')
58 self.text = self.printer('text')
57 59
58 out = Report(all_branches[0].project.config) 60 out = Report(all_branches[0].project.config)
61 out.text("Deprecated. See repo info -o.")
62 out.nl()
59 out.project('Projects Overview') 63 out.project('Projects Overview')
60 out.nl() 64 out.nl()
61 65
@@ -70,11 +74,11 @@ are displayed.
70 74
71 commits = branch.commits 75 commits = branch.commits
72 date = branch.date 76 date = branch.date
73 print '%s %-33s (%2d commit%s, %s)' % ( 77 print('%s %-33s (%2d commit%s, %s)' % (
74 branch.name == project.CurrentBranch and '*' or ' ', 78 branch.name == project.CurrentBranch and '*' or ' ',
75 branch.name, 79 branch.name,
76 len(commits), 80 len(commits),
77 len(commits) != 1 and 's' or ' ', 81 len(commits) != 1 and 's' or ' ',
78 date) 82 date))
79 for commit in commits: 83 for commit in commits:
80 print '%-35s - %s' % ('', commit) 84 print('%-35s - %s' % ('', commit))
diff --git a/subcmds/prune.py b/subcmds/prune.py
index c50a5507..39c571a4 100644
--- a/subcmds/prune.py
+++ b/subcmds/prune.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16from color import Coloring 17from color import Coloring
17from command import PagedCommand 18from command import PagedCommand
18 19
@@ -51,9 +52,9 @@ class Prune(PagedCommand):
51 52
52 commits = branch.commits 53 commits = branch.commits
53 date = branch.date 54 date = branch.date
54 print '%s %-33s (%2d commit%s, %s)' % ( 55 print('%s %-33s (%2d commit%s, %s)' % (
55 branch.name == project.CurrentBranch and '*' or ' ', 56 branch.name == project.CurrentBranch and '*' or ' ',
56 branch.name, 57 branch.name,
57 len(commits), 58 len(commits),
58 len(commits) != 1 and 's' or ' ', 59 len(commits) != 1 and 's' or ' ',
59 date) 60 date))
diff --git a/subcmds/rebase.py b/subcmds/rebase.py
index a8d58cdb..06cda22c 100644
--- a/subcmds/rebase.py
+++ b/subcmds/rebase.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import sys 17import sys
17 18
18from command import Command 19from command import Command
@@ -59,14 +60,16 @@ branch but need to incorporate new upstream changes "underneath" them.
59 one_project = len(all_projects) == 1 60 one_project = len(all_projects) == 1
60 61
61 if opt.interactive and not one_project: 62 if opt.interactive and not one_project:
62 print >>sys.stderr, 'error: interactive rebase not supported with multiple projects' 63 print('error: interactive rebase not supported with multiple projects',
64 file=sys.stderr)
63 return -1 65 return -1
64 66
65 for project in all_projects: 67 for project in all_projects:
66 cb = project.CurrentBranch 68 cb = project.CurrentBranch
67 if not cb: 69 if not cb:
68 if one_project: 70 if one_project:
69 print >>sys.stderr, "error: project %s has a detatched HEAD" % project.relpath 71 print("error: project %s has a detatched HEAD" % project.relpath,
72 file=sys.stderr)
70 return -1 73 return -1
71 # ignore branches with detatched HEADs 74 # ignore branches with detatched HEADs
72 continue 75 continue
@@ -74,7 +77,8 @@ branch but need to incorporate new upstream changes "underneath" them.
74 upbranch = project.GetBranch(cb) 77 upbranch = project.GetBranch(cb)
75 if not upbranch.LocalMerge: 78 if not upbranch.LocalMerge:
76 if one_project: 79 if one_project:
77 print >>sys.stderr, "error: project %s does not track any remote branches" % project.relpath 80 print("error: project %s does not track any remote branches"
81 % project.relpath, file=sys.stderr)
78 return -1 82 return -1
79 # ignore branches without remotes 83 # ignore branches without remotes
80 continue 84 continue
@@ -101,8 +105,8 @@ branch but need to incorporate new upstream changes "underneath" them.
101 105
102 args.append(upbranch.LocalMerge) 106 args.append(upbranch.LocalMerge)
103 107
104 print >>sys.stderr, '# %s: rebasing %s -> %s' % \ 108 print('# %s: rebasing %s -> %s'
105 (project.relpath, cb, upbranch.LocalMerge) 109 % (project.relpath, cb, upbranch.LocalMerge), file=sys.stderr)
106 110
107 needs_stash = False 111 needs_stash = False
108 if opt.auto_stash: 112 if opt.auto_stash:
diff --git a/subcmds/selfupdate.py b/subcmds/selfupdate.py
index 46aa3a19..d12e08d0 100644
--- a/subcmds/selfupdate.py
+++ b/subcmds/selfupdate.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16from optparse import SUPPRESS_HELP 17from optparse import SUPPRESS_HELP
17import sys 18import sys
18 19
@@ -52,7 +53,7 @@ need to be performed by an end-user.
52 53
53 else: 54 else:
54 if not rp.Sync_NetworkHalf(): 55 if not rp.Sync_NetworkHalf():
55 print >>sys.stderr, "error: can't update repo" 56 print("error: can't update repo", file=sys.stderr)
56 sys.exit(1) 57 sys.exit(1)
57 58
58 rp.bare_git.gc('--auto') 59 rp.bare_git.gc('--auto')
diff --git a/subcmds/stage.py b/subcmds/stage.py
index 2ec48069..ff15ee0c 100644
--- a/subcmds/stage.py
+++ b/subcmds/stage.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import sys 17import sys
17 18
18from color import Coloring 19from color import Coloring
@@ -50,7 +51,7 @@ The '%prog' command stages files to prepare the next commit.
50 def _Interactive(self, opt, args): 51 def _Interactive(self, opt, args):
51 all_projects = filter(lambda x: x.IsDirty(), self.GetProjects(args)) 52 all_projects = filter(lambda x: x.IsDirty(), self.GetProjects(args))
52 if not all_projects: 53 if not all_projects:
53 print >>sys.stderr,'no projects have uncommitted modifications' 54 print('no projects have uncommitted modifications', file=sys.stderr)
54 return 55 return
55 56
56 out = _ProjectList(self.manifest.manifestProject.config) 57 out = _ProjectList(self.manifest.manifestProject.config)
@@ -58,7 +59,7 @@ The '%prog' command stages files to prepare the next commit.
58 out.header(' %s', 'project') 59 out.header(' %s', 'project')
59 out.nl() 60 out.nl()
60 61
61 for i in xrange(0, len(all_projects)): 62 for i in range(len(all_projects)):
62 p = all_projects[i] 63 p = all_projects[i]
63 out.write('%3d: %s', i + 1, p.relpath + '/') 64 out.write('%3d: %s', i + 1, p.relpath + '/')
64 out.nl() 65 out.nl()
@@ -101,7 +102,7 @@ The '%prog' command stages files to prepare the next commit.
101 if len(p) == 1: 102 if len(p) == 1:
102 _AddI(p[0]) 103 _AddI(p[0])
103 continue 104 continue
104 print 'Bye.' 105 print('Bye.')
105 106
106def _AddI(project): 107def _AddI(project):
107 p = GitCommand(project, ['add', '--interactive'], bare=False) 108 p = GitCommand(project, ['add', '--interactive'], bare=False)
diff --git a/subcmds/start.py b/subcmds/start.py
index be645314..2d723fc2 100644
--- a/subcmds/start.py
+++ b/subcmds/start.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import sys 17import sys
17from command import Command 18from command import Command
18from git_config import IsId 19from git_config import IsId
@@ -41,7 +42,7 @@ revision specified in the manifest.
41 42
42 nb = args[0] 43 nb = args[0]
43 if not git.check_ref_format('heads/%s' % nb): 44 if not git.check_ref_format('heads/%s' % nb):
44 print >>sys.stderr, "error: '%s' is not a valid name" % nb 45 print("error: '%s' is not a valid name" % nb, file=sys.stderr)
45 sys.exit(1) 46 sys.exit(1)
46 47
47 err = [] 48 err = []
@@ -49,7 +50,7 @@ revision specified in the manifest.
49 if not opt.all: 50 if not opt.all:
50 projects = args[1:] 51 projects = args[1:]
51 if len(projects) < 1: 52 if len(projects) < 1:
52 print >>sys.stderr, "error: at least one project must be specified" 53 print("error: at least one project must be specified", file=sys.stderr)
53 sys.exit(1) 54 sys.exit(1)
54 55
55 all_projects = self.GetProjects(projects) 56 all_projects = self.GetProjects(projects)
@@ -67,7 +68,6 @@ revision specified in the manifest.
67 68
68 if err: 69 if err:
69 for p in err: 70 for p in err:
70 print >>sys.stderr,\ 71 print("error: %s/: cannot start %s" % (p.relpath, nb),
71 "error: %s/: cannot start %s" \ 72 file=sys.stderr)
72 % (p.relpath, nb)
73 sys.exit(1) 73 sys.exit(1)
diff --git a/subcmds/status.py b/subcmds/status.py
index 7611621e..cce00c81 100644
--- a/subcmds/status.py
+++ b/subcmds/status.py
@@ -20,10 +20,14 @@ try:
20except ImportError: 20except ImportError:
21 import dummy_threading as _threading 21 import dummy_threading as _threading
22 22
23import glob
23import itertools 24import itertools
25import os
24import sys 26import sys
25import StringIO 27import StringIO
26 28
29from color import Coloring
30
27class Status(PagedCommand): 31class Status(PagedCommand):
28 common = True 32 common = True
29 helpSummary = "Show the working tree status" 33 helpSummary = "Show the working tree status"
@@ -39,6 +43,13 @@ is a difference between these three states.
39The -j/--jobs option can be used to run multiple status queries 43The -j/--jobs option can be used to run multiple status queries
40in parallel. 44in parallel.
41 45
46The -o/--orphans option can be used to show objects that are in
47the working directory, but not associated with a repo project.
48This includes unmanaged top-level files and directories, but also
49includes deeper items. For example, if dir/subdir/proj1 and
50dir/subdir/proj2 are repo projects, dir/subdir/proj3 will be shown
51if it is not known to repo.
52
42Status Display 53Status Display
43-------------- 54--------------
44 55
@@ -76,6 +87,9 @@ the following meanings:
76 p.add_option('-j', '--jobs', 87 p.add_option('-j', '--jobs',
77 dest='jobs', action='store', type='int', default=2, 88 dest='jobs', action='store', type='int', default=2,
78 help="number of projects to check simultaneously") 89 help="number of projects to check simultaneously")
90 p.add_option('-o', '--orphans',
91 dest='orphans', action='store_true',
92 help="include objects in working directory outside of repo projects")
79 93
80 def _StatusHelper(self, project, clean_counter, sem, output): 94 def _StatusHelper(self, project, clean_counter, sem, output):
81 """Obtains the status for a specific project. 95 """Obtains the status for a specific project.
@@ -97,6 +111,22 @@ the following meanings:
97 finally: 111 finally:
98 sem.release() 112 sem.release()
99 113
114 def _FindOrphans(self, dirs, proj_dirs, proj_dirs_parents, outstring):
115 """find 'dirs' that are present in 'proj_dirs_parents' but not in 'proj_dirs'"""
116 status_header = ' --\t'
117 for item in dirs:
118 if not os.path.isdir(item):
119 outstring.write(''.join([status_header, item]))
120 continue
121 if item in proj_dirs:
122 continue
123 if item in proj_dirs_parents:
124 self._FindOrphans(glob.glob('%s/.*' % item) + \
125 glob.glob('%s/*' % item), \
126 proj_dirs, proj_dirs_parents, outstring)
127 continue
128 outstring.write(''.join([status_header, item, '/']))
129
100 def Execute(self, opt, args): 130 def Execute(self, opt, args):
101 all_projects = self.GetProjects(args) 131 all_projects = self.GetProjects(args)
102 counter = itertools.count() 132 counter = itertools.count()
@@ -129,4 +159,46 @@ the following meanings:
129 output.dump(sys.stdout) 159 output.dump(sys.stdout)
130 output.close() 160 output.close()
131 if len(all_projects) == counter.next(): 161 if len(all_projects) == counter.next():
132 print 'nothing to commit (working directory clean)' 162 print('nothing to commit (working directory clean)')
163
164 if opt.orphans:
165 proj_dirs = set()
166 proj_dirs_parents = set()
167 for project in self.GetProjects(None, missing_ok=True):
168 proj_dirs.add(project.relpath)
169 (head, _tail) = os.path.split(project.relpath)
170 while head != "":
171 proj_dirs_parents.add(head)
172 (head, _tail) = os.path.split(head)
173 proj_dirs.add('.repo')
174
175 class StatusColoring(Coloring):
176 def __init__(self, config):
177 Coloring.__init__(self, config, 'status')
178 self.project = self.printer('header', attr = 'bold')
179 self.untracked = self.printer('untracked', fg = 'red')
180
181 orig_path = os.getcwd()
182 try:
183 os.chdir(self.manifest.topdir)
184
185 outstring = StringIO.StringIO()
186 self._FindOrphans(glob.glob('.*') + \
187 glob.glob('*'), \
188 proj_dirs, proj_dirs_parents, outstring)
189
190 if outstring.buflist:
191 output = StatusColoring(self.manifest.globalConfig)
192 output.project('Objects not within a project (orphans)')
193 output.nl()
194 for entry in outstring.buflist:
195 output.untracked(entry)
196 output.nl()
197 else:
198 print('No orphan files or directories')
199
200 outstring.close()
201
202 finally:
203 # Restore CWD.
204 os.chdir(orig_path)
diff --git a/subcmds/sync.py b/subcmds/sync.py
index d4637d0c..228a279a 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import netrc 17import netrc
17from optparse import SUPPRESS_HELP 18from optparse import SUPPRESS_HELP
18import os 19import os
@@ -44,13 +45,13 @@ try:
44except ImportError: 45except ImportError:
45 multiprocessing = None 46 multiprocessing = None
46 47
47from git_command import GIT 48from git_command import GIT, git_require
48from git_refs import R_HEADS, HEAD 49from git_refs import R_HEADS, HEAD
49from main import WrapperModule 50from main import WrapperModule
50from project import Project 51from project import Project
51from project import RemoteSpec 52from project import RemoteSpec
52from command import Command, MirrorSafeCommand 53from command import Command, MirrorSafeCommand
53from error import RepoChangedException, GitError 54from error import RepoChangedException, GitError, ManifestParseError
54from project import SyncBuffer 55from project import SyncBuffer
55from progress import Progress 56from progress import Progress
56 57
@@ -113,6 +114,9 @@ resumeable bundle file on a content delivery network. This
113may be necessary if there are problems with the local Python 114may be necessary if there are problems with the local Python
114HTTP client or proxy configuration, but the Git binary works. 115HTTP client or proxy configuration, but the Git binary works.
115 116
117The --fetch-submodules option enables fetching Git submodules
118of a project from server.
119
116SSH Connections 120SSH Connections
117--------------- 121---------------
118 122
@@ -144,27 +148,30 @@ later is required to fix a server side protocol bug.
144""" 148"""
145 149
146 def _Options(self, p, show_smart=True): 150 def _Options(self, p, show_smart=True):
147 self.jobs = self.manifest.default.sync_j 151 try:
152 self.jobs = self.manifest.default.sync_j
153 except ManifestParseError:
154 self.jobs = 1
148 155
149 p.add_option('-f', '--force-broken', 156 p.add_option('-f', '--force-broken',
150 dest='force_broken', action='store_true', 157 dest='force_broken', action='store_true',
151 help="continue sync even if a project fails to sync") 158 help="continue sync even if a project fails to sync")
152 p.add_option('-l','--local-only', 159 p.add_option('-l', '--local-only',
153 dest='local_only', action='store_true', 160 dest='local_only', action='store_true',
154 help="only update working tree, don't fetch") 161 help="only update working tree, don't fetch")
155 p.add_option('-n','--network-only', 162 p.add_option('-n', '--network-only',
156 dest='network_only', action='store_true', 163 dest='network_only', action='store_true',
157 help="fetch only, don't update working tree") 164 help="fetch only, don't update working tree")
158 p.add_option('-d','--detach', 165 p.add_option('-d', '--detach',
159 dest='detach_head', action='store_true', 166 dest='detach_head', action='store_true',
160 help='detach projects back to manifest revision') 167 help='detach projects back to manifest revision')
161 p.add_option('-c','--current-branch', 168 p.add_option('-c', '--current-branch',
162 dest='current_branch_only', action='store_true', 169 dest='current_branch_only', action='store_true',
163 help='fetch only current branch from server') 170 help='fetch only current branch from server')
164 p.add_option('-q','--quiet', 171 p.add_option('-q', '--quiet',
165 dest='quiet', action='store_true', 172 dest='quiet', action='store_true',
166 help='be more quiet') 173 help='be more quiet')
167 p.add_option('-j','--jobs', 174 p.add_option('-j', '--jobs',
168 dest='jobs', action='store', type='int', 175 dest='jobs', action='store', type='int',
169 help="projects to fetch simultaneously (default %d)" % self.jobs) 176 help="projects to fetch simultaneously (default %d)" % self.jobs)
170 p.add_option('-m', '--manifest-name', 177 p.add_option('-m', '--manifest-name',
@@ -173,6 +180,15 @@ later is required to fix a server side protocol bug.
173 p.add_option('--no-clone-bundle', 180 p.add_option('--no-clone-bundle',
174 dest='no_clone_bundle', action='store_true', 181 dest='no_clone_bundle', action='store_true',
175 help='disable use of /clone.bundle on HTTP/HTTPS') 182 help='disable use of /clone.bundle on HTTP/HTTPS')
183 p.add_option('-u', '--manifest-server-username', action='store',
184 dest='manifest_server_username',
185 help='username to authenticate with the manifest server')
186 p.add_option('-p', '--manifest-server-password', action='store',
187 dest='manifest_server_password',
188 help='password to authenticate with the manifest server')
189 p.add_option('--fetch-submodules',
190 dest='fetch_submodules', action='store_true',
191 help='fetch submodules from server')
176 if show_smart: 192 if show_smart:
177 p.add_option('-s', '--smart-sync', 193 p.add_option('-s', '--smart-sync',
178 dest='smart_sync', action='store_true', 194 dest='smart_sync', action='store_true',
@@ -180,12 +196,6 @@ later is required to fix a server side protocol bug.
180 p.add_option('-t', '--smart-tag', 196 p.add_option('-t', '--smart-tag',
181 dest='smart_tag', action='store', 197 dest='smart_tag', action='store',
182 help='smart sync using manifest from a known tag') 198 help='smart sync using manifest from a known tag')
183 p.add_option('-u', '--manifest-server-username', action='store',
184 dest='manifest_server_username',
185 help='username to authenticate with the manifest server')
186 p.add_option('-p', '--manifest-server-password', action='store',
187 dest='manifest_server_password',
188 help='password to authenticate with the manifest server')
189 199
190 g = p.add_option_group('repo Version options') 200 g = p.add_option_group('repo Version options')
191 g.add_option('--no-repo-verify', 201 g.add_option('--no-repo-verify',
@@ -196,61 +206,62 @@ later is required to fix a server side protocol bug.
196 help=SUPPRESS_HELP) 206 help=SUPPRESS_HELP)
197 207
198 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): 208 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
199 """Main function of the fetch threads when jobs are > 1. 209 """Main function of the fetch threads when jobs are > 1.
200 210
201 Args: 211 Args:
202 opt: Program options returned from optparse. See _Options(). 212 opt: Program options returned from optparse. See _Options().
203 project: Project object for the project to fetch. 213 project: Project object for the project to fetch.
204 lock: Lock for accessing objects that are shared amongst multiple 214 lock: Lock for accessing objects that are shared amongst multiple
205 _FetchHelper() threads. 215 _FetchHelper() threads.
206 fetched: set object that we will add project.gitdir to when we're done 216 fetched: set object that we will add project.gitdir to when we're done
207 (with our lock held). 217 (with our lock held).
208 pm: Instance of a Project object. We will call pm.update() (with our 218 pm: Instance of a Project object. We will call pm.update() (with our
209 lock held). 219 lock held).
210 sem: We'll release() this semaphore when we exit so that another thread 220 sem: We'll release() this semaphore when we exit so that another thread
211 can be started up. 221 can be started up.
212 err_event: We'll set this event in the case of an error (after printing 222 err_event: We'll set this event in the case of an error (after printing
213 out info about the error). 223 out info about the error).
214 """ 224 """
215 # We'll set to true once we've locked the lock. 225 # We'll set to true once we've locked the lock.
216 did_lock = False 226 did_lock = False
217 227
218 # Encapsulate everything in a try/except/finally so that: 228 # Encapsulate everything in a try/except/finally so that:
219 # - We always set err_event in the case of an exception. 229 # - We always set err_event in the case of an exception.
220 # - We always make sure we call sem.release(). 230 # - We always make sure we call sem.release().
221 # - We always make sure we unlock the lock if we locked it. 231 # - We always make sure we unlock the lock if we locked it.
232 try:
222 try: 233 try:
223 try: 234 start = time.time()
224 start = time.time() 235 success = project.Sync_NetworkHalf(
225 success = project.Sync_NetworkHalf( 236 quiet=opt.quiet,
226 quiet=opt.quiet, 237 current_branch_only=opt.current_branch_only,
227 current_branch_only=opt.current_branch_only, 238 clone_bundle=not opt.no_clone_bundle)
228 clone_bundle=not opt.no_clone_bundle) 239 self._fetch_times.Set(project, time.time() - start)
229 self._fetch_times.Set(project, time.time() - start) 240
230 241 # Lock around all the rest of the code, since printing, updating a set
231 # Lock around all the rest of the code, since printing, updating a set 242 # and Progress.update() are not thread safe.
232 # and Progress.update() are not thread safe. 243 lock.acquire()
233 lock.acquire() 244 did_lock = True
234 did_lock = True 245
235 246 if not success:
236 if not success: 247 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
237 print >>sys.stderr, 'error: Cannot fetch %s' % project.name 248 if opt.force_broken:
238 if opt.force_broken: 249 print('warn: --force-broken, continuing to sync',
239 print >>sys.stderr, 'warn: --force-broken, continuing to sync' 250 file=sys.stderr)
240 else: 251 else:
241 raise _FetchError() 252 raise _FetchError()
242 253
243 fetched.add(project.gitdir) 254 fetched.add(project.gitdir)
244 pm.update() 255 pm.update()
245 except _FetchError: 256 except _FetchError:
246 err_event.set() 257 err_event.set()
247 except: 258 except:
248 err_event.set() 259 err_event.set()
249 raise 260 raise
250 finally: 261 finally:
251 if did_lock: 262 if did_lock:
252 lock.release() 263 lock.release()
253 sem.release() 264 sem.release()
254 265
255 def _Fetch(self, projects, opt): 266 def _Fetch(self, projects, opt):
256 fetched = set() 267 fetched = set()
@@ -265,9 +276,9 @@ later is required to fix a server side protocol bug.
265 clone_bundle=not opt.no_clone_bundle): 276 clone_bundle=not opt.no_clone_bundle):
266 fetched.add(project.gitdir) 277 fetched.add(project.gitdir)
267 else: 278 else:
268 print >>sys.stderr, 'error: Cannot fetch %s' % project.name 279 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
269 if opt.force_broken: 280 if opt.force_broken:
270 print >>sys.stderr, 'warn: --force-broken, continuing to sync' 281 print('warn: --force-broken, continuing to sync', file=sys.stderr)
271 else: 282 else:
272 sys.exit(1) 283 sys.exit(1)
273 else: 284 else:
@@ -300,7 +311,7 @@ later is required to fix a server side protocol bug.
300 311
301 # If we saw an error, exit with code 1 so that other scripts can check. 312 # If we saw an error, exit with code 1 so that other scripts can check.
302 if err_event.isSet(): 313 if err_event.isSet():
303 print >>sys.stderr, '\nerror: Exited sync due to fetch errors' 314 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
304 sys.exit(1) 315 sys.exit(1)
305 316
306 pm.end() 317 pm.end()
@@ -310,7 +321,8 @@ later is required to fix a server side protocol bug.
310 return fetched 321 return fetched
311 322
312 def _GCProjects(self, projects): 323 def _GCProjects(self, projects):
313 if multiprocessing: 324 has_dash_c = git_require((1, 7, 2))
325 if multiprocessing and has_dash_c:
314 cpu_count = multiprocessing.cpu_count() 326 cpu_count = multiprocessing.cpu_count()
315 else: 327 else:
316 cpu_count = 1 328 cpu_count = 1
@@ -352,7 +364,7 @@ later is required to fix a server side protocol bug.
352 t.join() 364 t.join()
353 365
354 if err_event.isSet(): 366 if err_event.isSet():
355 print >>sys.stderr, '\nerror: Exited sync due to gc errors' 367 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
356 sys.exit(1) 368 sys.exit(1)
357 369
358 def UpdateProjectList(self): 370 def UpdateProjectList(self):
@@ -376,34 +388,36 @@ later is required to fix a server side protocol bug.
376 if path not in new_project_paths: 388 if path not in new_project_paths:
377 # If the path has already been deleted, we don't need to do it 389 # If the path has already been deleted, we don't need to do it
378 if os.path.exists(self.manifest.topdir + '/' + path): 390 if os.path.exists(self.manifest.topdir + '/' + path):
379 project = Project( 391 project = Project(
380 manifest = self.manifest, 392 manifest = self.manifest,
381 name = path, 393 name = path,
382 remote = RemoteSpec('origin'), 394 remote = RemoteSpec('origin'),
383 gitdir = os.path.join(self.manifest.topdir, 395 gitdir = os.path.join(self.manifest.topdir,
384 path, '.git'), 396 path, '.git'),
385 worktree = os.path.join(self.manifest.topdir, path), 397 worktree = os.path.join(self.manifest.topdir, path),
386 relpath = path, 398 relpath = path,
387 revisionExpr = 'HEAD', 399 revisionExpr = 'HEAD',
388 revisionId = None, 400 revisionId = None,
389 groups = None) 401 groups = None)
390 402
391 if project.IsDirty(): 403 if project.IsDirty():
392 print >>sys.stderr, 'error: Cannot remove project "%s": \ 404 print('error: Cannot remove project "%s": uncommitted changes'
393uncommitted changes are present' % project.relpath 405 'are present' % project.relpath, file=sys.stderr)
394 print >>sys.stderr, ' commit changes, then run sync again' 406 print(' commit changes, then run sync again',
395 return -1 407 file=sys.stderr)
396 else: 408 return -1
397 print >>sys.stderr, 'Deleting obsolete path %s' % project.worktree 409 else:
398 shutil.rmtree(project.worktree) 410 print('Deleting obsolete path %s' % project.worktree,
399 # Try deleting parent subdirs if they are empty 411 file=sys.stderr)
400 project_dir = os.path.dirname(project.worktree) 412 shutil.rmtree(project.worktree)
401 while project_dir != self.manifest.topdir: 413 # Try deleting parent subdirs if they are empty
402 try: 414 project_dir = os.path.dirname(project.worktree)
403 os.rmdir(project_dir) 415 while project_dir != self.manifest.topdir:
404 except OSError: 416 try:
405 break 417 os.rmdir(project_dir)
406 project_dir = os.path.dirname(project_dir) 418 except OSError:
419 break
420 project_dir = os.path.dirname(project_dir)
407 421
408 new_project_paths.sort() 422 new_project_paths.sort()
409 fd = open(file_path, 'w') 423 fd = open(file_path, 'w')
@@ -422,24 +436,24 @@ uncommitted changes are present' % project.relpath
422 self.jobs = min(self.jobs, (soft_limit - 5) / 3) 436 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
423 437
424 if opt.network_only and opt.detach_head: 438 if opt.network_only and opt.detach_head:
425 print >>sys.stderr, 'error: cannot combine -n and -d' 439 print('error: cannot combine -n and -d', file=sys.stderr)
426 sys.exit(1) 440 sys.exit(1)
427 if opt.network_only and opt.local_only: 441 if opt.network_only and opt.local_only:
428 print >>sys.stderr, 'error: cannot combine -n and -l' 442 print('error: cannot combine -n and -l', file=sys.stderr)
429 sys.exit(1) 443 sys.exit(1)
430 if opt.manifest_name and opt.smart_sync: 444 if opt.manifest_name and opt.smart_sync:
431 print >>sys.stderr, 'error: cannot combine -m and -s' 445 print('error: cannot combine -m and -s', file=sys.stderr)
432 sys.exit(1) 446 sys.exit(1)
433 if opt.manifest_name and opt.smart_tag: 447 if opt.manifest_name and opt.smart_tag:
434 print >>sys.stderr, 'error: cannot combine -m and -t' 448 print('error: cannot combine -m and -t', file=sys.stderr)
435 sys.exit(1) 449 sys.exit(1)
436 if opt.manifest_server_username or opt.manifest_server_password: 450 if opt.manifest_server_username or opt.manifest_server_password:
437 if not (opt.smart_sync or opt.smart_tag): 451 if not (opt.smart_sync or opt.smart_tag):
438 print >>sys.stderr, 'error: -u and -p may only be combined with ' \ 452 print('error: -u and -p may only be combined with -s or -t',
439 '-s or -t' 453 file=sys.stderr)
440 sys.exit(1) 454 sys.exit(1)
441 if None in [opt.manifest_server_username, opt.manifest_server_password]: 455 if None in [opt.manifest_server_username, opt.manifest_server_password]:
442 print >>sys.stderr, 'error: both -u and -p must be given' 456 print('error: both -u and -p must be given', file=sys.stderr)
443 sys.exit(1) 457 sys.exit(1)
444 458
445 if opt.manifest_name: 459 if opt.manifest_name:
@@ -447,8 +461,8 @@ uncommitted changes are present' % project.relpath
447 461
448 if opt.smart_sync or opt.smart_tag: 462 if opt.smart_sync or opt.smart_tag:
449 if not self.manifest.manifest_server: 463 if not self.manifest.manifest_server:
450 print >>sys.stderr, \ 464 print('error: cannot smart sync: no manifest server defined in'
451 'error: cannot smart sync: no manifest server defined in manifest' 465 'manifest', file=sys.stderr)
452 sys.exit(1) 466 sys.exit(1)
453 467
454 manifest_server = self.manifest.manifest_server 468 manifest_server = self.manifest.manifest_server
@@ -463,7 +477,8 @@ uncommitted changes are present' % project.relpath
463 try: 477 try:
464 info = netrc.netrc() 478 info = netrc.netrc()
465 except IOError: 479 except IOError:
466 print >>sys.stderr, '.netrc file does not exist or could not be opened' 480 print('.netrc file does not exist or could not be opened',
481 file=sys.stderr)
467 else: 482 else:
468 try: 483 try:
469 parse_result = urlparse.urlparse(manifest_server) 484 parse_result = urlparse.urlparse(manifest_server)
@@ -473,10 +488,10 @@ uncommitted changes are present' % project.relpath
473 except TypeError: 488 except TypeError:
474 # TypeError is raised when the given hostname is not present 489 # TypeError is raised when the given hostname is not present
475 # in the .netrc file. 490 # in the .netrc file.
476 print >>sys.stderr, 'No credentials found for %s in .netrc' % \ 491 print('No credentials found for %s in .netrc'
477 parse_result.hostname 492 % parse_result.hostname, file=sys.stderr)
478 except netrc.NetrcParseError as e: 493 except netrc.NetrcParseError as e:
479 print >>sys.stderr, 'Error parsing .netrc file: %s' % e 494 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
480 495
481 if (username and password): 496 if (username and password):
482 manifest_server = manifest_server.replace('://', '://%s:%s@' % 497 manifest_server = manifest_server.replace('://', '://%s:%s@' %
@@ -515,20 +530,21 @@ uncommitted changes are present' % project.relpath
515 finally: 530 finally:
516 f.close() 531 f.close()
517 except IOError: 532 except IOError:
518 print >>sys.stderr, 'error: cannot write manifest to %s' % \ 533 print('error: cannot write manifest to %s' % manifest_path,
519 manifest_path 534 file=sys.stderr)
520 sys.exit(1) 535 sys.exit(1)
521 self.manifest.Override(manifest_name) 536 self.manifest.Override(manifest_name)
522 else: 537 else:
523 print >>sys.stderr, 'error: %s' % manifest_str 538 print('error: %s' % manifest_str, file=sys.stderr)
524 sys.exit(1) 539 sys.exit(1)
525 except (socket.error, IOError, xmlrpclib.Fault) as e: 540 except (socket.error, IOError, xmlrpclib.Fault) as e:
526 print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%s' % ( 541 print('error: cannot connect to manifest server %s:\n%s'
527 self.manifest.manifest_server, e) 542 % (self.manifest.manifest_server, e), file=sys.stderr)
528 sys.exit(1) 543 sys.exit(1)
529 except xmlrpclib.ProtocolError as e: 544 except xmlrpclib.ProtocolError as e:
530 print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%d %s' % ( 545 print('error: cannot connect to manifest server %s:\n%d %s'
531 self.manifest.manifest_server, e.errcode, e.errmsg) 546 % (self.manifest.manifest_server, e.errcode, e.errmsg),
547 file=sys.stderr)
532 sys.exit(1) 548 sys.exit(1)
533 549
534 rp = self.manifest.repoProject 550 rp = self.manifest.repoProject
@@ -552,7 +568,9 @@ uncommitted changes are present' % project.relpath
552 self.manifest._Unload() 568 self.manifest._Unload()
553 if opt.jobs is None: 569 if opt.jobs is None:
554 self.jobs = self.manifest.default.sync_j 570 self.jobs = self.manifest.default.sync_j
555 all_projects = self.GetProjects(args, missing_ok=True) 571 all_projects = self.GetProjects(args,
572 missing_ok=True,
573 submodules_ok=opt.fetch_submodules)
556 574
557 self._fetch_times = _FetchTimes(self.manifest) 575 self._fetch_times = _FetchTimes(self.manifest)
558 if not opt.local_only: 576 if not opt.local_only:
@@ -563,12 +581,33 @@ uncommitted changes are present' % project.relpath
563 to_fetch.extend(all_projects) 581 to_fetch.extend(all_projects)
564 to_fetch.sort(key=self._fetch_times.Get, reverse=True) 582 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
565 583
566 self._Fetch(to_fetch, opt) 584 fetched = self._Fetch(to_fetch, opt)
567 _PostRepoFetch(rp, opt.no_repo_verify) 585 _PostRepoFetch(rp, opt.no_repo_verify)
568 if opt.network_only: 586 if opt.network_only:
569 # bail out now; the rest touches the working tree 587 # bail out now; the rest touches the working tree
570 return 588 return
571 589
590 # Iteratively fetch missing and/or nested unregistered submodules
591 previously_missing_set = set()
592 while True:
593 self.manifest._Unload()
594 all_projects = self.GetProjects(args,
595 missing_ok=True,
596 submodules_ok=opt.fetch_submodules)
597 missing = []
598 for project in all_projects:
599 if project.gitdir not in fetched:
600 missing.append(project)
601 if not missing:
602 break
603 # Stop us from non-stopped fetching actually-missing repos: If set of
604 # missing repos has not been changed from last fetch, we break.
605 missing_set = set(p.name for p in missing)
606 if previously_missing_set == missing_set:
607 break
608 previously_missing_set = missing_set
609 fetched.update(self._Fetch(missing, opt))
610
572 if self.manifest.IsMirror: 611 if self.manifest.IsMirror:
573 # bail out now, we have no working tree 612 # bail out now, we have no working tree
574 return 613 return
@@ -584,14 +623,14 @@ uncommitted changes are present' % project.relpath
584 if project.worktree: 623 if project.worktree:
585 project.Sync_LocalHalf(syncbuf) 624 project.Sync_LocalHalf(syncbuf)
586 pm.end() 625 pm.end()
587 print >>sys.stderr 626 print(file=sys.stderr)
588 if not syncbuf.Finish(): 627 if not syncbuf.Finish():
589 sys.exit(1) 628 sys.exit(1)
590 629
591 # If there's a notice that's supposed to print at the end of the sync, print 630 # If there's a notice that's supposed to print at the end of the sync, print
592 # it now... 631 # it now...
593 if self.manifest.notice: 632 if self.manifest.notice:
594 print self.manifest.notice 633 print(self.manifest.notice)
595 634
596def _PostRepoUpgrade(manifest, quiet=False): 635def _PostRepoUpgrade(manifest, quiet=False):
597 wrapper = WrapperModule() 636 wrapper = WrapperModule()
@@ -603,27 +642,28 @@ def _PostRepoUpgrade(manifest, quiet=False):
603 642
604def _PostRepoFetch(rp, no_repo_verify=False, verbose=False): 643def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
605 if rp.HasChanges: 644 if rp.HasChanges:
606 print >>sys.stderr, 'info: A new version of repo is available' 645 print('info: A new version of repo is available', file=sys.stderr)
607 print >>sys.stderr, '' 646 print(file=sys.stderr)
608 if no_repo_verify or _VerifyTag(rp): 647 if no_repo_verify or _VerifyTag(rp):
609 syncbuf = SyncBuffer(rp.config) 648 syncbuf = SyncBuffer(rp.config)
610 rp.Sync_LocalHalf(syncbuf) 649 rp.Sync_LocalHalf(syncbuf)
611 if not syncbuf.Finish(): 650 if not syncbuf.Finish():
612 sys.exit(1) 651 sys.exit(1)
613 print >>sys.stderr, 'info: Restarting repo with latest version' 652 print('info: Restarting repo with latest version', file=sys.stderr)
614 raise RepoChangedException(['--repo-upgraded']) 653 raise RepoChangedException(['--repo-upgraded'])
615 else: 654 else:
616 print >>sys.stderr, 'warning: Skipped upgrade to unverified version' 655 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
617 else: 656 else:
618 if verbose: 657 if verbose:
619 print >>sys.stderr, 'repo version %s is current' % rp.work_git.describe(HEAD) 658 print('repo version %s is current' % rp.work_git.describe(HEAD),
659 file=sys.stderr)
620 660
621def _VerifyTag(project): 661def _VerifyTag(project):
622 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg') 662 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
623 if not os.path.exists(gpg_dir): 663 if not os.path.exists(gpg_dir):
624 print >>sys.stderr,\ 664 print('warning: GnuPG was not available during last "repo init"\n'
625"""warning: GnuPG was not available during last "repo init" 665 'warning: Cannot automatically authenticate repo."""',
626warning: Cannot automatically authenticate repo.""" 666 file=sys.stderr)
627 return True 667 return True
628 668
629 try: 669 try:
@@ -637,10 +677,9 @@ warning: Cannot automatically authenticate repo."""
637 if rev.startswith(R_HEADS): 677 if rev.startswith(R_HEADS):
638 rev = rev[len(R_HEADS):] 678 rev = rev[len(R_HEADS):]
639 679
640 print >>sys.stderr 680 print(file=sys.stderr)
641 print >>sys.stderr,\ 681 print("warning: project '%s' branch '%s' is not signed"
642 "warning: project '%s' branch '%s' is not signed" \ 682 % (project.name, rev), file=sys.stderr)
643 % (project.name, rev)
644 return False 683 return False
645 684
646 env = os.environ.copy() 685 env = os.environ.copy()
@@ -659,10 +698,10 @@ warning: Cannot automatically authenticate repo."""
659 proc.stderr.close() 698 proc.stderr.close()
660 699
661 if proc.wait() != 0: 700 if proc.wait() != 0:
662 print >>sys.stderr 701 print(file=sys.stderr)
663 print >>sys.stderr, out 702 print(out, file=sys.stderr)
664 print >>sys.stderr, err 703 print(err, file=sys.stderr)
665 print >>sys.stderr 704 print(file=sys.stderr)
666 return False 705 return False
667 return True 706 return True
668 707
@@ -696,7 +735,7 @@ class _FetchTimes(object):
696 try: 735 try:
697 try: 736 try:
698 self._times = pickle.load(f) 737 self._times = pickle.load(f)
699 except: 738 except IOError:
700 try: 739 try:
701 os.remove(self._path) 740 os.remove(self._path)
702 except OSError: 741 except OSError:
diff --git a/subcmds/upload.py b/subcmds/upload.py
index 84a5e440..e314032a 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import copy 17import copy
17import re 18import re
18import sys 19import sys
@@ -26,16 +27,18 @@ UNUSUAL_COMMIT_THRESHOLD = 5
26 27
27def _ConfirmManyUploads(multiple_branches=False): 28def _ConfirmManyUploads(multiple_branches=False):
28 if multiple_branches: 29 if multiple_branches:
29 print "ATTENTION: One or more branches has an unusually high number of commits." 30 print('ATTENTION: One or more branches has an unusually high number'
31 'of commits.')
30 else: 32 else:
31 print "ATTENTION: You are uploading an unusually high number of commits." 33 print('ATTENTION: You are uploading an unusually high number of commits.')
32 print "YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across branches?)" 34 print('YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across'
35 'branches?)')
33 answer = raw_input("If you are sure you intend to do this, type 'yes': ").strip() 36 answer = raw_input("If you are sure you intend to do this, type 'yes': ").strip()
34 return answer == "yes" 37 return answer == "yes"
35 38
36def _die(fmt, *args): 39def _die(fmt, *args):
37 msg = fmt % args 40 msg = fmt % args
38 print >>sys.stderr, 'error: %s' % msg 41 print('error: %s' % msg, file=sys.stderr)
39 sys.exit(1) 42 sys.exit(1)
40 43
41def _SplitEmails(values): 44def _SplitEmails(values):
@@ -47,7 +50,7 @@ def _SplitEmails(values):
47class Upload(InteractiveCommand): 50class Upload(InteractiveCommand):
48 common = True 51 common = True
49 helpSummary = "Upload changes for code review" 52 helpSummary = "Upload changes for code review"
50 helpUsage=""" 53 helpUsage = """
51%prog [--re --cc] [<project>]... 54%prog [--re --cc] [<project>]...
52""" 55"""
53 helpDescription = """ 56 helpDescription = """
@@ -176,18 +179,18 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
176 date = branch.date 179 date = branch.date
177 commit_list = branch.commits 180 commit_list = branch.commits
178 181
179 print 'Upload project %s/ to remote branch %s:' % (project.relpath, project.revisionExpr) 182 print('Upload project %s/ to remote branch %s:' % (project.relpath, project.revisionExpr))
180 print ' branch %s (%2d commit%s, %s):' % ( 183 print(' branch %s (%2d commit%s, %s):' % (
181 name, 184 name,
182 len(commit_list), 185 len(commit_list),
183 len(commit_list) != 1 and 's' or '', 186 len(commit_list) != 1 and 's' or '',
184 date) 187 date))
185 for commit in commit_list: 188 for commit in commit_list:
186 print ' %s' % commit 189 print(' %s' % commit)
187 190
188 sys.stdout.write('to %s (y/N)? ' % remote.review) 191 sys.stdout.write('to %s (y/N)? ' % remote.review)
189 answer = sys.stdin.readline().strip() 192 answer = sys.stdin.readline().strip().lower()
190 answer = answer in ('y', 'Y', 'yes', '1', 'true', 't') 193 answer = answer in ('y', 'yes', '1', 'true', 't')
191 194
192 if answer: 195 if answer:
193 if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD: 196 if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
@@ -297,7 +300,7 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
297 try: 300 try:
298 # refs/changes/XYZ/N --> XYZ 301 # refs/changes/XYZ/N --> XYZ
299 return refs.get(last_pub).split('/')[-2] 302 return refs.get(last_pub).split('/')[-2]
300 except: 303 except (AttributeError, IndexError):
301 return "" 304 return ""
302 305
303 def _UploadAndReport(self, opt, todo, original_people): 306 def _UploadAndReport(self, opt, todo, original_people):
@@ -309,23 +312,23 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
309 312
310 # Check if there are local changes that may have been forgotten 313 # Check if there are local changes that may have been forgotten
311 if branch.project.HasChanges(): 314 if branch.project.HasChanges():
312 key = 'review.%s.autoupload' % branch.project.remote.review 315 key = 'review.%s.autoupload' % branch.project.remote.review
313 answer = branch.project.config.GetBoolean(key) 316 answer = branch.project.config.GetBoolean(key)
314 317
315 # if they want to auto upload, let's not ask because it could be automated 318 # if they want to auto upload, let's not ask because it could be automated
316 if answer is None: 319 if answer is None:
317 sys.stdout.write('Uncommitted changes in ' + branch.project.name + ' (did you forget to amend?). Continue uploading? (y/N) ') 320 sys.stdout.write('Uncommitted changes in ' + branch.project.name + ' (did you forget to amend?). Continue uploading? (y/N) ')
318 a = sys.stdin.readline().strip().lower() 321 a = sys.stdin.readline().strip().lower()
319 if a not in ('y', 'yes', 't', 'true', 'on'): 322 if a not in ('y', 'yes', 't', 'true', 'on'):
320 print >>sys.stderr, "skipping upload" 323 print("skipping upload", file=sys.stderr)
321 branch.uploaded = False 324 branch.uploaded = False
322 branch.error = 'User aborted' 325 branch.error = 'User aborted'
323 continue 326 continue
324 327
325 # Check if topic branches should be sent to the server during upload 328 # Check if topic branches should be sent to the server during upload
326 if opt.auto_topic is not True: 329 if opt.auto_topic is not True:
327 key = 'review.%s.uploadtopic' % branch.project.remote.review 330 key = 'review.%s.uploadtopic' % branch.project.remote.review
328 opt.auto_topic = branch.project.config.GetBoolean(key) 331 opt.auto_topic = branch.project.config.GetBoolean(key)
329 332
330 branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft) 333 branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft)
331 branch.uploaded = True 334 branch.uploaded = True
@@ -334,8 +337,8 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
334 branch.uploaded = False 337 branch.uploaded = False
335 have_errors = True 338 have_errors = True
336 339
337 print >>sys.stderr, '' 340 print(file=sys.stderr)
338 print >>sys.stderr, '----------------------------------------------------------------------' 341 print('----------------------------------------------------------------------', file=sys.stderr)
339 342
340 if have_errors: 343 if have_errors:
341 for branch in todo: 344 for branch in todo:
@@ -344,17 +347,19 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
344 fmt = ' (%s)' 347 fmt = ' (%s)'
345 else: 348 else:
346 fmt = '\n (%s)' 349 fmt = '\n (%s)'
347 print >>sys.stderr, ('[FAILED] %-15s %-15s' + fmt) % ( 350 print(('[FAILED] %-15s %-15s' + fmt) % (
348 branch.project.relpath + '/', \ 351 branch.project.relpath + '/', \
349 branch.name, \ 352 branch.name, \
350 str(branch.error)) 353 str(branch.error)),
351 print >>sys.stderr, '' 354 file=sys.stderr)
355 print()
352 356
353 for branch in todo: 357 for branch in todo:
354 if branch.uploaded: 358 if branch.uploaded:
355 print >>sys.stderr, '[OK ] %-15s %s' % ( 359 print('[OK ] %-15s %s' % (
356 branch.project.relpath + '/', 360 branch.project.relpath + '/',
357 branch.name) 361 branch.name),
362 file=sys.stderr)
358 363
359 if have_errors: 364 if have_errors:
360 sys.exit(1) 365 sys.exit(1)
@@ -385,17 +390,17 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
385 try: 390 try:
386 hook.Run(opt.allow_all_hooks, project_list=pending_proj_names) 391 hook.Run(opt.allow_all_hooks, project_list=pending_proj_names)
387 except HookError as e: 392 except HookError as e:
388 print >>sys.stderr, "ERROR: %s" % str(e) 393 print("ERROR: %s" % str(e), file=sys.stderr)
389 return 394 return
390 395
391 if opt.reviewers: 396 if opt.reviewers:
392 reviewers = _SplitEmails(opt.reviewers) 397 reviewers = _SplitEmails(opt.reviewers)
393 if opt.cc: 398 if opt.cc:
394 cc = _SplitEmails(opt.cc) 399 cc = _SplitEmails(opt.cc)
395 people = (reviewers,cc) 400 people = (reviewers, cc)
396 401
397 if not pending: 402 if not pending:
398 print >>sys.stdout, "no branches ready for upload" 403 print("no branches ready for upload", file=sys.stderr)
399 elif len(pending) == 1 and len(pending[0][1]) == 1: 404 elif len(pending) == 1 and len(pending[0][1]) == 1:
400 self._SingleBranch(opt, pending[0][1][0], people) 405 self._SingleBranch(opt, pending[0][1][0], people)
401 else: 406 else:
diff --git a/subcmds/version.py b/subcmds/version.py
index 243e3676..01b7fd8c 100644
--- a/subcmds/version.py
+++ b/subcmds/version.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function
16import sys 17import sys
17from command import Command, MirrorSafeCommand 18from command import Command, MirrorSafeCommand
18from git_command import git 19from git_command import git
@@ -32,12 +33,12 @@ class Version(Command, MirrorSafeCommand):
32 rp = self.manifest.repoProject 33 rp = self.manifest.repoProject
33 rem = rp.GetRemote(rp.remote.name) 34 rem = rp.GetRemote(rp.remote.name)
34 35
35 print 'repo version %s' % rp.work_git.describe(HEAD) 36 print('repo version %s' % rp.work_git.describe(HEAD))
36 print ' (from %s)' % rem.url 37 print(' (from %s)' % rem.url)
37 38
38 if Version.wrapper_path is not None: 39 if Version.wrapper_path is not None:
39 print 'repo launcher version %s' % Version.wrapper_version 40 print('repo launcher version %s' % Version.wrapper_version)
40 print ' (from %s)' % Version.wrapper_path 41 print(' (from %s)' % Version.wrapper_path)
41 42
42 print git.version().strip() 43 print(git.version().strip())
43 print 'Python %s' % sys.version 44 print('Python %s' % sys.version)