diff options
Diffstat (limited to 'subcmds/sync.py')
| -rw-r--r-- | subcmds/sync.py | 323 |
1 files changed, 181 insertions, 142 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py index d4637d0c..228a279a 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import netrc | 17 | import netrc |
| 17 | from optparse import SUPPRESS_HELP | 18 | from optparse import SUPPRESS_HELP |
| 18 | import os | 19 | import os |
| @@ -44,13 +45,13 @@ try: | |||
| 44 | except ImportError: | 45 | except ImportError: |
| 45 | multiprocessing = None | 46 | multiprocessing = None |
| 46 | 47 | ||
| 47 | from git_command import GIT | 48 | from git_command import GIT, git_require |
| 48 | from git_refs import R_HEADS, HEAD | 49 | from git_refs import R_HEADS, HEAD |
| 49 | from main import WrapperModule | 50 | from main import WrapperModule |
| 50 | from project import Project | 51 | from project import Project |
| 51 | from project import RemoteSpec | 52 | from project import RemoteSpec |
| 52 | from command import Command, MirrorSafeCommand | 53 | from command import Command, MirrorSafeCommand |
| 53 | from error import RepoChangedException, GitError | 54 | from error import RepoChangedException, GitError, ManifestParseError |
| 54 | from project import SyncBuffer | 55 | from project import SyncBuffer |
| 55 | from progress import Progress | 56 | from progress import Progress |
| 56 | 57 | ||
| @@ -113,6 +114,9 @@ resumeable bundle file on a content delivery network. This | |||
| 113 | may be necessary if there are problems with the local Python | 114 | may be necessary if there are problems with the local Python |
| 114 | HTTP client or proxy configuration, but the Git binary works. | 115 | HTTP client or proxy configuration, but the Git binary works. |
| 115 | 116 | ||
| 117 | The --fetch-submodules option enables fetching Git submodules | ||
| 118 | of a project from server. | ||
| 119 | |||
| 116 | SSH Connections | 120 | SSH Connections |
| 117 | --------------- | 121 | --------------- |
| 118 | 122 | ||
| @@ -144,27 +148,30 @@ later is required to fix a server side protocol bug. | |||
| 144 | """ | 148 | """ |
| 145 | 149 | ||
| 146 | def _Options(self, p, show_smart=True): | 150 | def _Options(self, p, show_smart=True): |
| 147 | self.jobs = self.manifest.default.sync_j | 151 | try: |
| 152 | self.jobs = self.manifest.default.sync_j | ||
| 153 | except ManifestParseError: | ||
| 154 | self.jobs = 1 | ||
| 148 | 155 | ||
| 149 | p.add_option('-f', '--force-broken', | 156 | p.add_option('-f', '--force-broken', |
| 150 | dest='force_broken', action='store_true', | 157 | dest='force_broken', action='store_true', |
| 151 | help="continue sync even if a project fails to sync") | 158 | help="continue sync even if a project fails to sync") |
| 152 | p.add_option('-l','--local-only', | 159 | p.add_option('-l', '--local-only', |
| 153 | dest='local_only', action='store_true', | 160 | dest='local_only', action='store_true', |
| 154 | help="only update working tree, don't fetch") | 161 | help="only update working tree, don't fetch") |
| 155 | p.add_option('-n','--network-only', | 162 | p.add_option('-n', '--network-only', |
| 156 | dest='network_only', action='store_true', | 163 | dest='network_only', action='store_true', |
| 157 | help="fetch only, don't update working tree") | 164 | help="fetch only, don't update working tree") |
| 158 | p.add_option('-d','--detach', | 165 | p.add_option('-d', '--detach', |
| 159 | dest='detach_head', action='store_true', | 166 | dest='detach_head', action='store_true', |
| 160 | help='detach projects back to manifest revision') | 167 | help='detach projects back to manifest revision') |
| 161 | p.add_option('-c','--current-branch', | 168 | p.add_option('-c', '--current-branch', |
| 162 | dest='current_branch_only', action='store_true', | 169 | dest='current_branch_only', action='store_true', |
| 163 | help='fetch only current branch from server') | 170 | help='fetch only current branch from server') |
| 164 | p.add_option('-q','--quiet', | 171 | p.add_option('-q', '--quiet', |
| 165 | dest='quiet', action='store_true', | 172 | dest='quiet', action='store_true', |
| 166 | help='be more quiet') | 173 | help='be more quiet') |
| 167 | p.add_option('-j','--jobs', | 174 | p.add_option('-j', '--jobs', |
| 168 | dest='jobs', action='store', type='int', | 175 | dest='jobs', action='store', type='int', |
| 169 | help="projects to fetch simultaneously (default %d)" % self.jobs) | 176 | help="projects to fetch simultaneously (default %d)" % self.jobs) |
| 170 | p.add_option('-m', '--manifest-name', | 177 | p.add_option('-m', '--manifest-name', |
| @@ -173,6 +180,15 @@ later is required to fix a server side protocol bug. | |||
| 173 | p.add_option('--no-clone-bundle', | 180 | p.add_option('--no-clone-bundle', |
| 174 | dest='no_clone_bundle', action='store_true', | 181 | dest='no_clone_bundle', action='store_true', |
| 175 | help='disable use of /clone.bundle on HTTP/HTTPS') | 182 | help='disable use of /clone.bundle on HTTP/HTTPS') |
| 183 | p.add_option('-u', '--manifest-server-username', action='store', | ||
| 184 | dest='manifest_server_username', | ||
| 185 | help='username to authenticate with the manifest server') | ||
| 186 | p.add_option('-p', '--manifest-server-password', action='store', | ||
| 187 | dest='manifest_server_password', | ||
| 188 | help='password to authenticate with the manifest server') | ||
| 189 | p.add_option('--fetch-submodules', | ||
| 190 | dest='fetch_submodules', action='store_true', | ||
| 191 | help='fetch submodules from server') | ||
| 176 | if show_smart: | 192 | if show_smart: |
| 177 | p.add_option('-s', '--smart-sync', | 193 | p.add_option('-s', '--smart-sync', |
| 178 | dest='smart_sync', action='store_true', | 194 | dest='smart_sync', action='store_true', |
| @@ -180,12 +196,6 @@ later is required to fix a server side protocol bug. | |||
| 180 | p.add_option('-t', '--smart-tag', | 196 | p.add_option('-t', '--smart-tag', |
| 181 | dest='smart_tag', action='store', | 197 | dest='smart_tag', action='store', |
| 182 | help='smart sync using manifest from a known tag') | 198 | help='smart sync using manifest from a known tag') |
| 183 | p.add_option('-u', '--manifest-server-username', action='store', | ||
| 184 | dest='manifest_server_username', | ||
| 185 | help='username to authenticate with the manifest server') | ||
| 186 | p.add_option('-p', '--manifest-server-password', action='store', | ||
| 187 | dest='manifest_server_password', | ||
| 188 | help='password to authenticate with the manifest server') | ||
| 189 | 199 | ||
| 190 | g = p.add_option_group('repo Version options') | 200 | g = p.add_option_group('repo Version options') |
| 191 | g.add_option('--no-repo-verify', | 201 | g.add_option('--no-repo-verify', |
| @@ -196,61 +206,62 @@ later is required to fix a server side protocol bug. | |||
| 196 | help=SUPPRESS_HELP) | 206 | help=SUPPRESS_HELP) |
| 197 | 207 | ||
| 198 | def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): | 208 | def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): |
| 199 | """Main function of the fetch threads when jobs are > 1. | 209 | """Main function of the fetch threads when jobs are > 1. |
| 200 | 210 | ||
| 201 | Args: | 211 | Args: |
| 202 | opt: Program options returned from optparse. See _Options(). | 212 | opt: Program options returned from optparse. See _Options(). |
| 203 | project: Project object for the project to fetch. | 213 | project: Project object for the project to fetch. |
| 204 | lock: Lock for accessing objects that are shared amongst multiple | 214 | lock: Lock for accessing objects that are shared amongst multiple |
| 205 | _FetchHelper() threads. | 215 | _FetchHelper() threads. |
| 206 | fetched: set object that we will add project.gitdir to when we're done | 216 | fetched: set object that we will add project.gitdir to when we're done |
| 207 | (with our lock held). | 217 | (with our lock held). |
| 208 | pm: Instance of a Project object. We will call pm.update() (with our | 218 | pm: Instance of a Project object. We will call pm.update() (with our |
| 209 | lock held). | 219 | lock held). |
| 210 | sem: We'll release() this semaphore when we exit so that another thread | 220 | sem: We'll release() this semaphore when we exit so that another thread |
| 211 | can be started up. | 221 | can be started up. |
| 212 | err_event: We'll set this event in the case of an error (after printing | 222 | err_event: We'll set this event in the case of an error (after printing |
| 213 | out info about the error). | 223 | out info about the error). |
| 214 | """ | 224 | """ |
| 215 | # We'll set to true once we've locked the lock. | 225 | # We'll set to true once we've locked the lock. |
| 216 | did_lock = False | 226 | did_lock = False |
| 217 | 227 | ||
| 218 | # Encapsulate everything in a try/except/finally so that: | 228 | # Encapsulate everything in a try/except/finally so that: |
| 219 | # - We always set err_event in the case of an exception. | 229 | # - We always set err_event in the case of an exception. |
| 220 | # - We always make sure we call sem.release(). | 230 | # - We always make sure we call sem.release(). |
| 221 | # - We always make sure we unlock the lock if we locked it. | 231 | # - We always make sure we unlock the lock if we locked it. |
| 232 | try: | ||
| 222 | try: | 233 | try: |
| 223 | try: | 234 | start = time.time() |
| 224 | start = time.time() | 235 | success = project.Sync_NetworkHalf( |
| 225 | success = project.Sync_NetworkHalf( | 236 | quiet=opt.quiet, |
| 226 | quiet=opt.quiet, | 237 | current_branch_only=opt.current_branch_only, |
| 227 | current_branch_only=opt.current_branch_only, | 238 | clone_bundle=not opt.no_clone_bundle) |
| 228 | clone_bundle=not opt.no_clone_bundle) | 239 | self._fetch_times.Set(project, time.time() - start) |
| 229 | self._fetch_times.Set(project, time.time() - start) | 240 | |
| 230 | 241 | # Lock around all the rest of the code, since printing, updating a set | |
| 231 | # Lock around all the rest of the code, since printing, updating a set | 242 | # and Progress.update() are not thread safe. |
| 232 | # and Progress.update() are not thread safe. | 243 | lock.acquire() |
| 233 | lock.acquire() | 244 | did_lock = True |
| 234 | did_lock = True | 245 | |
| 235 | 246 | if not success: | |
| 236 | if not success: | 247 | print('error: Cannot fetch %s' % project.name, file=sys.stderr) |
| 237 | print >>sys.stderr, 'error: Cannot fetch %s' % project.name | 248 | if opt.force_broken: |
| 238 | if opt.force_broken: | 249 | print('warn: --force-broken, continuing to sync', |
| 239 | print >>sys.stderr, 'warn: --force-broken, continuing to sync' | 250 | file=sys.stderr) |
| 240 | else: | 251 | else: |
| 241 | raise _FetchError() | 252 | raise _FetchError() |
| 242 | 253 | ||
| 243 | fetched.add(project.gitdir) | 254 | fetched.add(project.gitdir) |
| 244 | pm.update() | 255 | pm.update() |
| 245 | except _FetchError: | 256 | except _FetchError: |
| 246 | err_event.set() | 257 | err_event.set() |
| 247 | except: | 258 | except: |
| 248 | err_event.set() | 259 | err_event.set() |
| 249 | raise | 260 | raise |
| 250 | finally: | 261 | finally: |
| 251 | if did_lock: | 262 | if did_lock: |
| 252 | lock.release() | 263 | lock.release() |
| 253 | sem.release() | 264 | sem.release() |
| 254 | 265 | ||
| 255 | def _Fetch(self, projects, opt): | 266 | def _Fetch(self, projects, opt): |
| 256 | fetched = set() | 267 | fetched = set() |
| @@ -265,9 +276,9 @@ later is required to fix a server side protocol bug. | |||
| 265 | clone_bundle=not opt.no_clone_bundle): | 276 | clone_bundle=not opt.no_clone_bundle): |
| 266 | fetched.add(project.gitdir) | 277 | fetched.add(project.gitdir) |
| 267 | else: | 278 | else: |
| 268 | print >>sys.stderr, 'error: Cannot fetch %s' % project.name | 279 | print('error: Cannot fetch %s' % project.name, file=sys.stderr) |
| 269 | if opt.force_broken: | 280 | if opt.force_broken: |
| 270 | print >>sys.stderr, 'warn: --force-broken, continuing to sync' | 281 | print('warn: --force-broken, continuing to sync', file=sys.stderr) |
| 271 | else: | 282 | else: |
| 272 | sys.exit(1) | 283 | sys.exit(1) |
| 273 | else: | 284 | else: |
| @@ -300,7 +311,7 @@ later is required to fix a server side protocol bug. | |||
| 300 | 311 | ||
| 301 | # If we saw an error, exit with code 1 so that other scripts can check. | 312 | # If we saw an error, exit with code 1 so that other scripts can check. |
| 302 | if err_event.isSet(): | 313 | if err_event.isSet(): |
| 303 | print >>sys.stderr, '\nerror: Exited sync due to fetch errors' | 314 | print('\nerror: Exited sync due to fetch errors', file=sys.stderr) |
| 304 | sys.exit(1) | 315 | sys.exit(1) |
| 305 | 316 | ||
| 306 | pm.end() | 317 | pm.end() |
| @@ -310,7 +321,8 @@ later is required to fix a server side protocol bug. | |||
| 310 | return fetched | 321 | return fetched |
| 311 | 322 | ||
| 312 | def _GCProjects(self, projects): | 323 | def _GCProjects(self, projects): |
| 313 | if multiprocessing: | 324 | has_dash_c = git_require((1, 7, 2)) |
| 325 | if multiprocessing and has_dash_c: | ||
| 314 | cpu_count = multiprocessing.cpu_count() | 326 | cpu_count = multiprocessing.cpu_count() |
| 315 | else: | 327 | else: |
| 316 | cpu_count = 1 | 328 | cpu_count = 1 |
| @@ -352,7 +364,7 @@ later is required to fix a server side protocol bug. | |||
| 352 | t.join() | 364 | t.join() |
| 353 | 365 | ||
| 354 | if err_event.isSet(): | 366 | if err_event.isSet(): |
| 355 | print >>sys.stderr, '\nerror: Exited sync due to gc errors' | 367 | print('\nerror: Exited sync due to gc errors', file=sys.stderr) |
| 356 | sys.exit(1) | 368 | sys.exit(1) |
| 357 | 369 | ||
| 358 | def UpdateProjectList(self): | 370 | def UpdateProjectList(self): |
| @@ -376,34 +388,36 @@ later is required to fix a server side protocol bug. | |||
| 376 | if path not in new_project_paths: | 388 | if path not in new_project_paths: |
| 377 | # If the path has already been deleted, we don't need to do it | 389 | # If the path has already been deleted, we don't need to do it |
| 378 | if os.path.exists(self.manifest.topdir + '/' + path): | 390 | if os.path.exists(self.manifest.topdir + '/' + path): |
| 379 | project = Project( | 391 | project = Project( |
| 380 | manifest = self.manifest, | 392 | manifest = self.manifest, |
| 381 | name = path, | 393 | name = path, |
| 382 | remote = RemoteSpec('origin'), | 394 | remote = RemoteSpec('origin'), |
| 383 | gitdir = os.path.join(self.manifest.topdir, | 395 | gitdir = os.path.join(self.manifest.topdir, |
| 384 | path, '.git'), | 396 | path, '.git'), |
| 385 | worktree = os.path.join(self.manifest.topdir, path), | 397 | worktree = os.path.join(self.manifest.topdir, path), |
| 386 | relpath = path, | 398 | relpath = path, |
| 387 | revisionExpr = 'HEAD', | 399 | revisionExpr = 'HEAD', |
| 388 | revisionId = None, | 400 | revisionId = None, |
| 389 | groups = None) | 401 | groups = None) |
| 390 | 402 | ||
| 391 | if project.IsDirty(): | 403 | if project.IsDirty(): |
| 392 | print >>sys.stderr, 'error: Cannot remove project "%s": \ | 404 | print('error: Cannot remove project "%s": uncommitted changes' |
| 393 | uncommitted changes are present' % project.relpath | 405 | 'are present' % project.relpath, file=sys.stderr) |
| 394 | print >>sys.stderr, ' commit changes, then run sync again' | 406 | print(' commit changes, then run sync again', |
| 395 | return -1 | 407 | file=sys.stderr) |
| 396 | else: | 408 | return -1 |
| 397 | print >>sys.stderr, 'Deleting obsolete path %s' % project.worktree | 409 | else: |
| 398 | shutil.rmtree(project.worktree) | 410 | print('Deleting obsolete path %s' % project.worktree, |
| 399 | # Try deleting parent subdirs if they are empty | 411 | file=sys.stderr) |
| 400 | project_dir = os.path.dirname(project.worktree) | 412 | shutil.rmtree(project.worktree) |
| 401 | while project_dir != self.manifest.topdir: | 413 | # Try deleting parent subdirs if they are empty |
| 402 | try: | 414 | project_dir = os.path.dirname(project.worktree) |
| 403 | os.rmdir(project_dir) | 415 | while project_dir != self.manifest.topdir: |
| 404 | except OSError: | 416 | try: |
| 405 | break | 417 | os.rmdir(project_dir) |
| 406 | project_dir = os.path.dirname(project_dir) | 418 | except OSError: |
| 419 | break | ||
| 420 | project_dir = os.path.dirname(project_dir) | ||
| 407 | 421 | ||
| 408 | new_project_paths.sort() | 422 | new_project_paths.sort() |
| 409 | fd = open(file_path, 'w') | 423 | fd = open(file_path, 'w') |
| @@ -422,24 +436,24 @@ uncommitted changes are present' % project.relpath | |||
| 422 | self.jobs = min(self.jobs, (soft_limit - 5) / 3) | 436 | self.jobs = min(self.jobs, (soft_limit - 5) / 3) |
| 423 | 437 | ||
| 424 | if opt.network_only and opt.detach_head: | 438 | if opt.network_only and opt.detach_head: |
| 425 | print >>sys.stderr, 'error: cannot combine -n and -d' | 439 | print('error: cannot combine -n and -d', file=sys.stderr) |
| 426 | sys.exit(1) | 440 | sys.exit(1) |
| 427 | if opt.network_only and opt.local_only: | 441 | if opt.network_only and opt.local_only: |
| 428 | print >>sys.stderr, 'error: cannot combine -n and -l' | 442 | print('error: cannot combine -n and -l', file=sys.stderr) |
| 429 | sys.exit(1) | 443 | sys.exit(1) |
| 430 | if opt.manifest_name and opt.smart_sync: | 444 | if opt.manifest_name and opt.smart_sync: |
| 431 | print >>sys.stderr, 'error: cannot combine -m and -s' | 445 | print('error: cannot combine -m and -s', file=sys.stderr) |
| 432 | sys.exit(1) | 446 | sys.exit(1) |
| 433 | if opt.manifest_name and opt.smart_tag: | 447 | if opt.manifest_name and opt.smart_tag: |
| 434 | print >>sys.stderr, 'error: cannot combine -m and -t' | 448 | print('error: cannot combine -m and -t', file=sys.stderr) |
| 435 | sys.exit(1) | 449 | sys.exit(1) |
| 436 | if opt.manifest_server_username or opt.manifest_server_password: | 450 | if opt.manifest_server_username or opt.manifest_server_password: |
| 437 | if not (opt.smart_sync or opt.smart_tag): | 451 | if not (opt.smart_sync or opt.smart_tag): |
| 438 | print >>sys.stderr, 'error: -u and -p may only be combined with ' \ | 452 | print('error: -u and -p may only be combined with -s or -t', |
| 439 | '-s or -t' | 453 | file=sys.stderr) |
| 440 | sys.exit(1) | 454 | sys.exit(1) |
| 441 | if None in [opt.manifest_server_username, opt.manifest_server_password]: | 455 | if None in [opt.manifest_server_username, opt.manifest_server_password]: |
| 442 | print >>sys.stderr, 'error: both -u and -p must be given' | 456 | print('error: both -u and -p must be given', file=sys.stderr) |
| 443 | sys.exit(1) | 457 | sys.exit(1) |
| 444 | 458 | ||
| 445 | if opt.manifest_name: | 459 | if opt.manifest_name: |
| @@ -447,8 +461,8 @@ uncommitted changes are present' % project.relpath | |||
| 447 | 461 | ||
| 448 | if opt.smart_sync or opt.smart_tag: | 462 | if opt.smart_sync or opt.smart_tag: |
| 449 | if not self.manifest.manifest_server: | 463 | if not self.manifest.manifest_server: |
| 450 | print >>sys.stderr, \ | 464 | print('error: cannot smart sync: no manifest server defined in' |
| 451 | 'error: cannot smart sync: no manifest server defined in manifest' | 465 | 'manifest', file=sys.stderr) |
| 452 | sys.exit(1) | 466 | sys.exit(1) |
| 453 | 467 | ||
| 454 | manifest_server = self.manifest.manifest_server | 468 | manifest_server = self.manifest.manifest_server |
| @@ -463,7 +477,8 @@ uncommitted changes are present' % project.relpath | |||
| 463 | try: | 477 | try: |
| 464 | info = netrc.netrc() | 478 | info = netrc.netrc() |
| 465 | except IOError: | 479 | except IOError: |
| 466 | print >>sys.stderr, '.netrc file does not exist or could not be opened' | 480 | print('.netrc file does not exist or could not be opened', |
| 481 | file=sys.stderr) | ||
| 467 | else: | 482 | else: |
| 468 | try: | 483 | try: |
| 469 | parse_result = urlparse.urlparse(manifest_server) | 484 | parse_result = urlparse.urlparse(manifest_server) |
| @@ -473,10 +488,10 @@ uncommitted changes are present' % project.relpath | |||
| 473 | except TypeError: | 488 | except TypeError: |
| 474 | # TypeError is raised when the given hostname is not present | 489 | # TypeError is raised when the given hostname is not present |
| 475 | # in the .netrc file. | 490 | # in the .netrc file. |
| 476 | print >>sys.stderr, 'No credentials found for %s in .netrc' % \ | 491 | print('No credentials found for %s in .netrc' |
| 477 | parse_result.hostname | 492 | % parse_result.hostname, file=sys.stderr) |
| 478 | except netrc.NetrcParseError as e: | 493 | except netrc.NetrcParseError as e: |
| 479 | print >>sys.stderr, 'Error parsing .netrc file: %s' % e | 494 | print('Error parsing .netrc file: %s' % e, file=sys.stderr) |
| 480 | 495 | ||
| 481 | if (username and password): | 496 | if (username and password): |
| 482 | manifest_server = manifest_server.replace('://', '://%s:%s@' % | 497 | manifest_server = manifest_server.replace('://', '://%s:%s@' % |
| @@ -515,20 +530,21 @@ uncommitted changes are present' % project.relpath | |||
| 515 | finally: | 530 | finally: |
| 516 | f.close() | 531 | f.close() |
| 517 | except IOError: | 532 | except IOError: |
| 518 | print >>sys.stderr, 'error: cannot write manifest to %s' % \ | 533 | print('error: cannot write manifest to %s' % manifest_path, |
| 519 | manifest_path | 534 | file=sys.stderr) |
| 520 | sys.exit(1) | 535 | sys.exit(1) |
| 521 | self.manifest.Override(manifest_name) | 536 | self.manifest.Override(manifest_name) |
| 522 | else: | 537 | else: |
| 523 | print >>sys.stderr, 'error: %s' % manifest_str | 538 | print('error: %s' % manifest_str, file=sys.stderr) |
| 524 | sys.exit(1) | 539 | sys.exit(1) |
| 525 | except (socket.error, IOError, xmlrpclib.Fault) as e: | 540 | except (socket.error, IOError, xmlrpclib.Fault) as e: |
| 526 | print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%s' % ( | 541 | print('error: cannot connect to manifest server %s:\n%s' |
| 527 | self.manifest.manifest_server, e) | 542 | % (self.manifest.manifest_server, e), file=sys.stderr) |
| 528 | sys.exit(1) | 543 | sys.exit(1) |
| 529 | except xmlrpclib.ProtocolError as e: | 544 | except xmlrpclib.ProtocolError as e: |
| 530 | print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%d %s' % ( | 545 | print('error: cannot connect to manifest server %s:\n%d %s' |
| 531 | self.manifest.manifest_server, e.errcode, e.errmsg) | 546 | % (self.manifest.manifest_server, e.errcode, e.errmsg), |
| 547 | file=sys.stderr) | ||
| 532 | sys.exit(1) | 548 | sys.exit(1) |
| 533 | 549 | ||
| 534 | rp = self.manifest.repoProject | 550 | rp = self.manifest.repoProject |
| @@ -552,7 +568,9 @@ uncommitted changes are present' % project.relpath | |||
| 552 | self.manifest._Unload() | 568 | self.manifest._Unload() |
| 553 | if opt.jobs is None: | 569 | if opt.jobs is None: |
| 554 | self.jobs = self.manifest.default.sync_j | 570 | self.jobs = self.manifest.default.sync_j |
| 555 | all_projects = self.GetProjects(args, missing_ok=True) | 571 | all_projects = self.GetProjects(args, |
| 572 | missing_ok=True, | ||
| 573 | submodules_ok=opt.fetch_submodules) | ||
| 556 | 574 | ||
| 557 | self._fetch_times = _FetchTimes(self.manifest) | 575 | self._fetch_times = _FetchTimes(self.manifest) |
| 558 | if not opt.local_only: | 576 | if not opt.local_only: |
| @@ -563,12 +581,33 @@ uncommitted changes are present' % project.relpath | |||
| 563 | to_fetch.extend(all_projects) | 581 | to_fetch.extend(all_projects) |
| 564 | to_fetch.sort(key=self._fetch_times.Get, reverse=True) | 582 | to_fetch.sort(key=self._fetch_times.Get, reverse=True) |
| 565 | 583 | ||
| 566 | self._Fetch(to_fetch, opt) | 584 | fetched = self._Fetch(to_fetch, opt) |
| 567 | _PostRepoFetch(rp, opt.no_repo_verify) | 585 | _PostRepoFetch(rp, opt.no_repo_verify) |
| 568 | if opt.network_only: | 586 | if opt.network_only: |
| 569 | # bail out now; the rest touches the working tree | 587 | # bail out now; the rest touches the working tree |
| 570 | return | 588 | return |
| 571 | 589 | ||
| 590 | # Iteratively fetch missing and/or nested unregistered submodules | ||
| 591 | previously_missing_set = set() | ||
| 592 | while True: | ||
| 593 | self.manifest._Unload() | ||
| 594 | all_projects = self.GetProjects(args, | ||
| 595 | missing_ok=True, | ||
| 596 | submodules_ok=opt.fetch_submodules) | ||
| 597 | missing = [] | ||
| 598 | for project in all_projects: | ||
| 599 | if project.gitdir not in fetched: | ||
| 600 | missing.append(project) | ||
| 601 | if not missing: | ||
| 602 | break | ||
| 603 | # Stop us from non-stopped fetching actually-missing repos: If set of | ||
| 604 | # missing repos has not been changed from last fetch, we break. | ||
| 605 | missing_set = set(p.name for p in missing) | ||
| 606 | if previously_missing_set == missing_set: | ||
| 607 | break | ||
| 608 | previously_missing_set = missing_set | ||
| 609 | fetched.update(self._Fetch(missing, opt)) | ||
| 610 | |||
| 572 | if self.manifest.IsMirror: | 611 | if self.manifest.IsMirror: |
| 573 | # bail out now, we have no working tree | 612 | # bail out now, we have no working tree |
| 574 | return | 613 | return |
| @@ -584,14 +623,14 @@ uncommitted changes are present' % project.relpath | |||
| 584 | if project.worktree: | 623 | if project.worktree: |
| 585 | project.Sync_LocalHalf(syncbuf) | 624 | project.Sync_LocalHalf(syncbuf) |
| 586 | pm.end() | 625 | pm.end() |
| 587 | print >>sys.stderr | 626 | print(file=sys.stderr) |
| 588 | if not syncbuf.Finish(): | 627 | if not syncbuf.Finish(): |
| 589 | sys.exit(1) | 628 | sys.exit(1) |
| 590 | 629 | ||
| 591 | # If there's a notice that's supposed to print at the end of the sync, print | 630 | # If there's a notice that's supposed to print at the end of the sync, print |
| 592 | # it now... | 631 | # it now... |
| 593 | if self.manifest.notice: | 632 | if self.manifest.notice: |
| 594 | print self.manifest.notice | 633 | print(self.manifest.notice) |
| 595 | 634 | ||
| 596 | def _PostRepoUpgrade(manifest, quiet=False): | 635 | def _PostRepoUpgrade(manifest, quiet=False): |
| 597 | wrapper = WrapperModule() | 636 | wrapper = WrapperModule() |
| @@ -603,27 +642,28 @@ def _PostRepoUpgrade(manifest, quiet=False): | |||
| 603 | 642 | ||
| 604 | def _PostRepoFetch(rp, no_repo_verify=False, verbose=False): | 643 | def _PostRepoFetch(rp, no_repo_verify=False, verbose=False): |
| 605 | if rp.HasChanges: | 644 | if rp.HasChanges: |
| 606 | print >>sys.stderr, 'info: A new version of repo is available' | 645 | print('info: A new version of repo is available', file=sys.stderr) |
| 607 | print >>sys.stderr, '' | 646 | print(file=sys.stderr) |
| 608 | if no_repo_verify or _VerifyTag(rp): | 647 | if no_repo_verify or _VerifyTag(rp): |
| 609 | syncbuf = SyncBuffer(rp.config) | 648 | syncbuf = SyncBuffer(rp.config) |
| 610 | rp.Sync_LocalHalf(syncbuf) | 649 | rp.Sync_LocalHalf(syncbuf) |
| 611 | if not syncbuf.Finish(): | 650 | if not syncbuf.Finish(): |
| 612 | sys.exit(1) | 651 | sys.exit(1) |
| 613 | print >>sys.stderr, 'info: Restarting repo with latest version' | 652 | print('info: Restarting repo with latest version', file=sys.stderr) |
| 614 | raise RepoChangedException(['--repo-upgraded']) | 653 | raise RepoChangedException(['--repo-upgraded']) |
| 615 | else: | 654 | else: |
| 616 | print >>sys.stderr, 'warning: Skipped upgrade to unverified version' | 655 | print('warning: Skipped upgrade to unverified version', file=sys.stderr) |
| 617 | else: | 656 | else: |
| 618 | if verbose: | 657 | if verbose: |
| 619 | print >>sys.stderr, 'repo version %s is current' % rp.work_git.describe(HEAD) | 658 | print('repo version %s is current' % rp.work_git.describe(HEAD), |
| 659 | file=sys.stderr) | ||
| 620 | 660 | ||
| 621 | def _VerifyTag(project): | 661 | def _VerifyTag(project): |
| 622 | gpg_dir = os.path.expanduser('~/.repoconfig/gnupg') | 662 | gpg_dir = os.path.expanduser('~/.repoconfig/gnupg') |
| 623 | if not os.path.exists(gpg_dir): | 663 | if not os.path.exists(gpg_dir): |
| 624 | print >>sys.stderr,\ | 664 | print('warning: GnuPG was not available during last "repo init"\n' |
| 625 | """warning: GnuPG was not available during last "repo init" | 665 | 'warning: Cannot automatically authenticate repo."""', |
| 626 | warning: Cannot automatically authenticate repo.""" | 666 | file=sys.stderr) |
| 627 | return True | 667 | return True |
| 628 | 668 | ||
| 629 | try: | 669 | try: |
| @@ -637,10 +677,9 @@ warning: Cannot automatically authenticate repo.""" | |||
| 637 | if rev.startswith(R_HEADS): | 677 | if rev.startswith(R_HEADS): |
| 638 | rev = rev[len(R_HEADS):] | 678 | rev = rev[len(R_HEADS):] |
| 639 | 679 | ||
| 640 | print >>sys.stderr | 680 | print(file=sys.stderr) |
| 641 | print >>sys.stderr,\ | 681 | print("warning: project '%s' branch '%s' is not signed" |
| 642 | "warning: project '%s' branch '%s' is not signed" \ | 682 | % (project.name, rev), file=sys.stderr) |
| 643 | % (project.name, rev) | ||
| 644 | return False | 683 | return False |
| 645 | 684 | ||
| 646 | env = os.environ.copy() | 685 | env = os.environ.copy() |
| @@ -659,10 +698,10 @@ warning: Cannot automatically authenticate repo.""" | |||
| 659 | proc.stderr.close() | 698 | proc.stderr.close() |
| 660 | 699 | ||
| 661 | if proc.wait() != 0: | 700 | if proc.wait() != 0: |
| 662 | print >>sys.stderr | 701 | print(file=sys.stderr) |
| 663 | print >>sys.stderr, out | 702 | print(out, file=sys.stderr) |
| 664 | print >>sys.stderr, err | 703 | print(err, file=sys.stderr) |
| 665 | print >>sys.stderr | 704 | print(file=sys.stderr) |
| 666 | return False | 705 | return False |
| 667 | return True | 706 | return True |
| 668 | 707 | ||
| @@ -696,7 +735,7 @@ class _FetchTimes(object): | |||
| 696 | try: | 735 | try: |
| 697 | try: | 736 | try: |
| 698 | self._times = pickle.load(f) | 737 | self._times = pickle.load(f) |
| 699 | except: | 738 | except IOError: |
| 700 | try: | 739 | try: |
| 701 | os.remove(self._path) | 740 | os.remove(self._path) |
| 702 | except OSError: | 741 | except OSError: |
