diff options
Diffstat (limited to 'subcmds')
| -rw-r--r-- | subcmds/abandon.py | 71 | ||||
| -rw-r--r-- | subcmds/init.py | 11 | ||||
| -rw-r--r-- | subcmds/start.py | 3 | ||||
| -rw-r--r-- | subcmds/status.py | 12 | ||||
| -rw-r--r-- | subcmds/sync.py | 110 | ||||
| -rw-r--r-- | subcmds/upload.py | 14 |
6 files changed, 158 insertions, 63 deletions
diff --git a/subcmds/abandon.py b/subcmds/abandon.py index b94ccdd3..6f78da74 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" % p.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/init.py b/subcmds/init.py index b8e3de5a..45d69b79 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
| @@ -61,6 +61,11 @@ directory use as much data as possible from the local reference | |||
| 61 | directory when fetching from the server. This will make the sync | 61 | directory when fetching from the server. This will make the sync |
| 62 | go a lot faster by reducing data traffic on the network. | 62 | go a lot faster by reducing data traffic on the network. |
| 63 | 63 | ||
| 64 | The --no-clone-bundle option disables any attempt to use | ||
| 65 | $URL/clone.bundle to bootstrap a new Git repository from a | ||
| 66 | resumeable bundle file on a content delivery network. This | ||
| 67 | may be necessary if there are problems with the local Python | ||
| 68 | HTTP client or proxy configuration, but the Git binary works. | ||
| 64 | 69 | ||
| 65 | Switching Manifest Branches | 70 | Switching Manifest Branches |
| 66 | --------------------------- | 71 | --------------------------- |
| @@ -113,6 +118,9 @@ to update the working directory files. | |||
| 113 | help='restrict manifest projects to ones with a specified ' | 118 | help='restrict manifest projects to ones with a specified ' |
| 114 | 'platform group [auto|all|none|linux|darwin|...]', | 119 | 'platform group [auto|all|none|linux|darwin|...]', |
| 115 | metavar='PLATFORM') | 120 | metavar='PLATFORM') |
| 121 | g.add_option('--no-clone-bundle', | ||
| 122 | dest='no_clone_bundle', action='store_true', | ||
| 123 | help='disable use of /clone.bundle on HTTP/HTTPS') | ||
| 116 | 124 | ||
| 117 | # Tool | 125 | # Tool |
| 118 | g = p.add_option_group('repo Version options') | 126 | g = p.add_option_group('repo Version options') |
| @@ -222,7 +230,8 @@ to update the working directory files. | |||
| 222 | 'in another location.', file=sys.stderr) | 230 | 'in another location.', file=sys.stderr) |
| 223 | sys.exit(1) | 231 | sys.exit(1) |
| 224 | 232 | ||
| 225 | if not m.Sync_NetworkHalf(is_new=is_new): | 233 | if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet, |
| 234 | clone_bundle=not opt.no_clone_bundle): | ||
| 226 | r = m.GetRemote(m.remote.name) | 235 | r = m.GetRemote(m.remote.name) |
| 227 | print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr) | 236 | print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr) |
| 228 | 237 | ||
diff --git a/subcmds/start.py b/subcmds/start.py index d1430a9d..290b6897 100644 --- a/subcmds/start.py +++ b/subcmds/start.py | |||
| @@ -54,8 +54,7 @@ revision specified in the manifest. | |||
| 54 | if not opt.all: | 54 | if not opt.all: |
| 55 | projects = args[1:] | 55 | projects = args[1:] |
| 56 | if len(projects) < 1: | 56 | if len(projects) < 1: |
| 57 | print("error: at least one project must be specified", file=sys.stderr) | 57 | projects = ['.',] # start it in the local project by default |
| 58 | sys.exit(1) | ||
| 59 | 58 | ||
| 60 | all_projects = self.GetProjects(projects, | 59 | all_projects = self.GetProjects(projects, |
| 61 | missing_ok=bool(self.gitc_manifest)) | 60 | missing_ok=bool(self.gitc_manifest)) |
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 9124a653..bbb166c0 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
| @@ -255,7 +255,7 @@ later is required to fix a server side protocol bug. | |||
| 255 | dest='repo_upgraded', action='store_true', | 255 | dest='repo_upgraded', action='store_true', |
| 256 | help=SUPPRESS_HELP) | 256 | help=SUPPRESS_HELP) |
| 257 | 257 | ||
| 258 | def _FetchProjectList(self, opt, projects, *args, **kwargs): | 258 | def _FetchProjectList(self, opt, projects, sem, *args, **kwargs): |
| 259 | """Main function of the fetch threads when jobs are > 1. | 259 | """Main function of the fetch threads when jobs are > 1. |
| 260 | 260 | ||
| 261 | Delegates most of the work to _FetchHelper. | 261 | Delegates most of the work to _FetchHelper. |
| @@ -263,15 +263,20 @@ later is required to fix a server side protocol bug. | |||
| 263 | Args: | 263 | Args: |
| 264 | opt: Program options returned from optparse. See _Options(). | 264 | opt: Program options returned from optparse. See _Options(). |
| 265 | projects: Projects to fetch. | 265 | projects: Projects to fetch. |
| 266 | sem: We'll release() this semaphore when we exit so that another thread | ||
| 267 | can be started up. | ||
| 266 | *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the | 268 | *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the |
| 267 | _FetchHelper docstring for details. | 269 | _FetchHelper docstring for details. |
| 268 | """ | 270 | """ |
| 269 | for project in projects: | 271 | try: |
| 270 | success = self._FetchHelper(opt, project, *args, **kwargs) | 272 | for project in projects: |
| 271 | if not success and not opt.force_broken: | 273 | success = self._FetchHelper(opt, project, *args, **kwargs) |
| 272 | break | 274 | if not success and not opt.force_broken: |
| 275 | break | ||
| 276 | finally: | ||
| 277 | sem.release() | ||
| 273 | 278 | ||
| 274 | def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): | 279 | def _FetchHelper(self, opt, project, lock, fetched, pm, err_event): |
| 275 | """Fetch git objects for a single project. | 280 | """Fetch git objects for a single project. |
| 276 | 281 | ||
| 277 | Args: | 282 | Args: |
| @@ -283,8 +288,6 @@ later is required to fix a server side protocol bug. | |||
| 283 | (with our lock held). | 288 | (with our lock held). |
| 284 | pm: Instance of a Project object. We will call pm.update() (with our | 289 | pm: Instance of a Project object. We will call pm.update() (with our |
| 285 | lock held). | 290 | 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 | 291 | err_event: We'll set this event in the case of an error (after printing |
| 289 | out info about the error). | 292 | out info about the error). |
| 290 | 293 | ||
| @@ -340,7 +343,6 @@ later is required to fix a server side protocol bug. | |||
| 340 | finally: | 343 | finally: |
| 341 | if did_lock: | 344 | if did_lock: |
| 342 | lock.release() | 345 | lock.release() |
| 343 | sem.release() | ||
| 344 | 346 | ||
| 345 | return success | 347 | return success |
| 346 | 348 | ||
| @@ -365,10 +367,10 @@ later is required to fix a server side protocol bug. | |||
| 365 | sem.acquire() | 367 | sem.acquire() |
| 366 | kwargs = dict(opt=opt, | 368 | kwargs = dict(opt=opt, |
| 367 | projects=project_list, | 369 | projects=project_list, |
| 370 | sem=sem, | ||
| 368 | lock=lock, | 371 | lock=lock, |
| 369 | fetched=fetched, | 372 | fetched=fetched, |
| 370 | pm=pm, | 373 | pm=pm, |
| 371 | sem=sem, | ||
| 372 | err_event=err_event) | 374 | err_event=err_event) |
| 373 | if self.jobs > 1: | 375 | if self.jobs > 1: |
| 374 | t = _threading.Thread(target = self._FetchProjectList, | 376 | t = _threading.Thread(target = self._FetchProjectList, |
| @@ -397,9 +399,12 @@ later is required to fix a server side protocol bug. | |||
| 397 | return fetched | 399 | return fetched |
| 398 | 400 | ||
| 399 | def _GCProjects(self, projects): | 401 | def _GCProjects(self, projects): |
| 400 | gitdirs = {} | 402 | gc_gitdirs = {} |
| 401 | for project in projects: | 403 | for project in projects: |
| 402 | gitdirs[project.gitdir] = project.bare_git | 404 | if len(project.manifest.GetProjectsWithName(project.name)) > 1: |
| 405 | print('Shared project %s found, disabling pruning.' % project.name) | ||
| 406 | project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never') | ||
| 407 | gc_gitdirs[project.gitdir] = project.bare_git | ||
| 403 | 408 | ||
| 404 | has_dash_c = git_require((1, 7, 2)) | 409 | has_dash_c = git_require((1, 7, 2)) |
| 405 | if multiprocessing and has_dash_c: | 410 | if multiprocessing and has_dash_c: |
| @@ -409,7 +414,7 @@ later is required to fix a server side protocol bug. | |||
| 409 | jobs = min(self.jobs, cpu_count) | 414 | jobs = min(self.jobs, cpu_count) |
| 410 | 415 | ||
| 411 | if jobs < 2: | 416 | if jobs < 2: |
| 412 | for bare_git in gitdirs.values(): | 417 | for bare_git in gc_gitdirs.values(): |
| 413 | bare_git.gc('--auto') | 418 | bare_git.gc('--auto') |
| 414 | return | 419 | return |
| 415 | 420 | ||
| @@ -431,7 +436,7 @@ later is required to fix a server side protocol bug. | |||
| 431 | finally: | 436 | finally: |
| 432 | sem.release() | 437 | sem.release() |
| 433 | 438 | ||
| 434 | for bare_git in gitdirs.values(): | 439 | for bare_git in gc_gitdirs.values(): |
| 435 | if err_event.isSet(): | 440 | if err_event.isSet(): |
| 436 | break | 441 | break |
| 437 | sem.acquire() | 442 | sem.acquire() |
| @@ -454,6 +459,65 @@ later is required to fix a server side protocol bug. | |||
| 454 | else: | 459 | else: |
| 455 | self.manifest._Unload() | 460 | self.manifest._Unload() |
| 456 | 461 | ||
| 462 | def _DeleteProject(self, path): | ||
| 463 | print('Deleting obsolete path %s' % path, file=sys.stderr) | ||
| 464 | |||
| 465 | # Delete the .git directory first, so we're less likely to have a partially | ||
| 466 | # working git repository around. There shouldn't be any git projects here, | ||
| 467 | # so rmtree works. | ||
| 468 | try: | ||
| 469 | shutil.rmtree(os.path.join(path, '.git')) | ||
| 470 | except OSError: | ||
| 471 | print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr) | ||
| 472 | print('error: Failed to delete obsolete path %s' % path, file=sys.stderr) | ||
| 473 | print(' remove manually, then run sync again', file=sys.stderr) | ||
| 474 | return -1 | ||
| 475 | |||
| 476 | # Delete everything under the worktree, except for directories that contain | ||
| 477 | # another git project | ||
| 478 | dirs_to_remove = [] | ||
| 479 | failed = False | ||
| 480 | for root, dirs, files in os.walk(path): | ||
| 481 | for f in files: | ||
| 482 | try: | ||
| 483 | os.remove(os.path.join(root, f)) | ||
| 484 | except OSError: | ||
| 485 | print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr) | ||
| 486 | failed = True | ||
| 487 | dirs[:] = [d for d in dirs | ||
| 488 | if not os.path.lexists(os.path.join(root, d, '.git'))] | ||
| 489 | dirs_to_remove += [os.path.join(root, d) for d in dirs | ||
| 490 | if os.path.join(root, d) not in dirs_to_remove] | ||
| 491 | for d in reversed(dirs_to_remove): | ||
| 492 | if os.path.islink(d): | ||
| 493 | try: | ||
| 494 | os.remove(d) | ||
| 495 | except OSError: | ||
| 496 | print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr) | ||
| 497 | failed = True | ||
| 498 | elif len(os.listdir(d)) == 0: | ||
| 499 | try: | ||
| 500 | os.rmdir(d) | ||
| 501 | except OSError: | ||
| 502 | print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr) | ||
| 503 | failed = True | ||
| 504 | continue | ||
| 505 | if failed: | ||
| 506 | print('error: Failed to delete obsolete path %s' % path, file=sys.stderr) | ||
| 507 | print(' remove manually, then run sync again', file=sys.stderr) | ||
| 508 | return -1 | ||
| 509 | |||
| 510 | # Try deleting parent dirs if they are empty | ||
| 511 | project_dir = path | ||
| 512 | while project_dir != self.manifest.topdir: | ||
| 513 | if len(os.listdir(project_dir)) == 0: | ||
| 514 | os.rmdir(project_dir) | ||
| 515 | else: | ||
| 516 | break | ||
| 517 | project_dir = os.path.dirname(project_dir) | ||
| 518 | |||
| 519 | return 0 | ||
| 520 | |||
| 457 | def UpdateProjectList(self): | 521 | def UpdateProjectList(self): |
| 458 | new_project_paths = [] | 522 | new_project_paths = [] |
| 459 | for project in self.GetProjects(None, missing_ok=True): | 523 | for project in self.GetProjects(None, missing_ok=True): |
| @@ -474,8 +538,8 @@ later is required to fix a server side protocol bug. | |||
| 474 | continue | 538 | continue |
| 475 | if path not in new_project_paths: | 539 | if path not in new_project_paths: |
| 476 | # If the path has already been deleted, we don't need to do it | 540 | # If the path has already been deleted, we don't need to do it |
| 477 | if os.path.exists(self.manifest.topdir + '/' + path): | 541 | gitdir = os.path.join(self.manifest.topdir, path, '.git') |
| 478 | gitdir = os.path.join(self.manifest.topdir, path, '.git') | 542 | if os.path.exists(gitdir): |
| 479 | project = Project( | 543 | project = Project( |
| 480 | manifest = self.manifest, | 544 | manifest = self.manifest, |
| 481 | name = path, | 545 | name = path, |
| @@ -494,18 +558,8 @@ later is required to fix a server side protocol bug. | |||
| 494 | print(' commit changes, then run sync again', | 558 | print(' commit changes, then run sync again', |
| 495 | file=sys.stderr) | 559 | file=sys.stderr) |
| 496 | return -1 | 560 | return -1 |
| 497 | else: | 561 | elif self._DeleteProject(project.worktree): |
| 498 | print('Deleting obsolete path %s' % project.worktree, | 562 | return -1 |
| 499 | file=sys.stderr) | ||
| 500 | shutil.rmtree(project.worktree) | ||
| 501 | # Try deleting parent subdirs if they are empty | ||
| 502 | project_dir = os.path.dirname(project.worktree) | ||
| 503 | while project_dir != self.manifest.topdir: | ||
| 504 | try: | ||
| 505 | os.rmdir(project_dir) | ||
| 506 | except OSError: | ||
| 507 | break | ||
| 508 | project_dir = os.path.dirname(project_dir) | ||
| 509 | 563 | ||
| 510 | new_project_paths.sort() | 564 | new_project_paths.sort() |
| 511 | fd = open(file_path, 'w') | 565 | fd = open(file_path, 'w') |
diff --git a/subcmds/upload.py b/subcmds/upload.py index 674fc17d..1172dadc 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
| @@ -454,9 +454,15 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 454 | if avail: | 454 | if avail: |
| 455 | pending.append((project, avail)) | 455 | pending.append((project, avail)) |
| 456 | 456 | ||
| 457 | if pending and (not opt.bypass_hooks): | 457 | if not pending: |
| 458 | print("no branches ready for upload", file=sys.stderr) | ||
| 459 | return | ||
| 460 | |||
| 461 | if not opt.bypass_hooks: | ||
| 458 | hook = RepoHook('pre-upload', self.manifest.repo_hooks_project, | 462 | hook = RepoHook('pre-upload', self.manifest.repo_hooks_project, |
| 459 | self.manifest.topdir, abort_if_user_denies=True) | 463 | self.manifest.topdir, |
| 464 | self.manifest.manifestProject.GetRemote('origin').url, | ||
| 465 | abort_if_user_denies=True) | ||
| 460 | pending_proj_names = [project.name for (project, avail) in pending] | 466 | pending_proj_names = [project.name for (project, avail) in pending] |
| 461 | pending_worktrees = [project.worktree for (project, avail) in pending] | 467 | pending_worktrees = [project.worktree for (project, avail) in pending] |
| 462 | try: | 468 | try: |
| @@ -472,9 +478,7 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 472 | cc = _SplitEmails(opt.cc) | 478 | cc = _SplitEmails(opt.cc) |
| 473 | people = (reviewers, cc) | 479 | people = (reviewers, cc) |
| 474 | 480 | ||
| 475 | if not pending: | 481 | if len(pending) == 1 and len(pending[0][1]) == 1: |
| 476 | print("no branches ready for upload", file=sys.stderr) | ||
| 477 | elif len(pending) == 1 and len(pending[0][1]) == 1: | ||
| 478 | self._SingleBranch(opt, pending[0][1][0], people) | 482 | self._SingleBranch(opt, pending[0][1][0], people) |
| 479 | else: | 483 | else: |
| 480 | self._MultipleBranches(opt, pending, people) | 484 | self._MultipleBranches(opt, pending, people) |
