diff options
Diffstat (limited to 'subcmds')
-rw-r--r-- | subcmds/abandon.py | 71 | ||||
-rw-r--r-- | subcmds/download.py | 5 | ||||
-rw-r--r-- | subcmds/forall.py | 36 | ||||
-rw-r--r-- | subcmds/gitc_delete.py | 6 | ||||
-rw-r--r-- | subcmds/init.py | 26 | ||||
-rw-r--r-- | subcmds/stage.py | 4 | ||||
-rw-r--r-- | subcmds/start.py | 10 | ||||
-rw-r--r-- | subcmds/status.py | 12 | ||||
-rw-r--r-- | subcmds/sync.py | 84 | ||||
-rw-r--r-- | subcmds/upload.py | 30 |
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 @@ | |||
16 | from __future__ import print_function | 16 | from __future__ import print_function |
17 | import sys | 17 | import sys |
18 | from command import Command | 18 | from command import Command |
19 | from collections import defaultdict | ||
19 | from git_command import git | 20 | from git_command import git |
20 | from progress import Progress | 21 | from 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 | ||
28 | This subcommand permanently abandons a development branch by | 29 | This subcommand permanently abandons a development branch by |
29 | deleting it (and all its history) from your local repository. | 30 | deleting it (and all its history) from your local repository. |
30 | 31 | ||
31 | It is equivalent to "git branch -D <branchname>". | 32 | It 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 = """ |
32 | The '%prog' command downloads a change from the review system and | 32 | The '%prog' command downloads a change from the review system and |
33 | makes it available in your project's local working directory. | 33 | makes it available in your project's local working directory. |
34 | If 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 | ||
16 | from __future__ import print_function | 16 | from __future__ import print_function |
17 | import errno | 17 | import errno |
18 | import fcntl | ||
19 | import multiprocessing | 18 | import multiprocessing |
20 | import re | 19 | import re |
21 | import os | 20 | import os |
22 | import select | ||
23 | import signal | 21 | import signal |
24 | import sys | 22 | import sys |
25 | import subprocess | 23 | import subprocess |
26 | 24 | ||
27 | from color import Coloring | 25 | from color import Coloring |
28 | from command import Command, MirrorSafeCommand | 26 | from command import Command, MirrorSafeCommand |
27 | import platform_utils | ||
29 | 28 | ||
30 | _CAN_COLOR = [ | 29 | _CAN_COLOR = [ |
31 | 'branch', | 30 | 'branch', |
@@ -105,6 +104,13 @@ annotating tree details. | |||
105 | shell positional arguments ($1, $2, .., $#) are set to any arguments | 104 | shell positional arguments ($1, $2, .., $#) are set to any arguments |
106 | following <command>. | 105 | following <command>. |
107 | 106 | ||
107 | Example: to list projects: | ||
108 | |||
109 | %prog% forall -c 'echo $REPO_PROJECT' | ||
110 | |||
111 | Notice that $REPO_PROJECT is quoted to ensure it is expanded in | ||
112 | the context of running <command> instead of in the calling shell. | ||
113 | |||
108 | Unless -p is used, stdin, stdout, stderr are inherited from the | 114 | Unless -p is used, stdin, stdout, stderr are inherited from the |
109 | terminal and are not redirected. | 115 | terminal 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 | ||
16 | from __future__ import print_function | 16 | from __future__ import print_function |
17 | import os | ||
18 | import shutil | ||
19 | import sys | 17 | import sys |
20 | 18 | ||
21 | from command import Command, GitcClientCommand | 19 | from command import Command, GitcClientCommand |
22 | import gitc_utils | 20 | import platform_utils |
23 | 21 | ||
24 | from pyversion import is_python3 | 22 | from pyversion import is_python3 |
25 | if not is_python3(): | 23 | if 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 | |||
17 | import os | 17 | import os |
18 | import platform | 18 | import platform |
19 | import re | 19 | import re |
20 | import shutil | ||
21 | import sys | 20 | import sys |
22 | 21 | ||
23 | from pyversion import is_python3 | 22 | from pyversion import is_python3 |
@@ -35,6 +34,7 @@ from error import ManifestParseError | |||
35 | from project import SyncBuffer | 34 | from project import SyncBuffer |
36 | from git_config import GitConfig | 35 | from git_config import GitConfig |
37 | from git_command import git_require, MIN_GIT_VERSION | 36 | from git_command import git_require, MIN_GIT_VERSION |
37 | import platform_utils | ||
38 | 38 | ||
39 | class Init(InteractiveCommand, MirrorSafeCommand): | 39 | class 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 | |||
18 | import sys | 18 | import sys |
19 | 19 | ||
20 | from command import Command | 20 | from command import Command |
21 | from git_config import IsId | 21 | from git_config import IsImmutable |
22 | from git_command import git | 22 | from git_command import git |
23 | import gitc_utils | 23 | import gitc_utils |
24 | from progress import Progress | 24 | from 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 | |||
19 | from optparse import SUPPRESS_HELP | 19 | from optparse import SUPPRESS_HELP |
20 | import os | 20 | import os |
21 | import re | 21 | import re |
22 | import shutil | ||
23 | import socket | 22 | import socket |
24 | import subprocess | 23 | import subprocess |
25 | import sys | 24 | import sys |
@@ -64,6 +63,7 @@ try: | |||
64 | except ImportError: | 63 | except ImportError: |
65 | multiprocessing = None | 64 | multiprocessing = None |
66 | 65 | ||
66 | import event_log | ||
67 | from git_command import GIT, git_require | 67 | from git_command import GIT, git_require |
68 | from git_config import GetUrlCookieFile | 68 | from git_config import GetUrlCookieFile |
69 | from git_refs import R_HEADS, HEAD | 69 | from git_refs import R_HEADS, HEAD |
@@ -72,6 +72,7 @@ from project import Project | |||
72 | from project import RemoteSpec | 72 | from project import RemoteSpec |
73 | from command import Command, MirrorSafeCommand | 73 | from command import Command, MirrorSafeCommand |
74 | from error import RepoChangedException, GitError, ManifestParseError | 74 | from error import RepoChangedException, GitError, ManifestParseError |
75 | import platform_utils | ||
75 | from project import SyncBuffer | 76 | from project import SyncBuffer |
76 | from progress import Progress | 77 | from progress import Progress |
77 | from wrapper import Wrapper | 78 | from 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 | |||
905 | class _FetchTimes(object): | 929 | class _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) |