summaryrefslogtreecommitdiffstats
path: root/subcmds
diff options
context:
space:
mode:
Diffstat (limited to 'subcmds')
-rw-r--r--subcmds/abandon.py71
-rw-r--r--subcmds/download.py5
-rw-r--r--subcmds/forall.py36
-rw-r--r--subcmds/gitc_delete.py6
-rw-r--r--subcmds/init.py26
-rw-r--r--subcmds/stage.py4
-rw-r--r--subcmds/start.py10
-rw-r--r--subcmds/status.py12
-rw-r--r--subcmds/sync.py84
-rw-r--r--subcmds/upload.py30
10 files changed, 184 insertions, 100 deletions
diff --git a/subcmds/abandon.py b/subcmds/abandon.py
index b94ccdd3..be32dc5c 100644
--- a/subcmds/abandon.py
+++ b/subcmds/abandon.py
@@ -16,6 +16,7 @@
16from __future__ import print_function 16from __future__ import print_function
17import sys 17import sys
18from command import Command 18from command import Command
19from collections import defaultdict
19from git_command import git 20from git_command import git
20from progress import Progress 21from progress import Progress
21 22
@@ -23,49 +24,75 @@ class Abandon(Command):
23 common = True 24 common = True
24 helpSummary = "Permanently abandon a development branch" 25 helpSummary = "Permanently abandon a development branch"
25 helpUsage = """ 26 helpUsage = """
26%prog <branchname> [<project>...] 27%prog [--all | <branchname>] [<project>...]
27 28
28This subcommand permanently abandons a development branch by 29This subcommand permanently abandons a development branch by
29deleting it (and all its history) from your local repository. 30deleting it (and all its history) from your local repository.
30 31
31It is equivalent to "git branch -D <branchname>". 32It is equivalent to "git branch -D <branchname>".
32""" 33"""
34 def _Options(self, p):
35 p.add_option('--all',
36 dest='all', action='store_true',
37 help='delete all branches in all projects')
33 38
34 def Execute(self, opt, args): 39 def Execute(self, opt, args):
35 if not args: 40 if not opt.all and not args:
36 self.Usage() 41 self.Usage()
37 42
38 nb = args[0] 43 if not opt.all:
39 if not git.check_ref_format('heads/%s' % nb): 44 nb = args[0]
40 print("error: '%s' is not a valid name" % nb, file=sys.stderr) 45 if not git.check_ref_format('heads/%s' % nb):
41 sys.exit(1) 46 print("error: '%s' is not a valid name" % nb, file=sys.stderr)
47 sys.exit(1)
48 else:
49 args.insert(0,None)
50 nb = "'All local branches'"
42 51
43 nb = args[0] 52 err = defaultdict(list)
44 err = [] 53 success = defaultdict(list)
45 success = []
46 all_projects = self.GetProjects(args[1:]) 54 all_projects = self.GetProjects(args[1:])
47 55
48 pm = Progress('Abandon %s' % nb, len(all_projects)) 56 pm = Progress('Abandon %s' % nb, len(all_projects))
49 for project in all_projects: 57 for project in all_projects:
50 pm.update() 58 pm.update()
51 59
52 status = project.AbandonBranch(nb) 60 if opt.all:
53 if status is not None: 61 branches = project.GetBranches().keys()
54 if status: 62 else:
55 success.append(project) 63 branches = [nb]
56 else: 64
57 err.append(project) 65 for name in branches:
66 status = project.AbandonBranch(name)
67 if status is not None:
68 if status:
69 success[name].append(project)
70 else:
71 err[name].append(project)
58 pm.end() 72 pm.end()
59 73
74 width = 25
75 for name in branches:
76 if width < len(name):
77 width = len(name)
78
60 if err: 79 if err:
61 for p in err: 80 for br in err.keys():
62 print("error: %s/: cannot abandon %s" % (p.relpath, nb), 81 err_msg = "error: cannot abandon %s" %br
63 file=sys.stderr) 82 print(err_msg, file=sys.stderr)
83 for proj in err[br]:
84 print(' '*len(err_msg) + " | %s" % proj.relpath, file=sys.stderr)
64 sys.exit(1) 85 sys.exit(1)
65 elif not success: 86 elif not success:
66 print('error: no project has branch %s' % nb, file=sys.stderr) 87 print('error: no project has local branch(es) : %s' % nb,
88 file=sys.stderr)
67 sys.exit(1) 89 sys.exit(1)
68 else: 90 else:
69 print('Abandoned in %d project(s):\n %s' 91 print('Abandoned branches:', file=sys.stderr)
70 % (len(success), '\n '.join(p.relpath for p in success)), 92 for br in success.keys():
71 file=sys.stderr) 93 if len(all_projects) > 1 and len(all_projects) == len(success[br]):
94 result = "all project"
95 else:
96 result = "%s" % (
97 ('\n'+' '*width + '| ').join(p.relpath for p in success[br]))
98 print("%s%s| %s\n" % (br,' '*(width-len(br)), result),file=sys.stderr)
diff --git a/subcmds/download.py b/subcmds/download.py
index a029462e..e1010aa2 100644
--- a/subcmds/download.py
+++ b/subcmds/download.py
@@ -26,11 +26,12 @@ class Download(Command):
26 common = True 26 common = True
27 helpSummary = "Download and checkout a change" 27 helpSummary = "Download and checkout a change"
28 helpUsage = """ 28 helpUsage = """
29%prog {project change[/patchset]}... 29%prog {[project] change[/patchset]}...
30""" 30"""
31 helpDescription = """ 31 helpDescription = """
32The '%prog' command downloads a change from the review system and 32The '%prog' command downloads a change from the review system and
33makes it available in your project's local working directory. 33makes it available in your project's local working directory.
34If no project is specified try to use current directory as a project.
34""" 35"""
35 36
36 def _Options(self, p): 37 def _Options(self, p):
@@ -55,7 +56,7 @@ makes it available in your project's local working directory.
55 m = CHANGE_RE.match(a) 56 m = CHANGE_RE.match(a)
56 if m: 57 if m:
57 if not project: 58 if not project:
58 self.Usage() 59 project = self.GetProjects(".")[0]
59 chg_id = int(m.group(1)) 60 chg_id = int(m.group(1))
60 if m.group(2): 61 if m.group(2):
61 ps_id = int(m.group(2)) 62 ps_id = int(m.group(2))
diff --git a/subcmds/forall.py b/subcmds/forall.py
index 07ee8d58..52eb5e28 100644
--- a/subcmds/forall.py
+++ b/subcmds/forall.py
@@ -15,17 +15,16 @@
15 15
16from __future__ import print_function 16from __future__ import print_function
17import errno 17import errno
18import fcntl
19import multiprocessing 18import multiprocessing
20import re 19import re
21import os 20import os
22import select
23import signal 21import signal
24import sys 22import sys
25import subprocess 23import subprocess
26 24
27from color import Coloring 25from color import Coloring
28from command import Command, MirrorSafeCommand 26from command import Command, MirrorSafeCommand
27import platform_utils
29 28
30_CAN_COLOR = [ 29_CAN_COLOR = [
31 'branch', 30 'branch',
@@ -105,6 +104,13 @@ annotating tree details.
105shell positional arguments ($1, $2, .., $#) are set to any arguments 104shell positional arguments ($1, $2, .., $#) are set to any arguments
106following <command>. 105following <command>.
107 106
107Example: to list projects:
108
109 %prog% forall -c 'echo $REPO_PROJECT'
110
111Notice that $REPO_PROJECT is quoted to ensure it is expanded in
112the context of running <command> instead of in the calling shell.
113
108Unless -p is used, stdin, stdout, stderr are inherited from the 114Unless -p is used, stdin, stdout, stderr are inherited from the
109terminal and are not redirected. 115terminal and are not redirected.
110 116
@@ -344,35 +350,25 @@ def DoWork(project, mirror, opt, cmd, shell, cnt, config):
344 if opt.project_header: 350 if opt.project_header:
345 out = ForallColoring(config) 351 out = ForallColoring(config)
346 out.redirect(sys.stdout) 352 out.redirect(sys.stdout)
347 class sfd(object):
348 def __init__(self, fd, dest):
349 self.fd = fd
350 self.dest = dest
351 def fileno(self):
352 return self.fd.fileno()
353
354 empty = True 353 empty = True
355 errbuf = '' 354 errbuf = ''
356 355
357 p.stdin.close() 356 p.stdin.close()
358 s_in = [sfd(p.stdout, sys.stdout), 357 s_in = platform_utils.FileDescriptorStreams.create()
359 sfd(p.stderr, sys.stderr)] 358 s_in.add(p.stdout, sys.stdout, 'stdout')
360 359 s_in.add(p.stderr, sys.stderr, 'stderr')
361 for s in s_in:
362 flags = fcntl.fcntl(s.fd, fcntl.F_GETFL)
363 fcntl.fcntl(s.fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
364 360
365 while s_in: 361 while not s_in.is_done:
366 in_ready, _out_ready, _err_ready = select.select(s_in, [], []) 362 in_ready = s_in.select()
367 for s in in_ready: 363 for s in in_ready:
368 buf = s.fd.read(4096) 364 buf = s.read()
369 if not buf: 365 if not buf:
370 s.fd.close() 366 s.close()
371 s_in.remove(s) 367 s_in.remove(s)
372 continue 368 continue
373 369
374 if not opt.verbose: 370 if not opt.verbose:
375 if s.fd != p.stdout: 371 if s.std_name == 'stderr':
376 errbuf += buf 372 errbuf += buf
377 continue 373 continue
378 374
diff --git a/subcmds/gitc_delete.py b/subcmds/gitc_delete.py
index 7380c352..54f62f46 100644
--- a/subcmds/gitc_delete.py
+++ b/subcmds/gitc_delete.py
@@ -14,12 +14,10 @@
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function 16from __future__ import print_function
17import os
18import shutil
19import sys 17import sys
20 18
21from command import Command, GitcClientCommand 19from command import Command, GitcClientCommand
22import gitc_utils 20import platform_utils
23 21
24from pyversion import is_python3 22from pyversion import is_python3
25if not is_python3(): 23if not is_python3():
@@ -52,4 +50,4 @@ and all locally downloaded sources.
52 if not response == 'yes': 50 if not response == 'yes':
53 print('Response was not "yes"\n Exiting...') 51 print('Response was not "yes"\n Exiting...')
54 sys.exit(1) 52 sys.exit(1)
55 shutil.rmtree(self.gitc_manifest.gitc_client_dir) 53 platform_utils.rmtree(self.gitc_manifest.gitc_client_dir)
diff --git a/subcmds/init.py b/subcmds/init.py
index 45d69b79..eeddca06 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -17,7 +17,6 @@ from __future__ import print_function
17import os 17import os
18import platform 18import platform
19import re 19import re
20import shutil
21import sys 20import sys
22 21
23from pyversion import is_python3 22from pyversion import is_python3
@@ -35,6 +34,7 @@ from error import ManifestParseError
35from project import SyncBuffer 34from project import SyncBuffer
36from git_config import GitConfig 35from git_config import GitConfig
37from git_command import git_require, MIN_GIT_VERSION 36from git_command import git_require, MIN_GIT_VERSION
37import platform_utils
38 38
39class Init(InteractiveCommand, MirrorSafeCommand): 39class Init(InteractiveCommand, MirrorSafeCommand):
40 common = True 40 common = True
@@ -91,6 +91,9 @@ to update the working directory files.
91 g.add_option('-b', '--manifest-branch', 91 g.add_option('-b', '--manifest-branch',
92 dest='manifest_branch', 92 dest='manifest_branch',
93 help='manifest branch or revision', metavar='REVISION') 93 help='manifest branch or revision', metavar='REVISION')
94 g.add_option('-c', '--current-branch',
95 dest='current_branch_only', action='store_true',
96 help='fetch only current manifest branch from server')
94 g.add_option('-m', '--manifest-name', 97 g.add_option('-m', '--manifest-name',
95 dest='manifest_name', default='default.xml', 98 dest='manifest_name', default='default.xml',
96 help='initial manifest file', metavar='NAME.xml') 99 help='initial manifest file', metavar='NAME.xml')
@@ -108,6 +111,9 @@ to update the working directory files.
108 dest='archive', action='store_true', 111 dest='archive', action='store_true',
109 help='checkout an archive instead of a git repository for ' 112 help='checkout an archive instead of a git repository for '
110 'each project. See git archive.') 113 'each project. See git archive.')
114 g.add_option('--submodules',
115 dest='submodules', action='store_true',
116 help='sync any submodules associated with the manifest repo')
111 g.add_option('-g', '--groups', 117 g.add_option('-g', '--groups',
112 dest='groups', default='default', 118 dest='groups', default='default',
113 help='restrict manifest projects to ones with specified ' 119 help='restrict manifest projects to ones with specified '
@@ -121,6 +127,9 @@ to update the working directory files.
121 g.add_option('--no-clone-bundle', 127 g.add_option('--no-clone-bundle',
122 dest='no_clone_bundle', action='store_true', 128 dest='no_clone_bundle', action='store_true',
123 help='disable use of /clone.bundle on HTTP/HTTPS') 129 help='disable use of /clone.bundle on HTTP/HTTPS')
130 g.add_option('--no-tags',
131 dest='no_tags', action='store_true',
132 help="don't fetch tags in the manifest")
124 133
125 # Tool 134 # Tool
126 g = p.add_option_group('repo Version options') 135 g = p.add_option_group('repo Version options')
@@ -230,22 +239,27 @@ to update the working directory files.
230 'in another location.', file=sys.stderr) 239 'in another location.', file=sys.stderr)
231 sys.exit(1) 240 sys.exit(1)
232 241
242 if opt.submodules:
243 m.config.SetString('repo.submodules', 'true')
244
233 if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet, 245 if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet,
234 clone_bundle=not opt.no_clone_bundle): 246 clone_bundle=not opt.no_clone_bundle,
247 current_branch_only=opt.current_branch_only,
248 no_tags=opt.no_tags, submodules=opt.submodules):
235 r = m.GetRemote(m.remote.name) 249 r = m.GetRemote(m.remote.name)
236 print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr) 250 print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
237 251
238 # Better delete the manifest git dir if we created it; otherwise next 252 # Better delete the manifest git dir if we created it; otherwise next
239 # time (when user fixes problems) we won't go through the "is_new" logic. 253 # time (when user fixes problems) we won't go through the "is_new" logic.
240 if is_new: 254 if is_new:
241 shutil.rmtree(m.gitdir) 255 platform_utils.rmtree(m.gitdir)
242 sys.exit(1) 256 sys.exit(1)
243 257
244 if opt.manifest_branch: 258 if opt.manifest_branch:
245 m.MetaBranchSwitch() 259 m.MetaBranchSwitch(submodules=opt.submodules)
246 260
247 syncbuf = SyncBuffer(m.config) 261 syncbuf = SyncBuffer(m.config)
248 m.Sync_LocalHalf(syncbuf) 262 m.Sync_LocalHalf(syncbuf, submodules=opt.submodules)
249 syncbuf.Finish() 263 syncbuf.Finish()
250 264
251 if is_new or m.CurrentBranch is None: 265 if is_new or m.CurrentBranch is None:
@@ -387,7 +401,7 @@ to update the working directory files.
387 git_require(MIN_GIT_VERSION, fail=True) 401 git_require(MIN_GIT_VERSION, fail=True)
388 402
389 if opt.reference: 403 if opt.reference:
390 opt.reference = os.path.expanduser(opt.reference) 404 opt.reference = os.path.abspath(os.path.expanduser(opt.reference))
391 405
392 # Check this here, else manifest will be tagged "not new" and init won't be 406 # Check this here, else manifest will be tagged "not new" and init won't be
393 # possible anymore without removing the .repo/manifests directory. 407 # possible anymore without removing the .repo/manifests directory.
diff --git a/subcmds/stage.py b/subcmds/stage.py
index 28849764..9d354268 100644
--- a/subcmds/stage.py
+++ b/subcmds/stage.py
@@ -60,8 +60,8 @@ The '%prog' command stages files to prepare the next commit.
60 out.nl() 60 out.nl()
61 61
62 for i in range(len(all_projects)): 62 for i in range(len(all_projects)):
63 p = all_projects[i] 63 project = all_projects[i]
64 out.write('%3d: %s', i + 1, p.relpath + '/') 64 out.write('%3d: %s', i + 1, project.relpath + '/')
65 out.nl() 65 out.nl()
66 out.nl() 66 out.nl()
67 67
diff --git a/subcmds/start.py b/subcmds/start.py
index 290b6897..c3ec303e 100644
--- a/subcmds/start.py
+++ b/subcmds/start.py
@@ -18,7 +18,7 @@ import os
18import sys 18import sys
19 19
20from command import Command 20from command import Command
21from git_config import IsId 21from git_config import IsImmutable
22from git_command import git 22from git_command import git
23import gitc_utils 23import gitc_utils
24from progress import Progress 24from progress import Progress
@@ -96,11 +96,11 @@ revision specified in the manifest.
96 project.Sync_LocalHalf(sync_buf) 96 project.Sync_LocalHalf(sync_buf)
97 project.revisionId = gitc_project.old_revision 97 project.revisionId = gitc_project.old_revision
98 98
99 # If the current revision is a specific SHA1 then we can't push back 99 # If the current revision is immutable, such as a SHA1, a tag or
100 # to it; so substitute with dest_branch if defined, or with manifest 100 # a change, then we can't push back to it. Substitute with
101 # default revision instead. 101 # dest_branch, if defined; or with manifest default revision instead.
102 branch_merge = '' 102 branch_merge = ''
103 if IsId(project.revisionExpr): 103 if IsImmutable(project.revisionExpr):
104 if project.dest_branch: 104 if project.dest_branch:
105 branch_merge = project.dest_branch 105 branch_merge = project.dest_branch
106 else: 106 else:
diff --git a/subcmds/status.py b/subcmds/status.py
index 38c229b1..60e26ff4 100644
--- a/subcmds/status.py
+++ b/subcmds/status.py
@@ -89,8 +89,10 @@ the following meanings:
89 p.add_option('-o', '--orphans', 89 p.add_option('-o', '--orphans',
90 dest='orphans', action='store_true', 90 dest='orphans', action='store_true',
91 help="include objects in working directory outside of repo projects") 91 help="include objects in working directory outside of repo projects")
92 p.add_option('-q', '--quiet', action='store_true',
93 help="only print the name of modified projects")
92 94
93 def _StatusHelper(self, project, clean_counter, sem): 95 def _StatusHelper(self, project, clean_counter, sem, quiet):
94 """Obtains the status for a specific project. 96 """Obtains the status for a specific project.
95 97
96 Obtains the status for a project, redirecting the output to 98 Obtains the status for a project, redirecting the output to
@@ -104,7 +106,7 @@ the following meanings:
104 output: Where to output the status. 106 output: Where to output the status.
105 """ 107 """
106 try: 108 try:
107 state = project.PrintWorkTreeStatus() 109 state = project.PrintWorkTreeStatus(quiet=quiet)
108 if state == 'CLEAN': 110 if state == 'CLEAN':
109 next(clean_counter) 111 next(clean_counter)
110 finally: 112 finally:
@@ -132,7 +134,7 @@ the following meanings:
132 134
133 if opt.jobs == 1: 135 if opt.jobs == 1:
134 for project in all_projects: 136 for project in all_projects:
135 state = project.PrintWorkTreeStatus() 137 state = project.PrintWorkTreeStatus(quiet=opt.quiet)
136 if state == 'CLEAN': 138 if state == 'CLEAN':
137 next(counter) 139 next(counter)
138 else: 140 else:
@@ -142,13 +144,13 @@ the following meanings:
142 sem.acquire() 144 sem.acquire()
143 145
144 t = _threading.Thread(target=self._StatusHelper, 146 t = _threading.Thread(target=self._StatusHelper,
145 args=(project, counter, sem)) 147 args=(project, counter, sem, opt.quiet))
146 threads.append(t) 148 threads.append(t)
147 t.daemon = True 149 t.daemon = True
148 t.start() 150 t.start()
149 for t in threads: 151 for t in threads:
150 t.join() 152 t.join()
151 if len(all_projects) == next(counter): 153 if not opt.quiet and len(all_projects) == next(counter):
152 print('nothing to commit (working directory clean)') 154 print('nothing to commit (working directory clean)')
153 155
154 if opt.orphans: 156 if opt.orphans:
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 7ba9ebfc..cda47fdd 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -19,7 +19,6 @@ import netrc
19from optparse import SUPPRESS_HELP 19from optparse import SUPPRESS_HELP
20import os 20import os
21import re 21import re
22import shutil
23import socket 22import socket
24import subprocess 23import subprocess
25import sys 24import sys
@@ -64,6 +63,7 @@ try:
64except ImportError: 63except ImportError:
65 multiprocessing = None 64 multiprocessing = None
66 65
66import event_log
67from git_command import GIT, git_require 67from git_command import GIT, git_require
68from git_config import GetUrlCookieFile 68from git_config import GetUrlCookieFile
69from git_refs import R_HEADS, HEAD 69from git_refs import R_HEADS, HEAD
@@ -72,6 +72,7 @@ from project import Project
72from project import RemoteSpec 72from project import RemoteSpec
73from command import Command, MirrorSafeCommand 73from command import Command, MirrorSafeCommand
74from error import RepoChangedException, GitError, ManifestParseError 74from error import RepoChangedException, GitError, ManifestParseError
75import platform_utils
75from project import SyncBuffer 76from project import SyncBuffer
76from progress import Progress 77from progress import Progress
77from wrapper import Wrapper 78from wrapper import Wrapper
@@ -255,7 +256,7 @@ later is required to fix a server side protocol bug.
255 dest='repo_upgraded', action='store_true', 256 dest='repo_upgraded', action='store_true',
256 help=SUPPRESS_HELP) 257 help=SUPPRESS_HELP)
257 258
258 def _FetchProjectList(self, opt, projects, *args, **kwargs): 259 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
259 """Main function of the fetch threads when jobs are > 1. 260 """Main function of the fetch threads when jobs are > 1.
260 261
261 Delegates most of the work to _FetchHelper. 262 Delegates most of the work to _FetchHelper.
@@ -263,15 +264,20 @@ later is required to fix a server side protocol bug.
263 Args: 264 Args:
264 opt: Program options returned from optparse. See _Options(). 265 opt: Program options returned from optparse. See _Options().
265 projects: Projects to fetch. 266 projects: Projects to fetch.
267 sem: We'll release() this semaphore when we exit so that another thread
268 can be started up.
266 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the 269 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
267 _FetchHelper docstring for details. 270 _FetchHelper docstring for details.
268 """ 271 """
269 for project in projects: 272 try:
270 success = self._FetchHelper(opt, project, *args, **kwargs) 273 for project in projects:
271 if not success and not opt.force_broken: 274 success = self._FetchHelper(opt, project, *args, **kwargs)
272 break 275 if not success and not opt.force_broken:
276 break
277 finally:
278 sem.release()
273 279
274 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): 280 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event):
275 """Fetch git objects for a single project. 281 """Fetch git objects for a single project.
276 282
277 Args: 283 Args:
@@ -283,8 +289,6 @@ later is required to fix a server side protocol bug.
283 (with our lock held). 289 (with our lock held).
284 pm: Instance of a Project object. We will call pm.update() (with our 290 pm: Instance of a Project object. We will call pm.update() (with our
285 lock held). 291 lock held).
286 sem: We'll release() this semaphore when we exit so that another thread
287 can be started up.
288 err_event: We'll set this event in the case of an error (after printing 292 err_event: We'll set this event in the case of an error (after printing
289 out info about the error). 293 out info about the error).
290 294
@@ -301,9 +305,10 @@ later is required to fix a server side protocol bug.
301 # - We always set err_event in the case of an exception. 305 # - We always set err_event in the case of an exception.
302 # - We always make sure we call sem.release(). 306 # - We always make sure we call sem.release().
303 # - We always make sure we unlock the lock if we locked it. 307 # - We always make sure we unlock the lock if we locked it.
308 start = time.time()
309 success = False
304 try: 310 try:
305 try: 311 try:
306 start = time.time()
307 success = project.Sync_NetworkHalf( 312 success = project.Sync_NetworkHalf(
308 quiet=opt.quiet, 313 quiet=opt.quiet,
309 current_branch_only=opt.current_branch_only, 314 current_branch_only=opt.current_branch_only,
@@ -321,7 +326,9 @@ later is required to fix a server side protocol bug.
321 326
322 if not success: 327 if not success:
323 err_event.set() 328 err_event.set()
324 print('error: Cannot fetch %s' % project.name, file=sys.stderr) 329 print('error: Cannot fetch %s from %s'
330 % (project.name, project.remote.url),
331 file=sys.stderr)
325 if opt.force_broken: 332 if opt.force_broken:
326 print('warn: --force-broken, continuing to sync', 333 print('warn: --force-broken, continuing to sync',
327 file=sys.stderr) 334 file=sys.stderr)
@@ -340,14 +347,18 @@ later is required to fix a server side protocol bug.
340 finally: 347 finally:
341 if did_lock: 348 if did_lock:
342 lock.release() 349 lock.release()
343 sem.release() 350 finish = time.time()
351 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
352 start, finish, success)
344 353
345 return success 354 return success
346 355
347 def _Fetch(self, projects, opt): 356 def _Fetch(self, projects, opt):
348 fetched = set() 357 fetched = set()
349 lock = _threading.Lock() 358 lock = _threading.Lock()
350 pm = Progress('Fetching projects', len(projects)) 359 pm = Progress('Fetching projects', len(projects),
360 print_newline=not(opt.quiet),
361 always_print_percentage=opt.quiet)
351 362
352 objdir_project_map = dict() 363 objdir_project_map = dict()
353 for project in projects: 364 for project in projects:
@@ -365,10 +376,10 @@ later is required to fix a server side protocol bug.
365 sem.acquire() 376 sem.acquire()
366 kwargs = dict(opt=opt, 377 kwargs = dict(opt=opt,
367 projects=project_list, 378 projects=project_list,
379 sem=sem,
368 lock=lock, 380 lock=lock,
369 fetched=fetched, 381 fetched=fetched,
370 pm=pm, 382 pm=pm,
371 sem=sem,
372 err_event=err_event) 383 err_event=err_event)
373 if self.jobs > 1: 384 if self.jobs > 1:
374 t = _threading.Thread(target = self._FetchProjectList, 385 t = _threading.Thread(target = self._FetchProjectList,
@@ -384,7 +395,7 @@ later is required to fix a server side protocol bug.
384 t.join() 395 t.join()
385 396
386 # If we saw an error, exit with code 1 so that other scripts can check. 397 # If we saw an error, exit with code 1 so that other scripts can check.
387 if err_event.isSet(): 398 if err_event.isSet() and not opt.force_broken:
388 print('\nerror: Exited sync due to fetch errors', file=sys.stderr) 399 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
389 sys.exit(1) 400 sys.exit(1)
390 401
@@ -464,7 +475,7 @@ later is required to fix a server side protocol bug.
464 # working git repository around. There shouldn't be any git projects here, 475 # working git repository around. There shouldn't be any git projects here,
465 # so rmtree works. 476 # so rmtree works.
466 try: 477 try:
467 shutil.rmtree(os.path.join(path, '.git')) 478 platform_utils.rmtree(os.path.join(path, '.git'))
468 except OSError: 479 except OSError:
469 print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr) 480 print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr)
470 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr) 481 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
@@ -478,7 +489,7 @@ later is required to fix a server side protocol bug.
478 for root, dirs, files in os.walk(path): 489 for root, dirs, files in os.walk(path):
479 for f in files: 490 for f in files:
480 try: 491 try:
481 os.remove(os.path.join(root, f)) 492 platform_utils.remove(os.path.join(root, f))
482 except OSError: 493 except OSError:
483 print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr) 494 print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr)
484 failed = True 495 failed = True
@@ -487,9 +498,9 @@ later is required to fix a server side protocol bug.
487 dirs_to_remove += [os.path.join(root, d) for d in dirs 498 dirs_to_remove += [os.path.join(root, d) for d in dirs
488 if os.path.join(root, d) not in dirs_to_remove] 499 if os.path.join(root, d) not in dirs_to_remove]
489 for d in reversed(dirs_to_remove): 500 for d in reversed(dirs_to_remove):
490 if os.path.islink(d): 501 if platform_utils.islink(d):
491 try: 502 try:
492 os.remove(d) 503 platform_utils.remove(d)
493 except OSError: 504 except OSError:
494 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr) 505 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
495 failed = True 506 failed = True
@@ -701,7 +712,7 @@ later is required to fix a server side protocol bug.
701 else: # Not smart sync or smart tag mode 712 else: # Not smart sync or smart tag mode
702 if os.path.isfile(smart_sync_manifest_path): 713 if os.path.isfile(smart_sync_manifest_path):
703 try: 714 try:
704 os.remove(smart_sync_manifest_path) 715 platform_utils.remove(smart_sync_manifest_path)
705 except OSError as e: 716 except OSError as e:
706 print('error: failed to remove existing smart sync override manifest: %s' % 717 print('error: failed to remove existing smart sync override manifest: %s' %
707 e, file=sys.stderr) 718 e, file=sys.stderr)
@@ -716,15 +727,24 @@ later is required to fix a server side protocol bug.
716 _PostRepoUpgrade(self.manifest, quiet=opt.quiet) 727 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
717 728
718 if not opt.local_only: 729 if not opt.local_only:
719 mp.Sync_NetworkHalf(quiet=opt.quiet, 730 start = time.time()
720 current_branch_only=opt.current_branch_only, 731 success = mp.Sync_NetworkHalf(quiet=opt.quiet,
721 no_tags=opt.no_tags, 732 current_branch_only=opt.current_branch_only,
722 optimized_fetch=opt.optimized_fetch) 733 no_tags=opt.no_tags,
734 optimized_fetch=opt.optimized_fetch,
735 submodules=self.manifest.HasSubmodules)
736 finish = time.time()
737 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
738 start, finish, success)
723 739
724 if mp.HasChanges: 740 if mp.HasChanges:
725 syncbuf = SyncBuffer(mp.config) 741 syncbuf = SyncBuffer(mp.config)
726 mp.Sync_LocalHalf(syncbuf) 742 start = time.time()
727 if not syncbuf.Finish(): 743 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
744 clean = syncbuf.Finish()
745 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
746 start, time.time(), clean)
747 if not clean:
728 sys.exit(1) 748 sys.exit(1)
729 self._ReloadManifest(manifest_name) 749 self._ReloadManifest(manifest_name)
730 if opt.jobs is None: 750 if opt.jobs is None:
@@ -761,8 +781,8 @@ later is required to fix a server side protocol bug.
761 # generate a new args list to represent the opened projects. 781 # generate a new args list to represent the opened projects.
762 # TODO: make this more reliable -- if there's a project name/path overlap, 782 # TODO: make this more reliable -- if there's a project name/path overlap,
763 # this may choose the wrong project. 783 # this may choose the wrong project.
764 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd()) 784 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
765 for p in opened_projects] 785 for path in opened_projects]
766 if not args: 786 if not args:
767 return 787 return
768 all_projects = self.GetProjects(args, 788 all_projects = self.GetProjects(args,
@@ -818,7 +838,10 @@ later is required to fix a server side protocol bug.
818 for project in all_projects: 838 for project in all_projects:
819 pm.update() 839 pm.update()
820 if project.worktree: 840 if project.worktree:
841 start = time.time()
821 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync) 842 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
843 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
844 start, time.time(), syncbuf.Recently())
822 pm.end() 845 pm.end()
823 print(file=sys.stderr) 846 print(file=sys.stderr)
824 if not syncbuf.Finish(): 847 if not syncbuf.Finish():
@@ -902,6 +925,7 @@ def _VerifyTag(project):
902 return False 925 return False
903 return True 926 return True
904 927
928
905class _FetchTimes(object): 929class _FetchTimes(object):
906 _ALPHA = 0.5 930 _ALPHA = 0.5
907 931
@@ -932,7 +956,7 @@ class _FetchTimes(object):
932 f.close() 956 f.close()
933 except (IOError, ValueError): 957 except (IOError, ValueError):
934 try: 958 try:
935 os.remove(self._path) 959 platform_utils.remove(self._path)
936 except OSError: 960 except OSError:
937 pass 961 pass
938 self._times = {} 962 self._times = {}
@@ -956,7 +980,7 @@ class _FetchTimes(object):
956 f.close() 980 f.close()
957 except (IOError, TypeError): 981 except (IOError, TypeError):
958 try: 982 try:
959 os.remove(self._path) 983 platform_utils.remove(self._path)
960 except OSError: 984 except OSError:
961 pass 985 pass
962 986
diff --git a/subcmds/upload.py b/subcmds/upload.py
index 1172dadc..77eaf81a 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -154,6 +154,16 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
154 p.add_option('-d', '--draft', 154 p.add_option('-d', '--draft',
155 action='store_true', dest='draft', default=False, 155 action='store_true', dest='draft', default=False,
156 help='If specified, upload as a draft.') 156 help='If specified, upload as a draft.')
157 p.add_option('-p', '--private',
158 action='store_true', dest='private', default=False,
159 help='If specified, upload as a private change.')
160 p.add_option('-w', '--wip',
161 action='store_true', dest='wip', default=False,
162 help='If specified, upload as a work-in-progress change.')
163 p.add_option('-o', '--push-option',
164 type='string', action='append', dest='push_options',
165 default=[],
166 help='Additional push options to transmit')
157 p.add_option('-D', '--destination', '--dest', 167 p.add_option('-D', '--destination', '--dest',
158 type='string', action='store', dest='dest_branch', 168 type='string', action='store', dest='dest_branch',
159 metavar='BRANCH', 169 metavar='BRANCH',
@@ -175,6 +185,9 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
175 # Never run upload hooks, but upload anyway (AKA bypass hooks). 185 # Never run upload hooks, but upload anyway (AKA bypass hooks).
176 # - no-verify=True, verify=True: 186 # - no-verify=True, verify=True:
177 # Invalid 187 # Invalid
188 p.add_option('--no-cert-checks',
189 dest='validate_certs', action='store_false', default=True,
190 help='Disable verifying ssl certs (unsafe).')
178 p.add_option('--no-verify', 191 p.add_option('--no-verify',
179 dest='bypass_hooks', action='store_true', 192 dest='bypass_hooks', action='store_true',
180 help='Do not run the upload hook.') 193 help='Do not run the upload hook.')
@@ -198,7 +211,8 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
198 commit_list = branch.commits 211 commit_list = branch.commits
199 212
200 destination = opt.dest_branch or project.dest_branch or project.revisionExpr 213 destination = opt.dest_branch or project.dest_branch or project.revisionExpr
201 print('Upload project %s/ to remote branch %s:' % (project.relpath, destination)) 214 print('Upload project %s/ to remote branch %s%s:' %
215 (project.relpath, destination, ' (draft)' if opt.draft else ''))
202 print(' branch %s (%2d commit%s, %s):' % ( 216 print(' branch %s (%2d commit%s, %s):' % (
203 name, 217 name,
204 len(commit_list), 218 len(commit_list),
@@ -377,7 +391,15 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
377 branch.uploaded = False 391 branch.uploaded = False
378 continue 392 continue
379 393
380 branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft, dest_branch=destination) 394 branch.UploadForReview(people,
395 auto_topic=opt.auto_topic,
396 draft=opt.draft,
397 private=opt.private,
398 wip=opt.wip,
399 dest_branch=destination,
400 validate_certs=opt.validate_certs,
401 push_options=opt.push_options)
402
381 branch.uploaded = True 403 branch.uploaded = True
382 except UploadError as e: 404 except UploadError as e:
383 branch.error = e 405 branch.error = e
@@ -463,8 +485,8 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
463 self.manifest.topdir, 485 self.manifest.topdir,
464 self.manifest.manifestProject.GetRemote('origin').url, 486 self.manifest.manifestProject.GetRemote('origin').url,
465 abort_if_user_denies=True) 487 abort_if_user_denies=True)
466 pending_proj_names = [project.name for (project, avail) in pending] 488 pending_proj_names = [project.name for (project, available) in pending]
467 pending_worktrees = [project.worktree for (project, avail) in pending] 489 pending_worktrees = [project.worktree for (project, available) in pending]
468 try: 490 try:
469 hook.Run(opt.allow_all_hooks, project_list=pending_proj_names, 491 hook.Run(opt.allow_all_hooks, project_list=pending_proj_names,
470 worktree_list=pending_worktrees) 492 worktree_list=pending_worktrees)