summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--project.py289
-rw-r--r--subcmds/init.py266
2 files changed, 315 insertions, 240 deletions
diff --git a/project.py b/project.py
index ed58c956..43cac88c 100644
--- a/project.py
+++ b/project.py
@@ -3357,3 +3357,292 @@ class ManifestProject(MetaProject):
3357 ['update-ref', '-d', 'refs/heads/default'], 3357 ['update-ref', '-d', 'refs/heads/default'],
3358 capture_stdout=True, 3358 capture_stdout=True,
3359 capture_stderr=True).Wait() == 0 3359 capture_stderr=True).Wait() == 0
3360
3361 @property
3362 def _platform_name(self):
3363 """Return the name of the platform."""
3364 return platform.system().lower()
3365
3366 def Sync(self, _kwargs_only=(), manifest_url='', manifest_branch=None,
3367 standalone_manifest=False, groups='', platform='', mirror=False,
3368 dissociate=False, reference='', worktree=False, submodules=False,
3369 archive=False, partial_clone=None, clone_filter='blob:none',
3370 partial_clone_exclude=None, clone_bundle=None, git_lfs=None,
3371 use_superproject=None, verbose=False, current_branch_only=False,
3372 tags='', depth=None):
3373 """Sync the manifest and all submanifests.
3374
3375 Args:
3376 manifest_url: a string, the URL of the manifest project.
3377 manifest_branch: a string, the manifest branch to use.
3378 standalone_manifest: a boolean, whether to store the manifest as a static
3379 file.
3380 groups: a string, restricts the checkout to projects with the specified
3381 groups.
3382 platform: a string, restrict the checkout to projects with the specified
3383 platform group.
3384 mirror: a boolean, whether to create a mirror of the remote repository.
3385 reference: a string, location of a repo instance to use as a reference.
3386 dissociate: a boolean, whether to dissociate from reference mirrors after
3387 clone.
3388 worktree: a boolean, whether to use git-worktree to manage projects.
3389 submodules: a boolean, whether sync submodules associated with the
3390 manifest project.
3391 archive: a boolean, whether to checkout each project as an archive. See
3392 git-archive.
3393 partial_clone: a boolean, whether to perform a partial clone.
3394 clone_filter: a string, filter to use with partial_clone.
3395 partial_clone_exclude : a string, comma-delimeted list of project namess
3396 to exclude from partial clone.
3397 clone_bundle: a boolean, whether to enable /clone.bundle on HTTP/HTTPS.
3398 git_lfs: a boolean, whether to enable git LFS support.
3399 use_superproject: a boolean, whether to use the manifest superproject to
3400 sync projects.
3401 verbose: a boolean, whether to show all output, rather than only errors.
3402 current_branch_only: a boolean, whether to only fetch the current manifest
3403 branch from the server.
3404 tags: a boolean, whether to fetch tags.,
3405 depth: an int, how deep of a shallow clone to create.
3406
3407 Returns:
3408 a boolean, whether the sync was successful.
3409 """
3410 assert _kwargs_only == (), 'Sync only accepts keyword arguments.'
3411
3412 # If repo has already been initialized, we take -u with the absence of
3413 # --standalone-manifest to mean "transition to a standard repo set up",
3414 # which necessitates starting fresh.
3415 # If --standalone-manifest is set, we always tear everything down and start
3416 # anew.
3417 if self.Exists:
3418 was_standalone_manifest = self.config.GetString('manifest.standalone')
3419 if was_standalone_manifest and not manifest_url:
3420 print('fatal: repo was initialized with a standlone manifest, '
3421 'cannot be re-initialized without --manifest-url/-u')
3422 return False
3423
3424 if standalone_manifest or (was_standalone_manifest and manifest_url):
3425 self.config.ClearCache()
3426 if self.gitdir and os.path.exists(self.gitdir):
3427 platform_utils.rmtree(self.gitdir)
3428 if self.worktree and os.path.exists(self.worktree):
3429 platform_utils.rmtree(self.worktree)
3430
3431 is_new = not self.Exists
3432 if is_new:
3433 if not manifest_url:
3434 print('fatal: manifest url is required.', file=sys.stderr)
3435 return False
3436
3437 if not quiet:
3438 print('Downloading manifest from %s' %
3439 (GitConfig.ForUser().UrlInsteadOf(manifest_url),),
3440 file=sys.stderr)
3441
3442 # The manifest project object doesn't keep track of the path on the
3443 # server where this git is located, so let's save that here.
3444 mirrored_manifest_git = None
3445 if reference:
3446 manifest_git_path = urllib.parse.urlparse(manifest_url).path[1:]
3447 mirrored_manifest_git = os.path.join(reference, manifest_git_path)
3448 if not mirrored_manifest_git.endswith(".git"):
3449 mirrored_manifest_git += ".git"
3450 if not os.path.exists(mirrored_manifest_git):
3451 mirrored_manifest_git = os.path.join(reference,
3452 '.repo/manifests.git')
3453
3454 self._InitGitDir(mirror_git=mirrored_manifest_git)
3455
3456 # If standalone_manifest is set, mark the project as "standalone" -- we'll
3457 # still do much of the manifests.git set up, but will avoid actual syncs to
3458 # a remote.
3459 if standalone_manifest:
3460 self.config.SetString('manifest.standalone', manifest_url)
3461 elif not manifest_url and not manifest_branch:
3462 # If -u is set and --standalone-manifest is not, then we're not in
3463 # standalone mode. Otherwise, use config to infer what we were in the last
3464 # init.
3465 standalone_manifest = bool(self.config.GetString('manifest.standalone'))
3466 if not standalone_manifest:
3467 self.config.SetString('manifest.standalone', None)
3468
3469 self._ConfigureDepth(depth)
3470
3471 # Set the remote URL before the remote branch as we might need it below.
3472 if manifest_url:
3473 r = self.GetRemote(self.remote.name)
3474 r.url = manifest_url
3475 r.ResetFetch()
3476 r.Save()
3477
3478 if not standalone_manifest:
3479 if manifest_branch:
3480 if manifest_branch == 'HEAD':
3481 manifest_branch = self.ResolveRemoteHead()
3482 if manifest_branch is None:
3483 print('fatal: unable to resolve HEAD', file=sys.stderr)
3484 return False
3485 self.revisionExpr = manifest_branch
3486 else:
3487 if is_new:
3488 default_branch = self.ResolveRemoteHead()
3489 if default_branch is None:
3490 # If the remote doesn't have HEAD configured, default to master.
3491 default_branch = 'refs/heads/master'
3492 self.revisionExpr = default_branch
3493 else:
3494 self.PreSync()
3495
3496 groups = re.split(r'[,\s]+', groups)
3497 all_platforms = ['linux', 'darwin', 'windows']
3498 platformize = lambda x: 'platform-' + x
3499 if platform == 'auto':
3500 if (not mirror and not self.config.GetString('repo.mirror') == 'true'):
3501 groups.append(platformize(self._platform_name))
3502 elif platform == 'all':
3503 groups.extend(map(platformize, all_platforms))
3504 elif platform in all_platforms:
3505 groups.append(platformize(platform))
3506 elif platform != 'none':
3507 print('fatal: invalid platform flag', file=sys.stderr)
3508 return False
3509
3510 groups = [x for x in groups if x]
3511 groupstr = ','.join(groups)
3512 if platform == 'auto' and groupstr == self.manifest.GetDefaultGroupsStr():
3513 groupstr = None
3514 self.config.SetString('manifest.groups', groupstr)
3515
3516 if reference:
3517 self.config.SetString('repo.reference', reference)
3518
3519 if dissociate:
3520 self.config.SetBoolean('repo.dissociate', dissociate)
3521
3522 if worktree:
3523 if mirror:
3524 print('fatal: --mirror and --worktree are incompatible',
3525 file=sys.stderr)
3526 return False
3527 if submodules:
3528 print('fatal: --submodules and --worktree are incompatible',
3529 file=sys.stderr)
3530 return False
3531 self.config.SetBoolean('repo.worktree', worktree)
3532 if is_new:
3533 self.use_git_worktrees = True
3534 print('warning: --worktree is experimental!', file=sys.stderr)
3535
3536 if archive:
3537 if is_new:
3538 self.config.SetBoolean('repo.archive', archive)
3539 else:
3540 print('fatal: --archive is only supported when initializing a new '
3541 'workspace.', file=sys.stderr)
3542 print('Either delete the .repo folder in this workspace, or initialize '
3543 'in another location.', file=sys.stderr)
3544 return False
3545
3546 if mirror:
3547 if is_new:
3548 self.config.SetBoolean('repo.mirror', mirror)
3549 else:
3550 print('fatal: --mirror is only supported when initializing a new '
3551 'workspace.', file=sys.stderr)
3552 print('Either delete the .repo folder in this workspace, or initialize '
3553 'in another location.', file=sys.stderr)
3554 return False
3555
3556 if partial_clone is not None:
3557 if mirror:
3558 print('fatal: --mirror and --partial-clone are mutually exclusive',
3559 file=sys.stderr)
3560 return False
3561 self.config.SetBoolean('repo.partialclone', partial_clone)
3562 if clone_filter:
3563 self.config.SetString('repo.clonefilter', clone_filter)
3564 elif self.config.GetBoolean('repo.partialclone'):
3565 clone_filter = self.config.GetString('repo.clonefilter')
3566 else:
3567 clone_filter = None
3568
3569 if partial_clone_exclude is not None:
3570 self.config.SetString('repo.partialcloneexclude', partial_clone_exclude)
3571
3572 if clone_bundle is None:
3573 clone_bundle = False if partial_clone else True
3574 else:
3575 self.config.SetBoolean('repo.clonebundle', clone_bundle)
3576
3577 if submodules:
3578 self.config.SetBoolean('repo.submodules', submodules)
3579
3580 if git_lfs is not None:
3581 if git_lfs:
3582 git_require((2, 17, 0), fail=True, msg='Git LFS support')
3583
3584 self.config.SetBoolean('repo.git-lfs', git_lfs)
3585 if not is_new:
3586 print('warning: Changing --git-lfs settings will only affect new project checkouts.\n'
3587 ' Existing projects will require manual updates.\n', file=sys.stderr)
3588
3589 if use_superproject is not None:
3590 self.config.SetBoolean('repo.superproject', use_superproject)
3591
3592 if standalone_manifest:
3593 if is_new:
3594 manifest_name = 'default.xml'
3595 manifest_data = fetch.fetch_file(manifest_url, verbose=verbose)
3596 dest = os.path.join(self.worktree, manifest_name)
3597 os.makedirs(os.path.dirname(dest), exist_ok=True)
3598 with open(dest, 'wb') as f:
3599 f.write(manifest_data)
3600 return
3601
3602 if not self.Sync_NetworkHalf(is_new=is_new, quiet=not verbose, verbose=verbose,
3603 clone_bundle=clone_bundle,
3604 current_branch_only=current_branch_only,
3605 tags=tags, submodules=submodules,
3606 clone_filter=clone_filter,
3607 partial_clone_exclude=self.manifest.PartialCloneExclude):
3608 r = self.GetRemote(self.remote.name)
3609 print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
3610
3611 # Better delete the manifest git dir if we created it; otherwise next
3612 # time (when user fixes problems) we won't go through the "is_new" logic.
3613 if is_new:
3614 platform_utils.rmtree(self.gitdir)
3615 return False
3616
3617 if manifest_branch:
3618 self.MetaBranchSwitch(submodules=submodules)
3619
3620 syncbuf = SyncBuffer(self.config)
3621 self.Sync_LocalHalf(syncbuf, submodules=submodules)
3622 syncbuf.Finish()
3623
3624 if is_new or self.CurrentBranch is None:
3625 if not self.StartBranch('default'):
3626 print('fatal: cannot create default in manifest', file=sys.stderr)
3627 return False
3628
3629 return True
3630
3631 def _ConfigureDepth(self, depth):
3632 """Configure the depth we'll sync down.
3633
3634 Args:
3635 depth: an int, how deep of a partial clone to create.
3636 """
3637 # Opt.depth will be non-None if user actually passed --depth to repo init.
3638 if depth is not None:
3639 if depth > 0:
3640 # Positive values will set the depth.
3641 depth = str(depth)
3642 else:
3643 # Negative numbers will clear the depth; passing None to SetString
3644 # will do that.
3645 depth = None
3646
3647 # We store the depth in the main manifest project.
3648 self.config.SetString('repo.depth', depth)
diff --git a/subcmds/init.py b/subcmds/init.py
index b9775a34..b6c891ac 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -128,230 +128,35 @@ to update the working directory files.
128 sys.exit(1) 128 sys.exit(1)
129 129
130 def _SyncManifest(self, opt): 130 def _SyncManifest(self, opt):
131 m = self.manifest.manifestProject 131 """Call manifestProject.Sync with arguments from opt.
132 is_new = not m.Exists
133
134 # If repo has already been initialized, we take -u with the absence of
135 # --standalone-manifest to mean "transition to a standard repo set up",
136 # which necessitates starting fresh.
137 # If --standalone-manifest is set, we always tear everything down and start
138 # anew.
139 if not is_new:
140 was_standalone_manifest = m.config.GetString('manifest.standalone')
141 if was_standalone_manifest and not opt.manifest_url:
142 print('fatal: repo was initialized with a standlone manifest, '
143 'cannot be re-initialized without --manifest-url/-u')
144 sys.exit(1)
145
146 if opt.standalone_manifest or (was_standalone_manifest and
147 opt.manifest_url):
148 m.config.ClearCache()
149 if m.gitdir and os.path.exists(m.gitdir):
150 platform_utils.rmtree(m.gitdir)
151 if m.worktree and os.path.exists(m.worktree):
152 platform_utils.rmtree(m.worktree)
153
154 is_new = not m.Exists
155 if is_new:
156 if not opt.manifest_url:
157 print('fatal: manifest url is required.', file=sys.stderr)
158 sys.exit(1)
159
160 if not opt.quiet:
161 print('Downloading manifest from %s' %
162 (GitConfig.ForUser().UrlInsteadOf(opt.manifest_url),),
163 file=sys.stderr)
164
165 # The manifest project object doesn't keep track of the path on the
166 # server where this git is located, so let's save that here.
167 mirrored_manifest_git = None
168 if opt.reference:
169 manifest_git_path = urllib.parse.urlparse(opt.manifest_url).path[1:]
170 mirrored_manifest_git = os.path.join(opt.reference, manifest_git_path)
171 if not mirrored_manifest_git.endswith(".git"):
172 mirrored_manifest_git += ".git"
173 if not os.path.exists(mirrored_manifest_git):
174 mirrored_manifest_git = os.path.join(opt.reference,
175 '.repo/manifests.git')
176
177 m._InitGitDir(mirror_git=mirrored_manifest_git)
178
179 # If standalone_manifest is set, mark the project as "standalone" -- we'll
180 # still do much of the manifests.git set up, but will avoid actual syncs to
181 # a remote.
182 standalone_manifest = False
183 if opt.standalone_manifest:
184 standalone_manifest = True
185 m.config.SetString('manifest.standalone', opt.manifest_url)
186 elif not opt.manifest_url and not opt.manifest_branch:
187 # If -u is set and --standalone-manifest is not, then we're not in
188 # standalone mode. Otherwise, use config to infer what we were in the last
189 # init.
190 standalone_manifest = bool(m.config.GetString('manifest.standalone'))
191 if not standalone_manifest:
192 m.config.SetString('manifest.standalone', None)
193
194 self._ConfigureDepth(opt)
195
196 # Set the remote URL before the remote branch as we might need it below.
197 if opt.manifest_url:
198 r = m.GetRemote(m.remote.name)
199 r.url = opt.manifest_url
200 r.ResetFetch()
201 r.Save()
202
203 if not standalone_manifest:
204 if opt.manifest_branch:
205 if opt.manifest_branch == 'HEAD':
206 opt.manifest_branch = m.ResolveRemoteHead()
207 if opt.manifest_branch is None:
208 print('fatal: unable to resolve HEAD', file=sys.stderr)
209 sys.exit(1)
210 m.revisionExpr = opt.manifest_branch
211 else:
212 if is_new:
213 default_branch = m.ResolveRemoteHead()
214 if default_branch is None:
215 # If the remote doesn't have HEAD configured, default to master.
216 default_branch = 'refs/heads/master'
217 m.revisionExpr = default_branch
218 else:
219 m.PreSync()
220
221 groups = re.split(r'[,\s]+', opt.groups)
222 all_platforms = ['linux', 'darwin', 'windows']
223 platformize = lambda x: 'platform-' + x
224 if opt.platform == 'auto':
225 if (not opt.mirror and
226 not m.config.GetString('repo.mirror') == 'true'):
227 groups.append(platformize(platform.system().lower()))
228 elif opt.platform == 'all':
229 groups.extend(map(platformize, all_platforms))
230 elif opt.platform in all_platforms:
231 groups.append(platformize(opt.platform))
232 elif opt.platform != 'none':
233 print('fatal: invalid platform flag', file=sys.stderr)
234 sys.exit(1)
235
236 groups = [x for x in groups if x]
237 groupstr = ','.join(groups)
238 if opt.platform == 'auto' and groupstr == self.manifest.GetDefaultGroupsStr():
239 groupstr = None
240 m.config.SetString('manifest.groups', groupstr)
241
242 if opt.reference:
243 m.config.SetString('repo.reference', opt.reference)
244
245 if opt.dissociate:
246 m.config.SetBoolean('repo.dissociate', opt.dissociate)
247
248 if opt.worktree:
249 if opt.mirror:
250 print('fatal: --mirror and --worktree are incompatible',
251 file=sys.stderr)
252 sys.exit(1)
253 if opt.submodules:
254 print('fatal: --submodules and --worktree are incompatible',
255 file=sys.stderr)
256 sys.exit(1)
257 m.config.SetBoolean('repo.worktree', opt.worktree)
258 if is_new:
259 m.use_git_worktrees = True
260 print('warning: --worktree is experimental!', file=sys.stderr)
261
262 if opt.archive:
263 if is_new:
264 m.config.SetBoolean('repo.archive', opt.archive)
265 else:
266 print('fatal: --archive is only supported when initializing a new '
267 'workspace.', file=sys.stderr)
268 print('Either delete the .repo folder in this workspace, or initialize '
269 'in another location.', file=sys.stderr)
270 sys.exit(1)
271
272 if opt.mirror:
273 if is_new:
274 m.config.SetBoolean('repo.mirror', opt.mirror)
275 else:
276 print('fatal: --mirror is only supported when initializing a new '
277 'workspace.', file=sys.stderr)
278 print('Either delete the .repo folder in this workspace, or initialize '
279 'in another location.', file=sys.stderr)
280 sys.exit(1)
281 132
282 if opt.partial_clone is not None: 133 Args:
283 if opt.mirror: 134 opt: options from optparse.
284 print('fatal: --mirror and --partial-clone are mutually exclusive', 135 """
285 file=sys.stderr) 136 if not self.manifest.manifestProject.Sync(
286 sys.exit(1) 137 manifest_url=opt.manifest_url,
287 m.config.SetBoolean('repo.partialclone', opt.partial_clone) 138 manifest_branch=opt.manifest_branch,
288 if opt.clone_filter: 139 standalone_manifest=opt.standalone_manifest,
289 m.config.SetString('repo.clonefilter', opt.clone_filter) 140 groups=opt.groups,
290 elif m.config.GetBoolean('repo.partialclone'): 141 platform=opt.platform,
291 opt.clone_filter = m.config.GetString('repo.clonefilter') 142 mirror=opt.mirror,
292 else: 143 dissociate=opt.dissociate,
293 opt.clone_filter = None 144 reference=opt.reference,
294 145 worktree=opt.worktree,
295 if opt.partial_clone_exclude is not None: 146 submodules=opt.submodules,
296 m.config.SetString('repo.partialcloneexclude', opt.partial_clone_exclude) 147 archive=opt.archive,
297 148 partial_clone=opt.partial_clone,
298 if opt.clone_bundle is None: 149 clone_filter=opt.clone_filter,
299 opt.clone_bundle = False if opt.partial_clone else True 150 partial_clone_exclude=opt.partial_clone_exclude,
300 else: 151 clone_bundle=opt.clone_bundle,
301 m.config.SetBoolean('repo.clonebundle', opt.clone_bundle) 152 git_lfs=opt.git_lfs,
302 153 use_superproject=opt.use_superproject,
303 if opt.submodules: 154 verbose=opt.verbose,
304 m.config.SetBoolean('repo.submodules', opt.submodules) 155 current_branch_only=opt.current_branch_only,
305 156 tags=opt.tags,
306 if opt.git_lfs is not None: 157 depth=opt.depth):
307 if opt.git_lfs:
308 git_require((2, 17, 0), fail=True, msg='Git LFS support')
309
310 m.config.SetBoolean('repo.git-lfs', opt.git_lfs)
311 if not is_new:
312 print('warning: Changing --git-lfs settings will only affect new project checkouts.\n'
313 ' Existing projects will require manual updates.\n', file=sys.stderr)
314
315 if opt.use_superproject is not None:
316 m.config.SetBoolean('repo.superproject', opt.use_superproject)
317
318 if standalone_manifest:
319 if is_new:
320 manifest_name = 'default.xml'
321 manifest_data = fetch.fetch_file(opt.manifest_url, verbose=opt.verbose)
322 dest = os.path.join(m.worktree, manifest_name)
323 os.makedirs(os.path.dirname(dest), exist_ok=True)
324 with open(dest, 'wb') as f:
325 f.write(manifest_data)
326 return
327
328 if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet, verbose=opt.verbose,
329 clone_bundle=opt.clone_bundle,
330 current_branch_only=opt.current_branch_only,
331 tags=opt.tags, submodules=opt.submodules,
332 clone_filter=opt.clone_filter,
333 partial_clone_exclude=self.manifest.PartialCloneExclude):
334 r = m.GetRemote(m.remote.name)
335 print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
336
337 # Better delete the manifest git dir if we created it; otherwise next
338 # time (when user fixes problems) we won't go through the "is_new" logic.
339 if is_new:
340 platform_utils.rmtree(m.gitdir)
341 sys.exit(1) 158 sys.exit(1)
342 159
343 if opt.manifest_branch:
344 m.MetaBranchSwitch(submodules=opt.submodules)
345
346 syncbuf = SyncBuffer(m.config)
347 m.Sync_LocalHalf(syncbuf, submodules=opt.submodules)
348 syncbuf.Finish()
349
350 if is_new or m.CurrentBranch is None:
351 if not m.StartBranch('default'):
352 print('fatal: cannot create default in manifest', file=sys.stderr)
353 sys.exit(1)
354
355 def _LinkManifest(self, name): 160 def _LinkManifest(self, name):
356 if not name: 161 if not name:
357 print('fatal: manifest name (-m) is required.', file=sys.stderr) 162 print('fatal: manifest name (-m) is required.', file=sys.stderr)
@@ -455,25 +260,6 @@ to update the working directory files.
455 if a in ('y', 'yes', 't', 'true', 'on'): 260 if a in ('y', 'yes', 't', 'true', 'on'):
456 gc.SetString('color.ui', 'auto') 261 gc.SetString('color.ui', 'auto')
457 262
458 def _ConfigureDepth(self, opt):
459 """Configure the depth we'll sync down.
460
461 Args:
462 opt: Options from optparse. We care about opt.depth.
463 """
464 # Opt.depth will be non-None if user actually passed --depth to repo init.
465 if opt.depth is not None:
466 if opt.depth > 0:
467 # Positive values will set the depth.
468 depth = str(opt.depth)
469 else:
470 # Negative numbers will clear the depth; passing None to SetString
471 # will do that.
472 depth = None
473
474 # We store the depth in the main manifest project.
475 self.manifest.manifestProject.config.SetString('repo.depth', depth)
476
477 def _DisplayResult(self, opt): 263 def _DisplayResult(self, opt):
478 if self.manifest.IsMirror: 264 if self.manifest.IsMirror:
479 init_type = 'mirror ' 265 init_type = 'mirror '