Compare commits

...

22 Commits
v2.23 ... v2.28

Author SHA1 Message Date
a8cf575d68 Omit local_manifest groups from superproject override.
When we create superproject_override.xml, do not include projects that
are present from local_manifests/*.  Such projects are fully under the
control of the local_manifests/ file.

Bug: b/238934278
Test: manual, ./run_tests
Change-Id: I40382ceb82d9cf7b8dc7b5f2abed3f6d4d80017e
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/340877
Tested-by: Xin Li <delphij@google.com>
Reviewed-by: Xin Li <delphij@google.com>
Reviewed-by: Sam Saccone 🐐 <samccone@google.com>
2022-07-15 23:32:24 +00:00
8501d4602a status, diff: display correct path for multi-manifest
Display the project path relative to the outermost manifest by default,
and relative to the sub manifest only when --this-manifest-only is
specified.

For project-related diagnostic messages, use the outermost manifest for
messages.

Change-Id: I4537d7dd412a2c182e77d6720e95c1b0ef70eb0e
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/340754
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: LaMont Jones <lamontjones@google.com>
2022-07-14 16:00:18 +00:00
8db78c7d4d project: simplify if-statement
Change-Id: I05e4505b45963fe6e85cf74a669afafd00fc83c0
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/340457
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: Martin Geisler <mgeisler@google.com>
2022-07-11 17:58:06 +00:00
9fb64ae29c upload: add ‘--ignore-untracked-files’ option
This option will suppress the

    Uncommitted changes in ... (did you forget to amend?)

prompt when there are untracked (unknown) files in the working copy.
The prompt is still shown if tracked files are modified.

Change-Id: Ia3fcc82989b7fad09b69214eda31e2d0dfc14600
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/340456
Tested-by: Martin Geisler <mgeisler@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
2022-07-11 17:57:43 +00:00
d47d9ff1cb man: regenerate
Change-Id: I3ca8ca8f502605b194ebe65b315eda08c51592a6
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/340494
Reviewed-by: LaMont Jones <lamontjones@google.com>
Tested-by: Mike Frysinger <vapier@google.com>
2022-07-11 16:42:23 +00:00
68d69635c7 Fix Projects.shareable_dirs
If this tree is not using alternates for object sharing, then we need to
continue to call it a shared directory.

Bug: https://bugs.chromium.org/p/gerrit/issues/detail?id=15982
Test: manual
Change-Id: I1750f10b192504ac67f552222f8ddb9809d344fe
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/338974
Tested-by: LaMont Jones <lamontjones@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
2022-06-08 16:49:08 +00:00
ff6b1dae1e Only sync superproject if it will be used.
If the user says `--no-use-superproject`, then do not bother syncing the
superproject.

Also add/update docstrings and comments throughout.

Change-Id: I9cdad706130501bab9a22d3099a1dae605e9c194
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/338975
Tested-by: LaMont Jones <lamontjones@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
2022-06-08 16:49:08 +00:00
bdcba7dc36 sync: add multi-manifest support
With this change, partial syncs (sync with a project list) are again
supported.

If the updated manifest includes new sub manifests, download them
inheriting options from the parent manifestProject.

Change-Id: Id952f85df2e26d34e38b251973be26434443ff56
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/334819
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: LaMont Jones <lamontjones@google.com>
2022-05-26 00:03:37 +00:00
1d00a7e2ae project: initial separation of shared project objects
For now, this is opt-in via environment variables:
  - export REPO_USE_ALTERNATES=1

The shared project logic that shares the internal .git/objects/ dir
directly between multiple projects via the project-objects/ tree has
a lot of KI with random corruption.  It all boils down to projects
sharing objects/ but not refs/.  Git operations that use refs to see
what objects are reachable and discard the rest can easily discard
objects that are used by other projects.

Consider this project layout:
<show fs layout>

There are unique refs in each of these trees that are not visible in
the others.  This means it's not safe to run basic operations like
git prune or git gc.

Since we can't share refs (each project needs to have unique refs
like HEAD in order to function), let's change how we share objects.
The old way involved symlinking .git/objects/ to the project-objects
tree.  The new way shares objects using git's info/alternates.

This means project-objects/ will only contain objects that exist in
the remote project.  Local per-project objects (like when creating
branches and making changes) will never be shared.  When running a
prune or gc operation in the per-project state, it will only ever
repack or discard those per-project objects.  The common shared
objects would only be cleaned up when running a common operation
(i.e. by repo itself).

One downside to this for users is if they try blending unrelated
upstream projects.  For example, in CrOS we have multiple kernel
projects (for diff versions) checked out.  If a dev fetched the
upstream Linus tree into one of them, the objects & tags would
not be shared with the others, so they would have to fetch the
upstream state for each project.  Annoying, but better than the
current corruption situation we're in now.

Also if the dev runs a manual `git fetch` in the per-project to
sync it up to newer state than the last `repo sync` they ran,
the objects would get duplicated.  However, git operations later
on should eventually dedupe this.

Bug: https://crbug.com/gerrit/15553
Change-Id: I313a9b8962f9d439ef98ac0ed37ecfb9e0b3864e
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/328101
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: LaMont Jones <lamontjones@google.com>
2022-05-26 00:02:18 +00:00
3a0a145b0e upload: move label validation to core function
This way we know we don't need to encode the labels.

Change-Id: Ib83ed8f4ed05f00b9d2d06a9dd3f304e4443430e
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/337518
Tested-by: Mike Frysinger <vapier@google.com>
Reviewed-by: LaMont Jones <lamontjones@google.com>
2022-05-21 19:19:44 +00:00
74737da1ab tests: switch to tempfile.TemporaryDirectory
Now that we don't need to support Python 2, we can switch to this
API for better contextmanager logic.

Change-Id: I2d03e391121886547e7808a3b5c3b470c411533f
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/337515
Reviewed-by: LaMont Jones <lamontjones@google.com>
Tested-by: Mike Frysinger <vapier@google.com>
2022-05-20 11:38:10 +00:00
0ddb677611 project: fix --use-superproject logic for init.
If init was run with --use-superproject, init failed.

If init was run without --{no,}use-superproject option then manifests
with <superproject/> elements were mishandled.

Bug: b/233226285
Test: manual
Change-Id: I737e71c89d2d7c324114f58bf2dc82b40e5beba7
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/337534
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: LaMont Jones <lamontjones@google.com>
2022-05-20 11:01:28 +00:00
501733c2ab manifest: add submanifest.default_groups attribute
When the user does not specify any manifest groups, this allows the
parent manifest to indicate which manifest groups should be used for
syncing the submanifest.

Change-Id: I88806ed35013d13dd2ab3cd245fcd4f9061112c4
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/335474
Tested-by: LaMont Jones <lamontjones@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
2022-04-29 18:42:23 +00:00
0165e20fcc project: Do not exit early on --standalone-manifest.
After we successfully download the standalone manifest file, we cannot
exit early.

Bug: https://bugs.chromium.org/p/gerrit/issues/detail?id=15861
Change-Id: Ic47c9f7e9921851f94c6f24fd82b896eff524037
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/335974
Reviewed-by: Raman Tenneti <rtenneti@google.com>
Tested-by: LaMont Jones <lamontjones@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
2022-04-29 17:13:49 +00:00
0de4fc3001 project: Add missing imports
Some imports were missed when moving manifestProject to project.py

Bug: https://bugs.chromium.org/p/gerrit/issues/detail?id=15861
Change-Id: Id8fffeaa3f88f344a13b5ab44e5403c7edd98f31
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/335554
Tested-by: LaMont Jones <lamontjones@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
Reviewed-by: Raman Tenneti <rtenneti@google.com>
2022-04-21 18:44:26 +00:00
4c11aebeb9 progress: optimize progress bar updates a bit
Rather than erase the entire line first then print out the new content,
print out the new content on top of the old and then erase anything we
didn't update.  This should result in a lot less flashing with faster
terminals.

Bug: https://crbug.com/gerrit/11293
Change-Id: Ie2920b0bf3d5e6f920b8631a1c406444b23cd12d
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/335214
Reviewed-by: LaMont Jones <lamontjones@google.com>
Tested-by: Mike Frysinger <vapier@google.com>
2022-04-19 23:50:48 +00:00
b90a422ab6 Override the manifest for the entire command
When a manifest file is overridden, remember that and keep using the
override for the remainder of the process.  If we need to revert it,
make the override name evaluate False.

Change-Id: I1eee05fec6988c1ee4a3c751c4b540d5b5d11797
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/335136
Tested-by: LaMont Jones <lamontjones@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
2022-04-19 21:28:20 +00:00
a46047a822 sync: refactor use of self.manifest
We need to iterate over multiple manifests, and generally use the
outer_client.manifest for multi-manifest support.  This refactors the
use of self.manifest into a chosen manifest.

Change-Id: I992f21d610c929675e99555ece9c38df4b635839
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/334699
Tested-by: LaMont Jones <lamontjones@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
2022-04-14 22:24:04 +00:00
5fa912b0d1 Stop passing optparse.Values to git_superproject
Make git_superproject independent of the command line by passing
the specific value instead of requiring the caller to have an
optparse.Values object to pass in.

Flag --use-superproject and --archive as incompatible in subcmds/init.py

Change-Id: Ied7c874b312e151038df903c8af4328f070f387c
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/335135
Tested-by: LaMont Jones <lamontjones@google.com>
Reviewed-by: Raman Tenneti <rtenneti@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
2022-04-14 22:23:16 +00:00
4ada043dc0 ManifestProject: add manifest_platform
And fix most of the other attributes to return the value instead of
None.

Change-Id: Iddcbbeb56238ee082bb1cae30adbd27a2f551f3d
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/335134
Tested-by: LaMont Jones <lamontjones@google.com>
Reviewed-by: Raman Tenneti <rtenneti@google.com>
Reviewed-by: Xin Li <delphij@google.com>
2022-04-14 20:56:45 +00:00
d8de29c447 forall: fix multi-manifest variables.
- REPO_PATH is relative to the root of the client. REPO_OUTERPATH is not
  needed.
- REPO_INNERPATH is relative to the sub manifest root.
- REPO_OUTERPATH is the path for the sub manifest root relative to the
  root of the client.

Change-Id: I031692891cfef2634d1358584d27a6a4df735c20
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/334899
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: LaMont Jones <lamontjones@google.com>
2022-04-14 14:31:47 +00:00
2cc3ab7663 git_superproject: only print beta notice once.
This eliminates duplicate notices during multi-manifest syncs.

Change-Id: Idcb038ddeb363368637c58c11346ebf8fd2b27ac
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/334939
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: LaMont Jones <lamontjones@google.com>
2022-04-14 00:07:25 +00:00
47 changed files with 1226 additions and 432 deletions

View File

@ -144,11 +144,10 @@ class Command(object):
help=f'number of jobs to run in parallel (default: {default})')
m = p.add_option_group('Multi-manifest options')
m.add_option('--outer-manifest', action='store_true',
m.add_option('--outer-manifest', action='store_true', default=None,
help='operate starting at the outermost manifest')
m.add_option('--no-outer-manifest', dest='outer_manifest',
action='store_false', default=None,
help='do not operate on outer manifests')
action='store_false', help='do not operate on outer manifests')
m.add_option('--this-manifest-only', action='store_true', default=None,
help='only operate on this (sub)manifest')
m.add_option('--no-this-manifest-only', '--all-manifests',
@ -186,6 +185,10 @@ class Command(object):
"""Validate common options."""
opt.quiet = opt.output_mode is False
opt.verbose = opt.output_mode is True
if opt.outer_manifest is None:
# By default, treat multi-manifest instances as a single manifest from
# the user's perspective.
opt.outer_manifest = True
def ValidateOptions(self, opt, args):
"""Validate the user options & arguments before executing.
@ -274,6 +277,18 @@ class Command(object):
def GetProjects(self, args, manifest=None, groups='', missing_ok=False,
submodules_ok=False, all_manifests=False):
"""A list of projects that match the arguments.
Args:
args: a list of (case-insensitive) strings, projects to search for.
manifest: an XmlManifest, the manifest to use, or None for default.
groups: a string, the manifest groups in use.
missing_ok: a boolean, whether to allow missing projects.
submodules_ok: a boolean, whether to allow submodules.
all_manifests: a boolean, if True then all manifests and submanifests are
used. If False, then only the local (sub)manifest is used.
Returns:
A list of matching Project instances.
"""
if all_manifests:
if not manifest:
@ -385,7 +400,7 @@ class Command(object):
opt: The command options.
"""
top = self.outer_manifest
if opt.outer_manifest is False or opt.this_manifest_only:
if not opt.outer_manifest or opt.this_manifest_only:
top = self.manifest
yield top
if not opt.this_manifest_only:

View File

@ -66,6 +66,7 @@ following DTD:
<!ATTLIST submanifest revision CDATA #IMPLIED>
<!ATTLIST submanifest path CDATA #IMPLIED>
<!ATTLIST submanifest groups CDATA #IMPLIED>
<!ATTLIST submanifest default-groups CDATA #IMPLIED>
<!ELEMENT project (annotation*,
project*,
@ -302,6 +303,9 @@ in the included submanifest belong. This appends and recurses, meaning
all projects in submanifests carry all parent submanifest groups.
Same syntax as the corresponding element of `project`.
Attribute `default-groups`: The list of manifest groups to sync if no
`--groups=` parameter was specified at init. When that list is empty, use this
list instead of "default" as the list of groups to sync.
### Element project

View File

@ -18,7 +18,7 @@ For more information on superproject, check out:
https://en.wikibooks.org/wiki/Git/Submodules_and_Superprojects
Examples:
superproject = Superproject()
superproject = Superproject(manifest, name, remote, revision)
UpdateProjectsResult = superproject.UpdateProjectsRevisionId(projects)
"""
@ -99,8 +99,8 @@ class Superproject(object):
self._work_git_name = git_name + _SUPERPROJECT_GIT_NAME
self._work_git = os.path.join(self._superproject_path, self._work_git_name)
# The following are command arguemnts, rather then superproject attributes,
# and where included here originally. They should eventually become
# The following are command arguemnts, rather than superproject attributes,
# and were included here originally. They should eventually become
# arguments that are passed down from the public methods, instead of being
# treated as attributes.
self._git_event_log = None
@ -238,8 +238,8 @@ class Superproject(object):
f'{self._manifest.manifestFile}')
return SyncResult(False, False)
print('NOTICE: --use-superproject is in beta; report any issues to the '
'address described in `repo version`', file=sys.stderr)
_PrintBetaNotice()
should_exit = True
if not self._remote_url:
self._LogWarning(f'superproject URL is not defined in manifest: '
@ -295,7 +295,8 @@ class Superproject(object):
if not os.path.exists(self._superproject_path):
self._LogWarning(f'missing superproject directory: {self._superproject_path}')
return None
manifest_str = self._manifest.ToXml(groups=self._manifest.GetGroupsStr()).toxml()
manifest_str = self._manifest.ToXml(groups=self._manifest.GetGroupsStr(),
omit_local=True).toxml()
manifest_path = self._manifest_path
try:
with open(manifest_path, 'w', encoding='utf-8') as fp:
@ -329,7 +330,8 @@ class Superproject(object):
"""Update revisionId of every project in projects with the commit id.
Args:
projects: List of projects whose revisionId needs to be updated.
projects: a list of projects whose revisionId needs to be updated.
git_event_log: an EventLog, for git tracing.
Returns:
UpdateProjectsResult
@ -364,6 +366,13 @@ class Superproject(object):
return UpdateProjectsResult(manifest_path, False)
@functools.lru_cache(maxsize=10)
def _PrintBetaNotice():
"""Print the notice of beta status."""
print('NOTICE: --use-superproject is in beta; report any issues to the '
'address described in `repo version`', file=sys.stderr)
@functools.lru_cache(maxsize=None)
def _UseSuperprojectFromConfiguration():
"""Returns the user choice of whether to use superproject."""
@ -408,16 +417,32 @@ def _UseSuperprojectFromConfiguration():
return False
def PrintMessages(opt, manifest):
"""Returns a boolean if error/warning messages are to be printed."""
return opt.use_superproject is not None or bool(manifest.superproject)
def PrintMessages(use_superproject, manifest):
"""Returns a boolean if error/warning messages are to be printed.
Args:
use_superproject: option value from optparse.
manifest: manifest to use.
"""
return use_superproject is not None or bool(manifest.superproject)
def UseSuperproject(opt, manifest):
"""Returns a boolean if use-superproject option is enabled."""
def UseSuperproject(use_superproject, manifest):
"""Returns a boolean if use-superproject option is enabled.
if opt.use_superproject is not None:
return opt.use_superproject
Args:
use_superproject: option value from optparse.
manifest: manifest to use.
Returns:
Whether the superproject should be used.
"""
if not manifest.superproject:
# This (sub) manifest does not have a superproject definition.
return False
elif use_superproject is not None:
return use_superproject
else:
client_value = manifest.manifestProject.use_superproject
if client_value is not None:

View File

@ -294,8 +294,7 @@ class _Repo(object):
cmd.ValidateOptions(copts, cargs)
this_manifest_only = copts.this_manifest_only
# If not specified, default to using the outer manifest.
outer_manifest = copts.outer_manifest is not False
outer_manifest = copts.outer_manifest
if cmd.MULTI_MANIFEST_SUPPORT or this_manifest_only:
result = cmd.Execute(copts, cargs)
elif outer_manifest and repo_client.manifest.is_submanifest:
@ -310,7 +309,7 @@ class _Repo(object):
# (sub)manifest, and then any child submanifests.
result = cmd.Execute(copts, cargs)
for submanifest in repo_client.manifest.submanifests.values():
spec = submanifest.ToSubmanifestSpec(root=repo_client.outer_client)
spec = submanifest.ToSubmanifestSpec()
gopts.submanifest_path = submanifest.repo_client.path_prefix
child_argv = argv[:]
child_argv.append('--no-outer-manifest')

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo abandon" "Repo Manual"
.TH REPO "1" "July 2022" "repo abandon" "Repo Manual"
.SH NAME
repo \- repo abandon - manual page for repo abandon
.SH SYNOPSIS
@ -32,5 +32,18 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help abandon` to view the detailed manual.

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo branches" "Repo Manual"
.TH REPO "1" "July 2022" "repo branches" "Repo Manual"
.SH NAME
repo \- repo branches - manual page for repo branches
.SH SYNOPSIS
@ -55,5 +55,18 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help branches` to view the detailed manual.

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo checkout" "Repo Manual"
.TH REPO "1" "July 2022" "repo checkout" "Repo Manual"
.SH NAME
repo \- repo checkout - manual page for repo checkout
.SH SYNOPSIS
@ -24,6 +24,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help checkout` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo cherry-pick" "Repo Manual"
.TH REPO "1" "July 2022" "repo cherry-pick" "Repo Manual"
.SH NAME
repo \- repo cherry-pick - manual page for repo cherry-pick
.SH SYNOPSIS
@ -20,6 +20,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help cherry\-pick` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo diff" "Repo Manual"
.TH REPO "1" "July 2022" "repo diff" "Repo Manual"
.SH NAME
repo \- repo diff - manual page for repo diff
.SH SYNOPSIS
@ -31,5 +31,18 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help diff` to view the detailed manual.

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo diffmanifests" "Repo Manual"
.TH REPO "1" "July 2022" "repo diffmanifests" "Repo Manual"
.SH NAME
repo \- repo diffmanifests - manual page for repo diffmanifests
.SH SYNOPSIS
@ -29,6 +29,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help diffmanifests` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo download" "Repo Manual"
.TH REPO "1" "July 2022" "repo download" "Repo Manual"
.SH NAME
repo \- repo download - manual page for repo download
.SH SYNOPSIS
@ -35,6 +35,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help download` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo forall" "Repo Manual"
.TH REPO "1" "July 2022" "repo forall" "Repo Manual"
.SH NAME
repo \- repo forall - manual page for repo forall
.SH SYNOPSIS
@ -54,6 +54,19 @@ only show errors
.TP
\fB\-p\fR
show project headers before output
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help forall` to view the detailed manual.
.SH DETAILS
@ -93,6 +106,11 @@ REPO_PROJECT is set to the unique name of the project.
.PP
REPO_PATH is the path relative the the root of the client.
.PP
REPO_OUTERPATH is the path of the sub manifest's root relative to the root of
the client.
.PP
REPO_INNERPATH is the path relative to the root of the sub manifest.
.PP
REPO_REMOTE is the name of the remote system from the manifest.
.PP
REPO_LREV is the name of the revision from the manifest, translated to a local

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo gitc-delete" "Repo Manual"
.TH REPO "1" "July 2022" "repo gitc-delete" "Repo Manual"
.SH NAME
repo \- repo gitc-delete - manual page for repo gitc-delete
.SH SYNOPSIS
@ -23,6 +23,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help gitc\-delete` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "November 2021" "repo gitc-init" "Repo Manual"
.TH REPO "1" "July 2022" "repo gitc-init" "Repo Manual"
.SH NAME
repo \- repo gitc-init - manual page for repo gitc-init
.SH SYNOPSIS
@ -109,6 +109,12 @@ not \fB\-\-partial\-clone\fR)
\fB\-\-no\-clone\-bundle\fR
disable use of \fI\,/clone.bundle\/\fP on HTTP/HTTPS (default if
\fB\-\-partial\-clone\fR)
.TP
\fB\-\-git\-lfs\fR
enable Git LFS support
.TP
\fB\-\-no\-git\-lfs\fR
disable Git LFS support
.SS repo Version options:
.TP
\fB\-\-repo\-url\fR=\fI\,URL\/\fR
@ -130,6 +136,19 @@ Optional manifest file to use for this GITC client.
.TP
\fB\-c\fR GITC_CLIENT, \fB\-\-gitc\-client\fR=\fI\,GITC_CLIENT\/\fR
Name of the gitc_client instance to create or modify.
.SS Multi\-manifest:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help gitc\-init` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo grep" "Repo Manual"
.TH REPO "1" "July 2022" "repo grep" "Repo Manual"
.SH NAME
repo \- repo grep - manual page for repo grep
.SH SYNOPSIS
@ -24,6 +24,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.SS Sources:
.TP
\fB\-\-cached\fR

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo help" "Repo Manual"
.TH REPO "1" "July 2022" "repo help" "Repo Manual"
.SH NAME
repo \- repo help - manual page for repo help
.SH SYNOPSIS
@ -26,6 +26,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help help` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo info" "Repo Manual"
.TH REPO "1" "July 2022" "repo info" "Repo Manual"
.SH NAME
repo \- repo info - manual page for repo info
.SH SYNOPSIS
@ -36,5 +36,18 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help info` to view the detailed manual.

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "November 2021" "repo init" "Repo Manual"
.TH REPO "1" "July 2022" "repo init" "Repo Manual"
.SH NAME
repo \- repo init - manual page for repo init
.SH SYNOPSIS
@ -109,6 +109,12 @@ not \fB\-\-partial\-clone\fR)
\fB\-\-no\-clone\-bundle\fR
disable use of \fI\,/clone.bundle\/\fP on HTTP/HTTPS (default if
\fB\-\-partial\-clone\fR)
.TP
\fB\-\-git\-lfs\fR
enable Git LFS support
.TP
\fB\-\-no\-git\-lfs\fR
disable Git LFS support
.SS repo Version options:
.TP
\fB\-\-repo\-url\fR=\fI\,URL\/\fR
@ -123,6 +129,19 @@ do not verify repo source code
.TP
\fB\-\-config\-name\fR
Always prompt for name/e\-mail
.SS Multi\-manifest:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help init` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo list" "Repo Manual"
.TH REPO "1" "July 2022" "repo list" "Repo Manual"
.SH NAME
repo \- repo list - manual page for repo list
.SH SYNOPSIS
@ -47,6 +47,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help list` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "November 2021" "repo manifest" "Repo Manual"
.TH REPO "1" "July 2022" "repo manifest" "Repo Manual"
.SH NAME
repo \- repo manifest - manual page for repo manifest
.SH SYNOPSIS
@ -40,7 +40,8 @@ format output for humans to read
ignore local manifests
.TP
\fB\-o\fR \-|NAME.xml, \fB\-\-output\-file\fR=\fI\,\-\/\fR|NAME.xml
file to save the manifest to
file to save the manifest to. (Filename prefix for
multi\-tree.)
.SS Logging options:
.TP
\fB\-v\fR, \fB\-\-verbose\fR
@ -48,6 +49,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help manifest` to view the detailed manual.
.SH DETAILS
@ -88,6 +102,7 @@ A manifest XML file (e.g. `default.xml`) roughly conforms to the following DTD:
remote*,
default?,
manifest\-server?,
submanifest*?,
remove\-project*,
project*,
extend\-project*,
@ -118,6 +133,16 @@ include*)>
.IP
<!ELEMENT manifest\-server EMPTY>
<!ATTLIST manifest\-server url CDATA #REQUIRED>
.IP
<!ELEMENT submanifest EMPTY>
<!ATTLIST submanifest name ID #REQUIRED>
<!ATTLIST submanifest remote IDREF #IMPLIED>
<!ATTLIST submanifest project CDATA #IMPLIED>
<!ATTLIST submanifest manifest\-name CDATA #IMPLIED>
<!ATTLIST submanifest revision CDATA #IMPLIED>
<!ATTLIST submanifest path CDATA #IMPLIED>
<!ATTLIST submanifest groups CDATA #IMPLIED>
<!ATTLIST submanifest default\-groups CDATA #IMPLIED>
.TP
<!ELEMENT project (annotation*,
project*,
@ -295,6 +320,65 @@ GetManifest(tag)
Return a manifest in which each project is pegged to the revision at the
specified tag. This is used by repo sync when the \fB\-\-smart\-tag\fR option is given.
.PP
Element submanifest
.PP
One or more submanifest elements may be specified. Each element describes a
single manifest to be checked out as a child.
.PP
Attribute `name`: A unique name (within the current (sub)manifest) for this
submanifest. It acts as a default for `revision` below. The same name can be
used for submanifests with different parent (sub)manifests.
.PP
Attribute `remote`: Name of a previously defined remote element. If not supplied
the remote given by the default element is used.
.PP
Attribute `project`: The manifest project name. The project's name is appended
onto its remote's fetch URL to generate the actual URL to configure the Git
remote with. The URL gets formed as:
.IP
${remote_fetch}/${project_name}.git
.PP
where ${remote_fetch} is the remote's fetch attribute and ${project_name} is the
project's name attribute. The suffix ".git" is always appended as repo assumes
the upstream is a forest of bare Git repositories. If the project has a parent
element, its name will be prefixed by the parent's.
.PP
The project name must match the name Gerrit knows, if Gerrit is being used for
code reviews.
.PP
`project` must not be empty, and may not be an absolute path or use "." or ".."
path components. It is always interpreted relative to the remote's fetch
settings, so if a different base path is needed, declare a different remote with
the new settings needed.
.PP
If not supplied the remote and project for this manifest will be used: `remote`
cannot be supplied.
.PP
Projects from a submanifest and its submanifests are added to the
submanifest::path:<path_prefix> group.
.PP
Attribute `manifest\-name`: The manifest filename in the manifest project. If not
supplied, `default.xml` is used.
.PP
Attribute `revision`: Name of a Git branch (e.g. "main" or "refs/heads/main"),
tag (e.g. "refs/tags/stable"), or a commit hash. If not supplied, `name` is
used.
.PP
Attribute `path`: An optional path relative to the top directory of the repo
client where the submanifest repo client top directory should be placed. If not
supplied, `revision` is used.
.PP
`path` may not be an absolute path or use "." or ".." path components.
.PP
Attribute `groups`: List of additional groups to which all projects in the
included submanifest belong. This appends and recurses, meaning all projects in
submanifests carry all parent submanifest groups. Same syntax as the
corresponding element of `project`.
.PP
Attribute `default\-groups`: The list of manifest groups to sync if no
`\-\-groups=` parameter was specified at init. When that list is empty, use this
list instead of "default" as the list of groups to sync.
.PP
Element project
.PP
One or more project elements may be specified. Each element describes a single
@ -513,8 +597,8 @@ restrictions are not enforced for [Local Manifests].
.PP
Attribute `groups`: List of additional groups to which all projects in the
included manifest belong. This appends and recurses, meaning all projects in
sub\-manifests carry all parent include groups. Same syntax as the corresponding
element of `project`.
included manifests carry all parent include groups. Same syntax as the
corresponding element of `project`.
.PP
Local Manifests
.PP

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo overview" "Repo Manual"
.TH REPO "1" "July 2022" "repo overview" "Repo Manual"
.SH NAME
repo \- repo overview - manual page for repo overview
.SH SYNOPSIS
@ -26,6 +26,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help overview` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo prune" "Repo Manual"
.TH REPO "1" "July 2022" "repo prune" "Repo Manual"
.SH NAME
repo \- repo prune - manual page for repo prune
.SH SYNOPSIS
@ -24,5 +24,18 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help prune` to view the detailed manual.

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo rebase" "Repo Manual"
.TH REPO "1" "July 2022" "repo rebase" "Repo Manual"
.SH NAME
repo \- repo rebase - manual page for repo rebase
.SH SYNOPSIS
@ -46,6 +46,19 @@ only show errors
.TP
\fB\-i\fR, \fB\-\-interactive\fR
interactive rebase (single project only)
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help rebase` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo selfupdate" "Repo Manual"
.TH REPO "1" "July 2022" "repo selfupdate" "Repo Manual"
.SH NAME
repo \- repo selfupdate - manual page for repo selfupdate
.SH SYNOPSIS
@ -20,6 +20,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.SS repo Version options:
.TP
\fB\-\-no\-repo\-verify\fR

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "November 2021" "repo smartsync" "Repo Manual"
.TH REPO "1" "July 2022" "repo smartsync" "Repo Manual"
.SH NAME
repo \- repo smartsync - manual page for repo smartsync
.SH SYNOPSIS
@ -112,6 +112,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.SS repo Version options:
.TP
\fB\-\-no\-repo\-verify\fR

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo stage" "Repo Manual"
.TH REPO "1" "July 2022" "repo stage" "Repo Manual"
.SH NAME
repo \- repo stage - manual page for repo stage
.SH SYNOPSIS
@ -23,6 +23,19 @@ only show errors
.TP
\fB\-i\fR, \fB\-\-interactive\fR
use interactive staging
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help stage` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo start" "Repo Manual"
.TH REPO "1" "July 2022" "repo start" "Repo Manual"
.SH NAME
repo \- repo start - manual page for repo start
.SH SYNOPSIS
@ -33,6 +33,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help start` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo status" "Repo Manual"
.TH REPO "1" "July 2022" "repo status" "Repo Manual"
.SH NAME
repo \- repo status - manual page for repo status
.SH SYNOPSIS
@ -28,6 +28,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help status` to view the detailed manual.
.SH DETAILS

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "November 2021" "repo sync" "Repo Manual"
.TH REPO "1" "July 2022" "repo sync" "Repo Manual"
.SH NAME
repo \- repo sync - manual page for repo sync
.SH SYNOPSIS
@ -119,6 +119,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.SS repo Version options:
.TP
\fB\-\-no\-repo\-verify\fR

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo upload" "Repo Manual"
.TH REPO "1" "July 2022" "repo upload" "Repo Manual"
.SH NAME
repo \- repo upload - manual page for repo upload
.SH SYNOPSIS
@ -75,6 +75,19 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.SS pre\-upload hooks:
.TP
\fB\-\-no\-verify\fR

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "July 2021" "repo version" "Repo Manual"
.TH REPO "1" "July 2022" "repo version" "Repo Manual"
.SH NAME
repo \- repo version - manual page for repo version
.SH SYNOPSIS
@ -20,5 +20,18 @@ show all output
.TP
\fB\-q\fR, \fB\-\-quiet\fR
only show errors
.SS Multi\-manifest options:
.TP
\fB\-\-outer\-manifest\fR
operate starting at the outermost manifest
.TP
\fB\-\-no\-outer\-manifest\fR
do not operate on outer manifests
.TP
\fB\-\-this\-manifest\-only\fR
only operate on this (sub)manifest
.TP
\fB\-\-no\-this\-manifest\-only\fR, \fB\-\-all\-manifests\fR
operate on this manifest and its submanifests
.PP
Run `repo help version` to view the detailed manual.

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH REPO "1" "November 2021" "repo" "Repo Manual"
.TH REPO "1" "July 2022" "repo" "Repo Manual"
.SH NAME
repo \- repository management tool built on top of git
.SH SYNOPSIS
@ -43,6 +43,9 @@ filename of event log to append timeline to
.TP
\fB\-\-git\-trace2\-event\-log\fR=\fI\,GIT_TRACE2_EVENT_LOG\/\fR
directory to write git trace2 event log to
.TP
\fB\-\-submanifest\-path\fR=\fI\,REL_PATH\/\fR
submanifest path
.SS "The complete list of recognized repo commands is:"
.TP
abandon

View File

@ -214,9 +214,11 @@ class _XmlSubmanifest:
revision: a string, the commitish.
manifestName: a string, the submanifest file name.
groups: a list of strings, the groups to add to all projects in the submanifest.
default_groups: a list of strings, the default groups to sync.
path: a string, the relative path for the submanifest checkout.
parent: an XmlManifest, the parent manifest.
annotations: (derived) a list of annotations.
present: (derived) a boolean, whether the submanifest's manifest file is present.
present: (derived) a boolean, whether the sub manifest file is present.
"""
def __init__(self,
name,
@ -225,6 +227,7 @@ class _XmlSubmanifest:
revision=None,
manifestName=None,
groups=None,
default_groups=None,
path=None,
parent=None):
self.name = name
@ -233,7 +236,9 @@ class _XmlSubmanifest:
self.revision = revision
self.manifestName = manifestName
self.groups = groups
self.default_groups = default_groups
self.path = path
self.parent = parent
self.annotations = []
outer_client = parent._outer_client or parent
if self.remote and not self.project:
@ -248,7 +253,8 @@ class _XmlSubmanifest:
os.path.join(parent.path_prefix, self.relpath), MANIFEST_FILE_NAME)
rc = self.repo_client = RepoClient(
parent.repodir, linkFile, parent_groups=','.join(groups) or '',
submanifest_path=self.relpath, outer_client=outer_client)
submanifest_path=self.relpath, outer_client=outer_client,
default_groups=default_groups)
self.present = os.path.exists(manifestFile)
@ -262,16 +268,17 @@ class _XmlSubmanifest:
self.revision == other.revision and
self.manifestName == other.manifestName and
self.groups == other.groups and
self.default_groups == other.default_groups and
self.path == other.path and
sorted(self.annotations) == sorted(other.annotations))
def __ne__(self, other):
return not self.__eq__(other)
def ToSubmanifestSpec(self, root):
def ToSubmanifestSpec(self):
"""Return a SubmanifestSpec object, populating attributes"""
mp = root.manifestProject
remote = root.remotes[self.remote or root.default.remote.name]
mp = self.parent.manifestProject
remote = self.parent.remotes[self.remote or self.parent.default.remote.name]
# If a project was given, generate the url from the remote and project.
# If not, use this manifestProject's url.
if self.project:
@ -282,6 +289,7 @@ class _XmlSubmanifest:
revision = self.revision or self.name
path = self.path or revision.split('/')[-1]
groups = self.groups or []
default_groups = self.default_groups or []
return SubmanifestSpec(self.name, manifestUrl, manifestName, revision, path,
groups)
@ -298,6 +306,10 @@ class _XmlSubmanifest:
return ','.join(self.groups)
return ''
def GetDefaultGroupsStr(self):
"""Returns the `default-groups` given for this submanifest."""
return ','.join(self.default_groups or [])
def AddAnnotation(self, name, value, keep):
"""Add annotations to the submanifest."""
self.annotations.append(Annotation(name, value, keep))
@ -325,7 +337,8 @@ class XmlManifest(object):
"""manages the repo configuration file"""
def __init__(self, repodir, manifest_file, local_manifests=None,
outer_client=None, parent_groups='', submanifest_path=''):
outer_client=None, parent_groups='', submanifest_path='',
default_groups=None):
"""Initialize.
Args:
@ -335,9 +348,10 @@ class XmlManifest(object):
be |repodir|/|MANIFEST_FILE_NAME|.
local_manifests: Full path to the directory of local override manifests.
This will usually be |repodir|/|LOCAL_MANIFESTS_DIR_NAME|.
outer_client: RepoClient of the outertree.
outer_client: RepoClient of the outer manifest.
parent_groups: a string, the groups to apply to this projects.
submanifest_path: The submanifest root relative to the repo root.
default_groups: a string, the default manifest groups to use.
"""
# TODO(vapier): Move this out of this class.
self.globalConfig = GitConfig.ForUser()
@ -348,9 +362,15 @@ class XmlManifest(object):
if manifest_file != os.path.abspath(manifest_file):
raise ManifestParseError('manifest_file must be abspath')
self.manifestFile = manifest_file
if not outer_client or outer_client == self:
# manifestFileOverrides only exists in the outer_client's manifest, since
# that is the only instance left when Unload() is called on the outer
# manifest.
self.manifestFileOverrides = {}
self.local_manifests = local_manifests
self._load_local_manifests = True
self.parent_groups = parent_groups
self.default_groups = default_groups
if outer_client and self.isGitcClient:
raise ManifestParseError('Multi-manifest is incompatible with `gitc-init`')
@ -396,14 +416,10 @@ class XmlManifest(object):
if not os.path.isfile(path):
raise ManifestParseError('manifest %s not found' % name)
old = self.manifestFile
try:
self._load_local_manifests = load_local_manifests
self.manifestFile = path
self.Unload()
self._Load()
finally:
self.manifestFile = old
self._load_local_manifests = load_local_manifests
self._outer_client.manifestFileOverrides[self.path_prefix] = path
self.Unload()
self._Load()
def Link(self, name):
"""Update the repo metadata to use a different manifest.
@ -469,6 +485,8 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
e.setAttribute('path', r.path)
if r.groups:
e.setAttribute('groups', r.GetGroupsStr())
if r.default_groups:
e.setAttribute('default-groups', r.GetDefaultGroupsStr())
for a in r.annotations:
if a.keep == 'true':
@ -484,7 +502,8 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
"""
return [x for x in re.split(r'[,\s]+', field) if x]
def ToXml(self, peg_rev=False, peg_rev_upstream=True, peg_rev_dest_branch=True, groups=None):
def ToXml(self, peg_rev=False, peg_rev_upstream=True,
peg_rev_dest_branch=True, groups=None, omit_local=False):
"""Return the current manifest XML."""
mp = self.manifestProject
@ -565,6 +584,9 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
if not p.MatchesGroups(groups):
return
if omit_local and self.IsFromLocalManifest(p):
return
name = p.name
relpath = p.relpath
if parent:
@ -750,23 +772,29 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
@property
def is_multimanifest(self):
"""Whether this is a multimanifest checkout"""
return bool(self.outer_client.submanifests)
"""Whether this is a multimanifest checkout.
This is safe to use as long as the outermost manifest XML has been parsed.
"""
return bool(self._outer_client._submanifests)
@property
def is_submanifest(self):
"""Whether this manifest is a submanifest"""
"""Whether this manifest is a submanifest.
This is safe to use as long as the outermost manifest XML has been parsed.
"""
return self._outer_client and self._outer_client != self
@property
def outer_client(self):
"""The instance of the outermost manifest client"""
"""The instance of the outermost manifest client."""
self._Load()
return self._outer_client
@property
def all_manifests(self):
"""Generator yielding all (sub)manifests."""
"""Generator yielding all (sub)manifests, in depth-first order."""
self._Load()
outer = self._outer_client
yield outer
@ -775,7 +803,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
@property
def all_children(self):
"""Generator yielding all child submanifests."""
"""Generator yielding all (present) child submanifests."""
self._Load()
for child in self._submanifests.values():
if child.repo_client:
@ -792,7 +820,14 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
@property
def all_paths(self):
"""All project paths for all (sub)manifests. See `paths`."""
"""All project paths for all (sub)manifests.
See also `paths`.
Returns:
A dictionary of {path: Project()}. `path` is relative to the outer
manifest.
"""
ret = {}
for tree in self.all_manifests:
prefix = tree.path_prefix
@ -808,7 +843,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
def paths(self):
"""Return all paths for this manifest.
Return:
Returns:
A dictionary of {path: Project()}. `path` is relative to this manifest.
"""
self._Load()
@ -822,11 +857,13 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
@property
def remotes(self):
"""Return a list of remotes for this manifest."""
self._Load()
return self._remotes
@property
def default(self):
"""Return default values for this manifest."""
self._Load()
return self._default
@ -880,6 +917,10 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
exclude = self.manifest.manifestProject.partial_clone_exclude or ''
return set(x.strip() for x in exclude.split(','))
def SetManifestOverride(self, path):
"""Override manifestFile. The caller must call Unload()"""
self._outer_client.manifest.manifestFileOverrides[self.path_prefix] = path
@property
def UseLocalManifests(self):
return self._load_local_manifests
@ -960,16 +1001,21 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
worktree=os.path.join(subdir, 'manifests'))
return mp
def GetDefaultGroupsStr(self):
"""Returns the default group string for the platform."""
return 'default,platform-' + platform.system().lower()
def GetDefaultGroupsStr(self, with_platform=True):
"""Returns the default group string to use.
Args:
with_platform: a boolean, whether to include the group for the
underlying platform.
"""
groups = ','.join(self.default_groups or ['default'])
if with_platform:
groups += f',platform-{platform.system().lower()}'
return groups
def GetGroupsStr(self):
"""Returns the manifest group string that should be synced."""
groups = self.manifestProject.manifest_groups
if not groups:
groups = self.GetDefaultGroupsStr()
return groups
return self.manifestProject.manifest_groups or self.GetDefaultGroupsStr()
def Unload(self):
"""Unload the manifest.
@ -1005,57 +1051,66 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
# This will load all clients.
self._outer_client._Load(initial_client=self)
m = self.manifestProject
b = m.GetBranch(m.CurrentBranch).merge
if b is not None and b.startswith(R_HEADS):
b = b[len(R_HEADS):]
self.branch = b
parent_groups = self.parent_groups
if self.path_prefix:
parent_groups = f'{SUBMANIFEST_GROUP_PREFIX}:path:{self.path_prefix},{parent_groups}'
# The manifestFile was specified by the user which is why we allow include
# paths to point anywhere.
nodes = []
nodes.append(self._ParseManifestXml(
self.manifestFile, self.manifestProject.worktree,
parent_groups=parent_groups, restrict_includes=False))
if self._load_local_manifests and self.local_manifests:
try:
for local_file in sorted(platform_utils.listdir(self.local_manifests)):
if local_file.endswith('.xml'):
local = os.path.join(self.local_manifests, local_file)
# Since local manifests are entirely managed by the user, allow
# them to point anywhere the user wants.
local_group = f'{LOCAL_MANIFEST_GROUP_PREFIX}:{local_file[:-4]}'
nodes.append(self._ParseManifestXml(
local, self.subdir,
parent_groups=f'{local_group},{parent_groups}',
restrict_includes=False))
except OSError:
pass
savedManifestFile = self.manifestFile
override = self._outer_client.manifestFileOverrides.get(self.path_prefix)
if override:
self.manifestFile = override
try:
self._ParseManifest(nodes)
except ManifestParseError as e:
# There was a problem parsing, unload ourselves in case they catch
# this error and try again later, we will show the correct error
self.Unload()
raise e
m = self.manifestProject
b = m.GetBranch(m.CurrentBranch).merge
if b is not None and b.startswith(R_HEADS):
b = b[len(R_HEADS):]
self.branch = b
if self.IsMirror:
self._AddMetaProjectMirror(self.repoProject)
self._AddMetaProjectMirror(self.manifestProject)
parent_groups = self.parent_groups
if self.path_prefix:
parent_groups = f'{SUBMANIFEST_GROUP_PREFIX}:path:{self.path_prefix},{parent_groups}'
self._loaded = True
# The manifestFile was specified by the user which is why we allow include
# paths to point anywhere.
nodes = []
nodes.append(self._ParseManifestXml(
self.manifestFile, self.manifestProject.worktree,
parent_groups=parent_groups, restrict_includes=False))
# Now that we have loaded this manifest, load any submanifest manifests
# as well. We need to do this after self._loaded is set to avoid looping.
if self._load_local_manifests and self.local_manifests:
try:
for local_file in sorted(platform_utils.listdir(self.local_manifests)):
if local_file.endswith('.xml'):
local = os.path.join(self.local_manifests, local_file)
# Since local manifests are entirely managed by the user, allow
# them to point anywhere the user wants.
local_group = f'{LOCAL_MANIFEST_GROUP_PREFIX}:{local_file[:-4]}'
nodes.append(self._ParseManifestXml(
local, self.subdir,
parent_groups=f'{local_group},{parent_groups}',
restrict_includes=False))
except OSError:
pass
try:
self._ParseManifest(nodes)
except ManifestParseError as e:
# There was a problem parsing, unload ourselves in case they catch
# this error and try again later, we will show the correct error
self.Unload()
raise e
if self.IsMirror:
self._AddMetaProjectMirror(self.repoProject)
self._AddMetaProjectMirror(self.manifestProject)
self._loaded = True
finally:
if override:
self.manifestFile = savedManifestFile
# Now that we have loaded this manifest, load any submanifests as well.
# We need to do this after self._loaded is set to avoid looping.
for name in self._submanifests:
tree = self._submanifests[name]
spec = tree.ToSubmanifestSpec(self)
spec = tree.ToSubmanifestSpec()
present = os.path.exists(os.path.join(self.subdir, MANIFEST_FILE_NAME))
if present and tree.present and not tree.repo_client:
if initial_client and initial_client.topdir == self.topdir:
@ -1475,6 +1530,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
if node.hasAttribute('groups'):
groups = node.getAttribute('groups')
groups = self._ParseList(groups)
default_groups = self._ParseList(node.getAttribute('default-groups'))
path = node.getAttribute('path')
if path == '':
path = None
@ -1495,7 +1551,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
'<submanifest> invalid "path": %s: %s' % (path, msg))
submanifest = _XmlSubmanifest(name, remote, project, revision, manifestName,
groups, path, self)
groups, default_groups, path, self)
for n in node.childNodes:
if n.nodeName == 'annotation':
@ -1619,6 +1675,10 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
name: a string, the name of the project.
path: a string, the path of the project.
remote: a string, the remote.name of the project.
Returns:
A tuple of (relpath, worktree, gitdir, objdir, use_git_worktrees) for the
project with |name| and |path|.
"""
# The manifest entries might have trailing slashes. Normalize them to avoid
# unexpected filesystem behavior since we do string concatenation below.
@ -1626,7 +1686,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
name = name.rstrip('/')
remote = remote.rstrip('/')
use_git_worktrees = False
use_remote_name = bool(self._outer_client._submanifests)
use_remote_name = self.is_multimanifest
relpath = path
if self.IsMirror:
worktree = None
@ -1642,7 +1702,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
# We allow people to mix git worktrees & non-git worktrees for now.
# This allows for in situ migration of repo clients.
if os.path.exists(gitdir) or not self.UseGitWorktrees:
objdir = os.path.join(self.subdir, 'project-objects', namepath)
objdir = os.path.join(self.repodir, 'project-objects', namepath)
else:
use_git_worktrees = True
gitdir = os.path.join(self.repodir, 'worktrees', namepath)
@ -1656,6 +1716,9 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
name: a string, the name of the project.
all_manifests: a boolean, if True, then all manifests are searched. If
False, then only this manifest is searched.
Returns:
A list of Project instances with name |name|.
"""
if all_manifests:
return list(itertools.chain.from_iterable(
@ -1916,6 +1979,16 @@ class RepoClient(XmlManifest):
"""Manages a repo client checkout."""
def __init__(self, repodir, manifest_file=None, submanifest_path='', **kwargs):
"""Initialize.
Args:
repodir: Path to the .repo/ dir for holding all internal checkout state.
It must be in the top directory of the repo client checkout.
manifest_file: Full path to the manifest file to parse. This will usually
be |repodir|/|MANIFEST_FILE_NAME|.
submanifest_path: The submanifest root relative to the repo root.
**kwargs: Additional keyword arguments, passed to XmlManifest.
"""
self.isGitcClient = False
submanifest_path = submanifest_path or ''
if submanifest_path:

View File

@ -24,6 +24,11 @@ _NOT_TTY = not os.isatty(2)
# column 0.
CSI_ERASE_LINE = '\x1b[2K'
# This will erase all content in the current line after the cursor. This is
# useful for partial updates & progress messages as the terminal can display
# it better.
CSI_ERASE_LINE_AFTER = '\x1b[K'
def duration_str(total):
"""A less noisy timedelta.__str__.
@ -85,10 +90,10 @@ class Progress(object):
return
if self._total <= 0:
sys.stderr.write('%s\r%s: %d,' % (
CSI_ERASE_LINE,
sys.stderr.write('\r%s: %d,%s' % (
self._title,
self._done))
self._done,
CSI_ERASE_LINE_AFTER))
sys.stderr.flush()
else:
p = (100 * self._done) / self._total
@ -96,14 +101,14 @@ class Progress(object):
jobs = '[%d job%s] ' % (self._active, 's' if self._active > 1 else '')
else:
jobs = ''
sys.stderr.write('%s\r%s: %2d%% %s(%d%s/%d%s)%s%s%s' % (
CSI_ERASE_LINE,
sys.stderr.write('\r%s: %2d%% %s(%d%s/%d%s)%s%s%s%s' % (
self._title,
p,
jobs,
self._done, self._units,
self._total, self._units,
' ' if msg else '', msg,
CSI_ERASE_LINE_AFTER,
'\n' if self._print_newline else ''))
sys.stderr.flush()
@ -113,19 +118,19 @@ class Progress(object):
duration = duration_str(time() - self._start)
if self._total <= 0:
sys.stderr.write('%s\r%s: %d, done in %s\n' % (
CSI_ERASE_LINE,
sys.stderr.write('\r%s: %d, done in %s%s\n' % (
self._title,
self._done,
duration))
duration,
CSI_ERASE_LINE_AFTER))
sys.stderr.flush()
else:
p = (100 * self._done) / self._total
sys.stderr.write('%s\r%s: %3d%% (%d%s/%d%s), done in %s\n' % (
CSI_ERASE_LINE,
sys.stderr.write('\r%s: %3d%% (%d%s/%d%s), done in %s%s\n' % (
self._title,
p,
self._done, self._units,
self._total, self._units,
duration))
duration,
CSI_ERASE_LINE_AFTER))
sys.stderr.flush()

View File

@ -29,9 +29,11 @@ import time
import urllib.parse
from color import Coloring
import fetch
from git_command import GitCommand, git_require
from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
ID_RE
import git_superproject
from git_trace2_event_log import EventLog
from error import GitError, UploadError, DownloadError
from error import ManifestInvalidRevisionError, ManifestInvalidPathError
@ -48,6 +50,9 @@ MAXIMUM_RETRY_SLEEP_SEC = 3600.0
# +-10% random jitter is added to each Fetches retry sleep duration.
RETRY_JITTER_PERCENT = 0.1
# Whether to use alternates.
# TODO(vapier): Remove knob once behavior is verified.
_ALTERNATES = os.environ.get('REPO_USE_ALTERNATES') == '1'
def _lwrite(path, content):
lock = '%s.lock' % path
@ -459,7 +464,13 @@ class RemoteSpec(object):
class Project(object):
# These objects can be shared between several working trees.
shareable_dirs = ['hooks', 'objects', 'rr-cache']
@property
def shareable_dirs(self):
"""Return the shareable directories"""
if self.UseAlternates:
return ['hooks', 'rr-cache']
else:
return ['hooks', 'objects', 'rr-cache']
def __init__(self,
manifest,
@ -589,6 +600,14 @@ class Project(object):
self.bare_ref = GitRefs(self.gitdir)
self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=self.objdir)
@property
def UseAlternates(self):
"""Whether git alternates are in use.
This will be removed once migration to alternates is complete.
"""
return _ALTERNATES or self.manifest.is_multimanifest
@property
def Derived(self):
return self.is_derived
@ -631,7 +650,7 @@ class Project(object):
return True
if self.work_git.DiffZ('diff-files'):
return True
if consider_untracked and self.work_git.LsOthers():
if consider_untracked and self.UntrackedFiles():
return True
return False
@ -714,7 +733,8 @@ class Project(object):
The special manifest group "default" will match any project that
does not have the special project group "notdefault"
"""
expanded_manifest_groups = manifest_groups or ['default']
default_groups = self.manifest.default_groups or ['default']
expanded_manifest_groups = manifest_groups or default_groups
expanded_project_groups = ['all'] + (self.groups or [])
if 'notdefault' not in expanded_project_groups:
expanded_project_groups += ['default']
@ -759,33 +779,37 @@ class Project(object):
if not get_all:
return details
changes = self.work_git.LsOthers()
changes = self.UntrackedFiles()
if changes:
details.extend(changes)
return details
def UntrackedFiles(self):
"""Returns a list of strings, untracked files in the git tree."""
return self.work_git.LsOthers()
def HasChanges(self):
"""Returns true if there are uncommitted changes.
"""
if self.UncommitedFiles(get_all=False):
return True
else:
return False
return bool(self.UncommitedFiles(get_all=False))
def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
def PrintWorkTreeStatus(self, output_redir=None, quiet=False, local=False):
"""Prints the status of the repository to stdout.
Args:
output_redir: If specified, redirect the output to this object.
quiet: If True then only print the project name. Do not print
the modified files, branch name, etc.
local: a boolean, if True, the path is relative to the local
(sub)manifest. If false, the path is relative to the
outermost manifest.
"""
if not platform_utils.isdir(self.worktree):
if output_redir is None:
output_redir = sys.stdout
print(file=output_redir)
print('project %s/' % self.relpath, file=output_redir)
print('project %s/' % self.RelPath(local), file=output_redir)
print(' missing (run "repo sync")', file=output_redir)
return
@ -803,7 +827,7 @@ class Project(object):
out = StatusColoring(self.config)
if output_redir is not None:
out.redirect(output_redir)
out.project('project %-40s', self.relpath + '/ ')
out.project('project %-40s', self.RelPath(local) + '/ ')
if quiet:
out.nl()
@ -864,7 +888,8 @@ class Project(object):
return 'DIRTY'
def PrintWorkTreeDiff(self, absolute_paths=False, output_redir=None):
def PrintWorkTreeDiff(self, absolute_paths=False, output_redir=None,
local=False):
"""Prints the status of the repository to stdout.
"""
out = DiffColoring(self.config)
@ -875,8 +900,8 @@ class Project(object):
cmd.append('--color')
cmd.append(HEAD)
if absolute_paths:
cmd.append('--src-prefix=a/%s/' % self.relpath)
cmd.append('--dst-prefix=b/%s/' % self.relpath)
cmd.append('--src-prefix=a/%s/' % self.RelPath(local))
cmd.append('--dst-prefix=b/%s/' % self.RelPath(local))
cmd.append('--')
try:
p = GitCommand(self,
@ -886,14 +911,14 @@ class Project(object):
p.Wait()
except GitError as e:
out.nl()
out.project('project %s/' % self.relpath)
out.project('project %s/' % self.RelPath(local))
out.nl()
out.fail('%s', str(e))
out.nl()
return False
if p.stdout:
out.nl()
out.project('project %s/' % self.relpath)
out.project('project %s/' % self.RelPath(local))
out.nl()
out.write('%s', p.stdout)
return p.Wait() == 0
@ -994,6 +1019,13 @@ class Project(object):
if not branch.remote.review:
raise GitError('remote %s has no review url' % branch.remote.name)
# Basic validity check on label syntax.
for label in labels:
if not re.match(r'^.+[+-][0-9]+$', label):
raise UploadError(
f'invalid label syntax "{label}": labels use forms like '
'CodeReview+1 or Verified-1')
if dest_branch is None:
dest_branch = self.dest_branch
if dest_branch is None:
@ -1029,6 +1061,7 @@ class Project(object):
if auto_topic:
opts += ['topic=' + branch.name]
opts += ['t=%s' % p for p in hashtags]
# NB: No need to encode labels as they've been validated above.
opts += ['l=%s' % p for p in labels]
opts += ['r=%s' % p for p in people[0]]
@ -1133,6 +1166,17 @@ class Project(object):
self._UpdateHooks(quiet=quiet)
self._InitRemote()
if self.UseAlternates:
# If gitdir/objects is a symlink, migrate it from the old layout.
gitdir_objects = os.path.join(self.gitdir, 'objects')
if platform_utils.islink(gitdir_objects):
platform_utils.remove(gitdir_objects, missing_ok=True)
gitdir_alt = os.path.join(self.gitdir, 'objects/info/alternates')
if not os.path.exists(gitdir_alt):
os.makedirs(os.path.dirname(gitdir_alt), exist_ok=True)
_lwrite(gitdir_alt, os.path.join(
os.path.relpath(self.objdir, gitdir_objects), 'objects') + '\n')
if is_new:
alt = os.path.join(self.objdir, 'objects/info/alternates')
try:
@ -1513,14 +1557,14 @@ class Project(object):
if self.IsDirty():
if force:
print('warning: %s: Removing dirty project: uncommitted changes lost.' %
(self.relpath,), file=sys.stderr)
(self.RelPath(local=False),), file=sys.stderr)
else:
print('error: %s: Cannot remove project: uncommitted changes are '
'present.\n' % (self.relpath,), file=sys.stderr)
'present.\n' % (self.RelPath(local=False),), file=sys.stderr)
return False
if not quiet:
print('%s: Deleting obsolete checkout.' % (self.relpath,))
print('%s: Deleting obsolete checkout.' % (self.RelPath(local=False),))
# Unlock and delink from the main worktree. We don't use git's worktree
# remove because it will recursively delete projects -- we handle that
@ -1559,7 +1603,8 @@ class Project(object):
if e.errno != errno.ENOENT:
print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
print('error: %s: Failed to delete obsolete checkout; remove manually, '
'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
'then run `repo sync -l`.' % (self.RelPath(local=False),),
file=sys.stderr)
return False
# Delete everything under the worktree, except for directories that contain
@ -1595,7 +1640,7 @@ class Project(object):
print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
failed = True
if failed:
print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
print('error: %s: Failed to delete obsolete checkout.' % (self.RelPath(local=False),),
file=sys.stderr)
print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
return False
@ -2010,7 +2055,7 @@ class Project(object):
def _FetchArchive(self, tarpath, cwd=None):
cmd = ['archive', '-v', '-o', tarpath]
cmd.append('--remote=%s' % self.remote.url)
cmd.append('--prefix=%s/' % self.relpath)
cmd.append('--prefix=%s/' % self.RelPath(local=False))
cmd.append(self.revisionExpr)
command = GitCommand(self, cmd, cwd=cwd,
@ -2156,6 +2201,8 @@ class Project(object):
if prune:
cmd.append('--prune')
# Always pass something for --recurse-submodules, git with GIT_DIR behaves
# incorrectly when not given `--recurse-submodules=no`. (b/218891912)
cmd.append(f'--recurse-submodules={"on-demand" if submodules else "no"}')
spec = []
@ -2592,7 +2639,7 @@ class Project(object):
if not filecmp.cmp(stock_hook, dst, shallow=False):
if not quiet:
_warn("%s: Not replacing locally modified %s hook",
self.relpath, name)
self.RelPath(local=False), name)
continue
try:
platform_utils.symlink(
@ -2687,7 +2734,7 @@ class Project(object):
'work tree. If you\'re comfortable with the '
'possibility of losing the work tree\'s git metadata,'
' use `repo sync --force-sync {0}` to '
'proceed.'.format(self.relpath))
'proceed.'.format(self.RelPath(local=False)))
def _ReferenceGitDir(self, gitdir, dotgit, copy_all):
"""Update |dotgit| to reference |gitdir|, using symlinks where possible.
@ -3167,7 +3214,7 @@ class _InfoMessage(object):
self.text = text
def Print(self, syncbuf):
syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
syncbuf.out.info('%s/: %s', self.project.RelPath(local=False), self.text)
syncbuf.out.nl()
@ -3179,7 +3226,7 @@ class _Failure(object):
def Print(self, syncbuf):
syncbuf.out.fail('error: %s/: %s',
self.project.relpath,
self.project.RelPath(local=False),
str(self.why))
syncbuf.out.nl()
@ -3192,7 +3239,7 @@ class _Later(object):
def Run(self, syncbuf):
out = syncbuf.out
out.project('project %s/', self.project.relpath)
out.project('project %s/', self.project.RelPath(local=False))
out.nl()
try:
self.action()
@ -3371,73 +3418,139 @@ class ManifestProject(MetaProject):
@property
def reference(self):
"""The --reference for this manifest."""
self.config.GetString('repo.reference')
return self.config.GetString('repo.reference')
@property
def dissociate(self):
"""Whether to dissociate."""
self.config.GetBoolean('repo.dissociate')
return self.config.GetBoolean('repo.dissociate')
@property
def archive(self):
"""Whether we use archive."""
self.config.GetBoolean('repo.archive')
return self.config.GetBoolean('repo.archive')
@property
def mirror(self):
"""Whether we use mirror."""
self.config.GetBoolean('repo.mirror')
return self.config.GetBoolean('repo.mirror')
@property
def use_worktree(self):
"""Whether we use worktree."""
self.config.GetBoolean('repo.worktree')
return self.config.GetBoolean('repo.worktree')
@property
def clone_bundle(self):
"""Whether we use clone_bundle."""
self.config.GetBoolean('repo.clonebundle')
return self.config.GetBoolean('repo.clonebundle')
@property
def submodules(self):
"""Whether we use submodules."""
self.config.GetBoolean('repo.submodules')
return self.config.GetBoolean('repo.submodules')
@property
def git_lfs(self):
"""Whether we use git_lfs."""
self.config.GetBoolean('repo.git-lfs')
return self.config.GetBoolean('repo.git-lfs')
@property
def use_superproject(self):
"""Whether we use superproject."""
self.config.GetBoolean('repo.superproject')
return self.config.GetBoolean('repo.superproject')
@property
def partial_clone(self):
"""Whether this is a partial clone."""
self.config.GetBoolean('repo.partialclone')
return self.config.GetBoolean('repo.partialclone')
@property
def depth(self):
"""Partial clone depth."""
self.config.GetString('repo.depth')
return self.config.GetString('repo.depth')
@property
def clone_filter(self):
"""The clone filter."""
self.config.GetString('repo.clonefilter')
return self.config.GetString('repo.clonefilter')
@property
def partial_clone_exclude(self):
"""Partial clone exclude string"""
self.config.GetBoolean('repo.partialcloneexclude')
return self.config.GetBoolean('repo.partialcloneexclude')
@property
def manifest_platform(self):
"""The --platform argument from `repo init`."""
return self.config.GetString('manifest.platform')
@property
def _platform_name(self):
"""Return the name of the platform."""
return platform.system().lower()
def SyncWithPossibleInit(self, submanifest, verbose=False,
current_branch_only=False, tags='', git_event_log=None):
"""Sync a manifestProject, possibly for the first time.
Call Sync() with arguments from the most recent `repo init`. If this is a
new sub manifest, then inherit options from the parent's manifestProject.
This is used by subcmds.Sync() to do an initial download of new sub
manifests.
Args:
submanifest: an XmlSubmanifest, the submanifest to re-sync.
verbose: a boolean, whether to show all output, rather than only errors.
current_branch_only: a boolean, whether to only fetch the current manifest
branch from the server.
tags: a boolean, whether to fetch tags.
git_event_log: an EventLog, for git tracing.
"""
# TODO(lamontjones): when refactoring sync (and init?) consider how to
# better get the init options that we should use for new submanifests that
# are added when syncing an existing workspace.
git_event_log = git_event_log or EventLog()
spec = submanifest.ToSubmanifestSpec()
# Use the init options from the existing manifestProject, or the parent if
# it doesn't exist.
#
# Today, we only support changing manifest_groups on the sub-manifest, with
# no supported-for-the-user way to change the other arguments from those
# specified by the outermost manifest.
#
# TODO(lamontjones): determine which of these should come from the outermost
# manifest and which should come from the parent manifest.
mp = self if self.Exists else submanifest.parent.manifestProject
return self.Sync(
manifest_url=spec.manifestUrl,
manifest_branch=spec.revision,
standalone_manifest=mp.standalone_manifest_url,
groups=mp.manifest_groups,
platform=mp.manifest_platform,
mirror=mp.mirror,
dissociate=mp.dissociate,
reference=mp.reference,
worktree=mp.use_worktree,
submodules=mp.submodules,
archive=mp.archive,
partial_clone=mp.partial_clone,
clone_filter=mp.clone_filter,
partial_clone_exclude=mp.partial_clone_exclude,
clone_bundle=mp.clone_bundle,
git_lfs=mp.git_lfs,
use_superproject=mp.use_superproject,
verbose=verbose,
current_branch_only=current_branch_only,
tags=tags,
depth=mp.depth,
git_event_log=git_event_log,
manifest_name=spec.manifestName,
this_manifest_only=True,
outer_manifest=False,
)
def Sync(self, _kwargs_only=(), manifest_url='', manifest_branch=None,
standalone_manifest=False, groups='', mirror=False, reference='',
dissociate=False, worktree=False, submodules=False, archive=False,
@ -3479,7 +3592,7 @@ class ManifestProject(MetaProject):
platform: a string, restrict the checkout to projects with the specified
platform group.
git_event_log: an EventLog, for git tracing.
tags: a boolean, whether to fetch tags.,
tags: a boolean, whether to fetch tags.
manifest_name: a string, the name of the manifest file to use.
this_manifest_only: a boolean, whether to only operate on the current sub
manifest.
@ -3490,7 +3603,7 @@ class ManifestProject(MetaProject):
"""
assert _kwargs_only == (), 'Sync only accepts keyword arguments.'
groups = groups or 'default'
groups = groups or self.manifest.GetDefaultGroupsStr(with_platform=False)
platform = platform or 'auto'
git_event_log = git_event_log or EventLog()
if outer_manifest and self.manifest.is_submanifest:
@ -3620,6 +3733,7 @@ class ManifestProject(MetaProject):
elif platform != 'none':
print('fatal: invalid platform flag', file=sys.stderr)
return False
self.config.SetString('manifest.platform', platform)
groups = [x for x in groups if x]
groupstr = ','.join(groups)
@ -3703,46 +3817,45 @@ class ManifestProject(MetaProject):
if use_superproject is not None:
self.config.SetBoolean('repo.superproject', use_superproject)
if standalone_manifest:
if is_new:
manifest_name = 'default.xml'
manifest_data = fetch.fetch_file(manifest_url, verbose=verbose)
dest = os.path.join(self.worktree, manifest_name)
os.makedirs(os.path.dirname(dest), exist_ok=True)
with open(dest, 'wb') as f:
f.write(manifest_data)
return
if not standalone_manifest:
if not self.Sync_NetworkHalf(
is_new=is_new, quiet=not verbose, verbose=verbose,
clone_bundle=clone_bundle, current_branch_only=current_branch_only,
tags=tags, submodules=submodules, clone_filter=clone_filter,
partial_clone_exclude=self.manifest.PartialCloneExclude):
r = self.GetRemote(self.remote.name)
print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
if not self.Sync_NetworkHalf(is_new=is_new, quiet=not verbose, verbose=verbose,
clone_bundle=clone_bundle,
current_branch_only=current_branch_only,
tags=tags, submodules=submodules,
clone_filter=clone_filter,
partial_clone_exclude=self.manifest.PartialCloneExclude):
r = self.GetRemote(self.remote.name)
print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
# Better delete the manifest git dir if we created it; otherwise next
# time (when user fixes problems) we won't go through the "is_new" logic.
if is_new:
platform_utils.rmtree(self.gitdir)
return False
if manifest_branch:
self.MetaBranchSwitch(submodules=submodules)
syncbuf = SyncBuffer(self.config)
self.Sync_LocalHalf(syncbuf, submodules=submodules)
syncbuf.Finish()
if is_new or self.CurrentBranch is None:
if not self.StartBranch('default'):
print('fatal: cannot create default in manifest', file=sys.stderr)
# Better delete the manifest git dir if we created it; otherwise next
# time (when user fixes problems) we won't go through the "is_new" logic.
if is_new:
platform_utils.rmtree(self.gitdir)
return False
if not manifest_name:
print('fatal: manifest name (-m) is required.', file=sys.stderr)
return False
if manifest_branch:
self.MetaBranchSwitch(submodules=submodules)
syncbuf = SyncBuffer(self.config)
self.Sync_LocalHalf(syncbuf, submodules=submodules)
syncbuf.Finish()
if is_new or self.CurrentBranch is None:
if not self.StartBranch('default'):
print('fatal: cannot create default in manifest', file=sys.stderr)
return False
if not manifest_name:
print('fatal: manifest name (-m) is required.', file=sys.stderr)
return False
elif is_new:
# This is a new standalone manifest.
manifest_name = 'default.xml'
manifest_data = fetch.fetch_file(manifest_url, verbose=verbose)
dest = os.path.join(self.worktree, manifest_name)
os.makedirs(os.path.dirname(dest), exist_ok=True)
with open(dest, 'wb') as f:
f.write(manifest_data)
try:
self.manifest.Link(manifest_name)
@ -3754,7 +3867,7 @@ class ManifestProject(MetaProject):
if not this_manifest_only:
for submanifest in self.manifest.submanifests.values():
spec = submanifest.ToSubmanifestSpec(root=self.manifest.outer_client)
spec = submanifest.ToSubmanifestSpec()
submanifest.repo_client.manifestProject.Sync(
manifest_url=spec.manifestUrl,
manifest_branch=spec.revision,
@ -3783,10 +3896,10 @@ class ManifestProject(MetaProject):
outer_manifest=False,
)
# Lastly, clone the superproject(s).
if self.manifest.manifestProject.use_superproject:
sync_result = Superproject(
self.manifest, self.manifest.repodir, git_event_log, quiet=not verbose).Sync()
# Lastly, if the manifest has a <superproject> then have the superproject
# sync it (if it will be used).
if git_superproject.UseSuperproject(use_superproject, self.manifest):
sync_result = self.manifest.superproject.Sync(git_event_log)
if not sync_result.success:
print('warning: git update of superproject for '
f'{self.manifest.path_prefix} failed, repo sync will not use '

View File

@ -35,18 +35,21 @@ to the Unix 'patch' command.
dest='absolute', action='store_true',
help='paths are relative to the repository root')
def _ExecuteOne(self, absolute, project):
def _ExecuteOne(self, absolute, local, project):
"""Obtains the diff for a specific project.
Args:
absolute: Paths are relative to the root.
local: a boolean, if True, the path is relative to the local
(sub)manifest. If false, the path is relative to the
outermost manifest.
project: Project to get status of.
Returns:
The status of the project.
"""
buf = io.StringIO()
ret = project.PrintWorkTreeDiff(absolute, output_redir=buf)
ret = project.PrintWorkTreeDiff(absolute, output_redir=buf, local=local)
return (ret, buf.getvalue())
def Execute(self, opt, args):
@ -63,7 +66,7 @@ to the Unix 'patch' command.
return self.ExecuteInParallel(
opt.jobs,
functools.partial(self._ExecuteOne, opt.absolute),
functools.partial(self._ExecuteOne, opt.absolute, opt.this_manifest_only),
all_projects,
callback=_ProcessResults,
ordered=True)

View File

@ -84,6 +84,11 @@ REPO_PROJECT is set to the unique name of the project.
REPO_PATH is the path relative the the root of the client.
REPO_OUTERPATH is the path of the sub manifest's root relative to the root of
the client.
REPO_INNERPATH is the path relative to the root of the sub manifest.
REPO_REMOTE is the name of the remote system from the manifest.
REPO_LREV is the name of the revision from the manifest, translated
@ -290,8 +295,9 @@ def DoWork(project, mirror, opt, cmd, shell, cnt, config):
env[name] = val
setenv('REPO_PROJECT', project.name)
setenv('REPO_PATH', project.relpath)
setenv('REPO_OUTERPATH', project.RelPath(local=opt.this_manifest_only))
setenv('REPO_OUTERPATH', project.manifest.path_prefix)
setenv('REPO_INNERPATH', project.relpath)
setenv('REPO_PATH', project.RelPath(local=opt.this_manifest_only))
setenv('REPO_REMOTE', project.remote.name)
try:
# If we aren't in a fully synced state and we don't have the ref the manifest

View File

@ -65,8 +65,7 @@ class Info(PagedCommand):
self.manifest = self.manifest.outer_client
manifestConfig = self.manifest.manifestProject.config
mergeBranch = manifestConfig.GetBranch("default").merge
manifestGroups = (manifestConfig.GetString('manifest.groups')
or 'all,-notdefault')
manifestGroups = self.manifest.GetGroupsStr()
self.heading("Manifest branch: ")
if self.manifest.default.revisionExpr:

View File

@ -24,8 +24,6 @@ from error import ManifestParseError
from project import SyncBuffer
from git_config import GitConfig
from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD
import fetch
import platform_utils
from wrapper import Wrapper
@ -91,11 +89,10 @@ to update the working directory files.
def _Options(self, p, gitc_init=False):
Wrapper().InitParser(p, gitc_init=gitc_init)
m = p.add_option_group('Multi-manifest')
m.add_option('--outer-manifest', action='store_true',
m.add_option('--outer-manifest', action='store_true', default=True,
help='operate starting at the outermost manifest')
m.add_option('--no-outer-manifest', dest='outer_manifest',
action='store_false', default=None,
help='do not operate on outer manifests')
action='store_false', help='do not operate on outer manifests')
m.add_option('--this-manifest-only', action='store_true', default=None,
help='only operate on this (sub)manifest')
m.add_option('--no-this-manifest-only', '--all-manifests',
@ -260,6 +257,9 @@ to update the working directory files.
if opt.use_superproject is not None:
self.OptionParser.error('--mirror and --use-superproject cannot be '
'used together.')
if opt.archive and opt.use_superproject is not None:
self.OptionParser.error('--archive and --use-superproject cannot be used '
'together.')
if opt.standalone_manifest and (opt.manifest_branch or
opt.manifest_name != 'default.xml'):

View File

@ -83,7 +83,7 @@ the following meanings:
dest='orphans', action='store_true',
help="include objects in working directory outside of repo projects")
def _StatusHelper(self, quiet, project):
def _StatusHelper(self, quiet, local, project):
"""Obtains the status for a specific project.
Obtains the status for a project, redirecting the output to
@ -91,13 +91,17 @@ the following meanings:
Args:
quiet: Where to output the status.
local: a boolean, if True, the path is relative to the local
(sub)manifest. If false, the path is relative to the
outermost manifest.
project: Project to get status of.
Returns:
The status of the project.
"""
buf = io.StringIO()
ret = project.PrintWorkTreeStatus(quiet=quiet, output_redir=buf)
ret = project.PrintWorkTreeStatus(quiet=quiet, output_redir=buf,
local=local)
return (ret, buf.getvalue())
def _FindOrphans(self, dirs, proj_dirs, proj_dirs_parents, outstring):
@ -130,7 +134,7 @@ the following meanings:
counter = self.ExecuteInParallel(
opt.jobs,
functools.partial(self._StatusHelper, opt.quiet),
functools.partial(self._StatusHelper, opt.quiet, opt.this_manifest_only),
all_projects,
callback=_ProcessResults,
ordered=True)

View File

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import collections
import functools
import http.cookiejar as cookielib
import io
@ -66,7 +67,7 @@ _ONE_DAY_S = 24 * 60 * 60
class Sync(Command, MirrorSafeCommand):
jobs = 1
COMMON = True
MULTI_MANIFEST_SUPPORT = False
MULTI_MANIFEST_SUPPORT = True
helpSummary = "Update working tree to the latest revision"
helpUsage = """
%prog [<project>...]
@ -170,9 +171,9 @@ later is required to fix a server side protocol bug.
PARALLEL_JOBS = 1
def _CommonOptions(self, p):
if self.manifest:
if self.outer_client and self.outer_client.manifest:
try:
self.PARALLEL_JOBS = self.manifest.default.sync_j
self.PARALLEL_JOBS = self.outer_client.manifest.default.sync_j
except ManifestParseError:
pass
super()._CommonOptions(p)
@ -270,68 +271,117 @@ later is required to fix a server side protocol bug.
dest='repo_upgraded', action='store_true',
help=SUPPRESS_HELP)
def _GetBranch(self):
"""Returns the branch name for getting the approved manifest."""
p = self.manifest.manifestProject
b = p.GetBranch(p.CurrentBranch)
def _GetBranch(self, manifest_project):
"""Returns the branch name for getting the approved smartsync manifest.
Args:
manifest_project: the manifestProject to query.
"""
b = manifest_project.GetBranch(manifest_project.CurrentBranch)
branch = b.merge
if branch.startswith(R_HEADS):
branch = branch[len(R_HEADS):]
return branch
def _GetCurrentBranchOnly(self, opt):
def _GetCurrentBranchOnly(self, opt, manifest):
"""Returns whether current-branch or use-superproject options are enabled.
Args:
opt: Program options returned from optparse. See _Options().
manifest: The manifest to use.
Returns:
True if a superproject is requested, otherwise the value of the
current_branch option (True, False or None).
"""
return git_superproject.UseSuperproject(opt, self.manifest) or opt.current_branch_only
return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests, superproject_logging_data):
"""Update revisionId of every project with the SHA from superproject.
def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
manifest):
"""Update revisionId of projects with the commit hash from the superproject.
This function updates each project's revisionId with SHA from superproject.
It writes the updated manifest into a file and reloads the manifest from it.
This function updates each project's revisionId with the commit hash from
the superproject. It writes the updated manifest into a file and reloads
the manifest from it. When appropriate, sub manifests are also processed.
Args:
opt: Program options returned from optparse. See _Options().
args: Arguments to pass to GetProjects. See the GetProjects
docstring for details.
load_local_manifests: Whether to load local manifests.
superproject_logging_data: A dictionary of superproject data that is to be logged.
Returns:
Returns path to the overriding manifest file instead of None.
superproject_logging_data: A dictionary of superproject data to log.
manifest: The manifest to use.
"""
superproject = self.manifest.superproject
superproject.SetQuiet(opt.quiet)
print_messages = git_superproject.PrintMessages(opt, self.manifest)
superproject.SetPrintMessages(print_messages)
if opt.local_only:
manifest_path = superproject.manifest_path
have_superproject = manifest.superproject or any(
m.superproject for m in manifest.all_children)
if not have_superproject:
return
if opt.local_only and manifest.superproject:
manifest_path = manifest.superproject.manifest_path
if manifest_path:
self._ReloadManifest(manifest_path, load_local_manifests)
return manifest_path
self._ReloadManifest(manifest_path, manifest)
return
all_projects = self.GetProjects(args,
missing_ok=True,
submodules_ok=opt.fetch_submodules)
update_result = superproject.UpdateProjectsRevisionId(
all_projects, git_event_log=self.git_event_log)
manifest_path = update_result.manifest_path
superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
if manifest_path:
self._ReloadManifest(manifest_path, load_local_manifests)
submodules_ok=opt.fetch_submodules,
manifest=manifest,
all_manifests=not opt.this_manifest_only)
per_manifest = collections.defaultdict(list)
manifest_paths = {}
if opt.this_manifest_only:
per_manifest[manifest.path_prefix] = all_projects
else:
if print_messages:
print('warning: Update of revisionId from superproject has failed, '
'repo sync will not use superproject to fetch the source. ',
'Please resync with the --no-use-superproject option to avoid this repo warning.',
file=sys.stderr)
if update_result.fatal and opt.use_superproject is not None:
sys.exit(1)
return manifest_path
for p in all_projects:
per_manifest[p.manifest.path_prefix].append(p)
superproject_logging_data = {}
need_unload = False
for m in self.ManifestList(opt):
if not m.path_prefix in per_manifest:
continue
use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
if superproject_logging_data:
superproject_logging_data['multimanifest'] = True
superproject_logging_data.update(
superproject=use_super,
haslocalmanifests=bool(m.HasLocalManifests),
hassuperprojecttag=bool(m.superproject),
)
if use_super and (m.IsMirror or m.IsArchive):
# Don't use superproject, because we have no working tree.
use_super = False
superproject_logging_data['superproject'] = False
superproject_logging_data['noworktree'] = True
if opt.use_superproject is not False:
print(f'{m.path_prefix}: not using superproject because there is no '
'working tree.')
if not use_super:
continue
m.superproject.SetQuiet(opt.quiet)
print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
m.superproject.SetPrintMessages(print_messages)
update_result = m.superproject.UpdateProjectsRevisionId(
per_manifest[m.path_prefix], git_event_log=self.git_event_log)
manifest_path = update_result.manifest_path
superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
if manifest_path:
m.SetManifestOverride(manifest_path)
need_unload = True
else:
if print_messages:
print(f'{m.path_prefix}: warning: Update of revisionId from '
'superproject has failed, repo sync will not use superproject '
'to fetch the source. ',
'Please resync with the --no-use-superproject option to avoid '
'this repo warning.',
file=sys.stderr)
if update_result.fatal and opt.use_superproject is not None:
sys.exit(1)
if need_unload:
m.outer_client.manifest.Unload()
def _FetchProjectList(self, opt, projects):
"""Main function of the fetch worker.
@ -365,16 +415,16 @@ later is required to fix a server side protocol bug.
quiet=opt.quiet,
verbose=opt.verbose,
output_redir=buf,
current_branch_only=self._GetCurrentBranchOnly(opt),
current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
force_sync=opt.force_sync,
clone_bundle=opt.clone_bundle,
tags=opt.tags, archive=self.manifest.IsArchive,
tags=opt.tags, archive=project.manifest.IsArchive,
optimized_fetch=opt.optimized_fetch,
retry_fetches=opt.retry_fetches,
prune=opt.prune,
ssh_proxy=self.ssh_proxy,
clone_filter=self.manifest.CloneFilter,
partial_clone_exclude=self.manifest.PartialCloneExclude)
clone_filter=project.manifest.CloneFilter,
partial_clone_exclude=project.manifest.PartialCloneExclude)
output = buf.getvalue()
if (opt.verbose or not success) and output:
@ -471,13 +521,13 @@ later is required to fix a server side protocol bug.
pm.end()
self._fetch_times.Save()
if not self.manifest.IsArchive:
if not self.outer_client.manifest.IsArchive:
self._GCProjects(projects, opt, err_event)
return (ret, fetched)
def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
load_local_manifests, ssh_proxy):
def _FetchMain(self, opt, args, all_projects, err_event,
ssh_proxy, manifest):
"""The main network fetch loop.
Args:
@ -485,14 +535,13 @@ later is required to fix a server side protocol bug.
args: Command line args used to filter out projects.
all_projects: List of all projects that should be fetched.
err_event: Whether an error was hit while processing.
manifest_name: Manifest file to be reloaded.
load_local_manifests: Whether to load local manifests.
ssh_proxy: SSH manager for clients & masters.
manifest: The manifest to use.
Returns:
List of all projects that should be checked out.
"""
rp = self.manifest.repoProject
rp = manifest.repoProject
to_fetch = []
now = time.time()
@ -516,10 +565,12 @@ later is required to fix a server side protocol bug.
# Iteratively fetch missing and/or nested unregistered submodules
previously_missing_set = set()
while True:
self._ReloadManifest(manifest_name, load_local_manifests)
self._ReloadManifest(None, manifest)
all_projects = self.GetProjects(args,
missing_ok=True,
submodules_ok=opt.fetch_submodules)
submodules_ok=opt.fetch_submodules,
manifest=manifest,
all_manifests=not opt.this_manifest_only)
missing = []
for project in all_projects:
if project.gitdir not in fetched:
@ -551,7 +602,7 @@ later is required to fix a server side protocol bug.
Whether the fetch was successful.
"""
start = time.time()
syncbuf = SyncBuffer(self.manifest.manifestProject.config,
syncbuf = SyncBuffer(project.manifest.manifestProject.config,
detach_head=detach_head)
success = False
try:
@ -614,7 +665,7 @@ later is required to fix a server side protocol bug.
for project in projects:
# Make sure pruning never kicks in with shared projects.
if (not project.use_git_worktrees and
len(project.manifest.GetProjectsWithName(project.name)) > 1):
len(project.manifest.GetProjectsWithName(project.name, all_manifests=True)) > 1):
if not opt.quiet:
print('\r%s: Shared project %s found, disabling pruning.' %
(project.relpath, project.name))
@ -688,28 +739,41 @@ later is required to fix a server side protocol bug.
t.join()
pm.end()
def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
def _ReloadManifest(self, manifest_name, manifest):
"""Reload the manfiest from the file specified by the |manifest_name|.
It unloads the manifest if |manifest_name| is None.
Args:
manifest_name: Manifest file to be reloaded.
load_local_manifests: Whether to load local manifests.
manifest: The manifest to use.
"""
if manifest_name:
# Override calls Unload already
self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
manifest.Override(manifest_name)
else:
self.manifest.Unload()
manifest.Unload()
def UpdateProjectList(self, opt):
def UpdateProjectList(self, opt, manifest):
"""Update the cached projects list for |manifest|
In a multi-manifest checkout, each manifest has its own project.list.
Args:
opt: Program options returned from optparse. See _Options().
manifest: The manifest to use.
Returns:
0: success
1: failure
"""
new_project_paths = []
for project in self.GetProjects(None, missing_ok=True):
for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
all_manifests=False):
if project.relpath:
new_project_paths.append(project.relpath)
file_name = 'project.list'
file_path = os.path.join(self.manifest.subdir, file_name)
file_path = os.path.join(manifest.subdir, file_name)
old_project_paths = []
if os.path.exists(file_path):
@ -721,16 +785,16 @@ later is required to fix a server side protocol bug.
continue
if path not in new_project_paths:
# If the path has already been deleted, we don't need to do it
gitdir = os.path.join(self.manifest.topdir, path, '.git')
gitdir = os.path.join(manifest.topdir, path, '.git')
if os.path.exists(gitdir):
project = Project(
manifest=self.manifest,
manifest=manifest,
name=path,
remote=RemoteSpec('origin'),
gitdir=gitdir,
objdir=gitdir,
use_git_worktrees=os.path.isfile(gitdir),
worktree=os.path.join(self.manifest.topdir, path),
worktree=os.path.join(manifest.topdir, path),
relpath=path,
revisionExpr='HEAD',
revisionId=None,
@ -746,7 +810,7 @@ later is required to fix a server side protocol bug.
fd.write('\n')
return 0
def UpdateCopyLinkfileList(self):
def UpdateCopyLinkfileList(self, manifest):
"""Save all dests of copyfile and linkfile, and update them if needed.
Returns:
@ -755,7 +819,8 @@ later is required to fix a server side protocol bug.
new_paths = {}
new_linkfile_paths = []
new_copyfile_paths = []
for project in self.GetProjects(None, missing_ok=True):
for project in self.GetProjects(None, missing_ok=True,
manifest=manifest, all_manifests=False):
new_linkfile_paths.extend(x.dest for x in project.linkfiles)
new_copyfile_paths.extend(x.dest for x in project.copyfiles)
@ -765,7 +830,7 @@ later is required to fix a server side protocol bug.
}
copylinkfile_name = 'copy-link-files.json'
copylinkfile_path = os.path.join(self.manifest.subdir, copylinkfile_name)
copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
old_copylinkfile_paths = {}
if os.path.exists(copylinkfile_path):
@ -796,13 +861,13 @@ later is required to fix a server side protocol bug.
json.dump(new_paths, fp)
return True
def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
if not self.manifest.manifest_server:
def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
if not manifest.manifest_server:
print('error: cannot smart sync: no manifest server defined in '
'manifest', file=sys.stderr)
sys.exit(1)
manifest_server = self.manifest.manifest_server
manifest_server = manifest.manifest_server
if not opt.quiet:
print('Using manifest server %s' % manifest_server)
@ -843,7 +908,7 @@ later is required to fix a server side protocol bug.
try:
server = xmlrpc.client.Server(manifest_server, transport=transport)
if opt.smart_sync:
branch = self._GetBranch()
branch = self._GetBranch(manifest.manifestProject)
if 'SYNC_TARGET' in os.environ:
target = os.environ['SYNC_TARGET']
@ -869,36 +934,68 @@ later is required to fix a server side protocol bug.
% (smart_sync_manifest_path, e),
file=sys.stderr)
sys.exit(1)
self._ReloadManifest(manifest_name)
self._ReloadManifest(manifest_name, manifest)
else:
print('error: manifest server RPC call failed: %s' %
manifest_str, file=sys.stderr)
sys.exit(1)
except (socket.error, IOError, xmlrpc.client.Fault) as e:
print('error: cannot connect to manifest server %s:\n%s'
% (self.manifest.manifest_server, e), file=sys.stderr)
% (manifest.manifest_server, e), file=sys.stderr)
sys.exit(1)
except xmlrpc.client.ProtocolError as e:
print('error: cannot connect to manifest server %s:\n%d %s'
% (self.manifest.manifest_server, e.errcode, e.errmsg),
% (manifest.manifest_server, e.errcode, e.errmsg),
file=sys.stderr)
sys.exit(1)
return manifest_name
def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
"""Fetch & update the local manifest project.
After syncing the manifest project, if the manifest has any sub manifests,
those are recursively processed.
Args:
opt: Program options returned from optparse. See _Options().
mp: the manifestProject to query.
manifest_name: Manifest file to be reloaded.
"""
if not mp.standalone_manifest_url:
self._UpdateManifestProject(opt, mp, manifest_name)
if mp.manifest.submanifests:
for submanifest in mp.manifest.submanifests.values():
child = submanifest.repo_client.manifest
child.manifestProject.SyncWithPossibleInit(
submanifest,
current_branch_only=self._GetCurrentBranchOnly(opt, child),
verbose=opt.verbose,
tags=opt.tags,
git_event_log=self.git_event_log,
)
self._UpdateAllManifestProjects(opt, child.manifestProject, None)
def _UpdateManifestProject(self, opt, mp, manifest_name):
"""Fetch & update the local manifest project."""
"""Fetch & update the local manifest project.
Args:
opt: Program options returned from optparse. See _Options().
mp: the manifestProject to query.
manifest_name: Manifest file to be reloaded.
"""
if not opt.local_only:
start = time.time()
success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
current_branch_only=self._GetCurrentBranchOnly(opt),
current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
force_sync=opt.force_sync,
tags=opt.tags,
optimized_fetch=opt.optimized_fetch,
retry_fetches=opt.retry_fetches,
submodules=self.manifest.HasSubmodules,
clone_filter=self.manifest.CloneFilter,
partial_clone_exclude=self.manifest.PartialCloneExclude)
submodules=mp.manifest.HasSubmodules,
clone_filter=mp.manifest.CloneFilter,
partial_clone_exclude=mp.manifest.PartialCloneExclude)
finish = time.time()
self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
start, finish, success)
@ -906,15 +1003,16 @@ later is required to fix a server side protocol bug.
if mp.HasChanges:
syncbuf = SyncBuffer(mp.config)
start = time.time()
mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
clean = syncbuf.Finish()
self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
start, time.time(), clean)
if not clean:
sys.exit(1)
self._ReloadManifest(manifest_name)
self._ReloadManifest(manifest_name, mp.manifest)
if opt.jobs is None:
self.jobs = self.manifest.default.sync_j
self.jobs = mp.manifest.default.sync_j
def ValidateOptions(self, opt, args):
if opt.force_broken:
@ -937,9 +1035,6 @@ later is required to fix a server side protocol bug.
if opt.prune is None:
opt.prune = True
if self.manifest.is_multimanifest and not opt.this_manifest_only and args:
self.OptionParser.error('partial syncs must use --this-manifest-only')
def Execute(self, opt, args):
if opt.jobs:
self.jobs = opt.jobs
@ -947,18 +1042,22 @@ later is required to fix a server side protocol bug.
soft_limit, _ = _rlimit_nofile()
self.jobs = min(self.jobs, (soft_limit - 5) // 3)
manifest = self.outer_manifest
if not opt.outer_manifest:
manifest = self.manifest
if opt.manifest_name:
self.manifest.Override(opt.manifest_name)
manifest.Override(opt.manifest_name)
manifest_name = opt.manifest_name
smart_sync_manifest_path = os.path.join(
self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
manifest.manifestProject.worktree, 'smart_sync_override.xml')
if opt.clone_bundle is None:
opt.clone_bundle = self.manifest.CloneBundle
opt.clone_bundle = manifest.CloneBundle
if opt.smart_sync or opt.smart_tag:
manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
else:
if os.path.isfile(smart_sync_manifest_path):
try:
@ -969,7 +1068,7 @@ later is required to fix a server side protocol bug.
err_event = multiprocessing.Event()
rp = self.manifest.repoProject
rp = manifest.repoProject
rp.PreSync()
cb = rp.CurrentBranch
if cb:
@ -979,38 +1078,26 @@ later is required to fix a server side protocol bug.
'receive updates; run `repo init --repo-rev=stable` to fix.',
file=sys.stderr)
mp = self.manifest.manifestProject
is_standalone_manifest = bool(mp.standalone_manifest_url)
if not is_standalone_manifest:
mp.PreSync()
for m in self.ManifestList(opt):
mp = m.manifestProject
is_standalone_manifest = bool(mp.standalone_manifest_url)
if not is_standalone_manifest:
mp.PreSync()
if opt.repo_upgraded:
_PostRepoUpgrade(self.manifest, quiet=opt.quiet)
if opt.repo_upgraded:
_PostRepoUpgrade(m, quiet=opt.quiet)
if not opt.mp_update:
if opt.mp_update:
self._UpdateAllManifestProjects(opt, mp, manifest_name)
else:
print('Skipping update of local manifest project.')
elif not is_standalone_manifest:
self._UpdateManifestProject(opt, mp, manifest_name)
load_local_manifests = not self.manifest.HasLocalManifests
use_superproject = git_superproject.UseSuperproject(opt, self.manifest)
if use_superproject and (self.manifest.IsMirror or self.manifest.IsArchive):
# Don't use superproject, because we have no working tree.
use_superproject = False
if opt.use_superproject is not None:
print('Defaulting to no-use-superproject because there is no working tree.')
superproject_logging_data = {
'superproject': use_superproject,
'haslocalmanifests': bool(self.manifest.HasLocalManifests),
'hassuperprojecttag': bool(self.manifest.superproject),
}
if use_superproject:
manifest_name = self._UpdateProjectsRevisionId(
opt, args, load_local_manifests, superproject_logging_data) or opt.manifest_name
superproject_logging_data = {}
self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
manifest)
if self.gitc_manifest:
gitc_manifest_projects = self.GetProjects(args,
missing_ok=True)
gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
gitc_projects = []
opened_projects = []
for project in gitc_manifest_projects:
@ -1029,7 +1116,7 @@ later is required to fix a server side protocol bug.
if manifest_name:
manifest.Override(manifest_name)
else:
manifest.Override(self.manifest.manifestFile)
manifest.Override(manifest.manifestFile)
gitc_utils.generate_gitc_manifest(self.gitc_manifest,
manifest,
gitc_projects)
@ -1039,26 +1126,28 @@ later is required to fix a server side protocol bug.
# generate a new args list to represent the opened projects.
# TODO: make this more reliable -- if there's a project name/path overlap,
# this may choose the wrong project.
args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
for path in opened_projects]
if not args:
return
all_projects = self.GetProjects(args,
missing_ok=True,
submodules_ok=opt.fetch_submodules)
submodules_ok=opt.fetch_submodules,
manifest=manifest,
all_manifests=not opt.this_manifest_only)
err_network_sync = False
err_update_projects = False
self._fetch_times = _FetchTimes(self.manifest)
self._fetch_times = _FetchTimes(manifest)
if not opt.local_only:
with multiprocessing.Manager() as manager:
with ssh.ProxyManager(manager) as ssh_proxy:
# Initialize the socket dir once in the parent.
ssh_proxy.sock()
all_projects = self._FetchMain(opt, args, all_projects, err_event,
manifest_name, load_local_manifests,
ssh_proxy)
ssh_proxy, manifest)
if opt.network_only:
return
@ -1074,23 +1163,24 @@ later is required to fix a server side protocol bug.
file=sys.stderr)
sys.exit(1)
if self.manifest.IsMirror or self.manifest.IsArchive:
# bail out now, we have no working tree
return
for m in self.ManifestList(opt):
if m.IsMirror or m.IsArchive:
# bail out now, we have no working tree
continue
if self.UpdateProjectList(opt):
err_event.set()
err_update_projects = True
if opt.fail_fast:
print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
sys.exit(1)
if self.UpdateProjectList(opt, m):
err_event.set()
err_update_projects = True
if opt.fail_fast:
print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
sys.exit(1)
err_update_linkfiles = not self.UpdateCopyLinkfileList()
if err_update_linkfiles:
err_event.set()
if opt.fail_fast:
print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
sys.exit(1)
err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
if err_update_linkfiles:
err_event.set()
if opt.fail_fast:
print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
sys.exit(1)
err_results = []
# NB: We don't exit here because this is the last step.
@ -1098,10 +1188,14 @@ later is required to fix a server side protocol bug.
if err_checkout:
err_event.set()
# If there's a notice that's supposed to print at the end of the sync, print
# it now...
if self.manifest.notice:
print(self.manifest.notice)
printed_notices = set()
# If there's a notice that's supposed to print at the end of the sync,
# print it now... But avoid printing duplicate messages, and preserve
# order.
for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
if m.notice and m.notice not in printed_notices:
print(m.notice)
printed_notices.add(m.notice)
# If we saw an error, exit with code 1 so that other scripts can check.
if err_event.is_set():

View File

@ -204,6 +204,12 @@ Gerrit Code Review: https://www.gerritcodereview.com/
p.add_option('-y', '--yes',
default=False, action='store_true',
help='answer yes to all safe prompts')
p.add_option('--ignore-untracked-files',
action='store_true', default=False,
help='ignore untracked files in the working copy')
p.add_option('--no-ignore-untracked-files',
dest='ignore_untracked_files', action='store_false',
help='always ask about untracked files in the working copy')
p.add_option('--no-cert-checks',
dest='validate_certs', action='store_false', default=True,
help='disable verifying ssl certs (unsafe)')
@ -370,6 +376,10 @@ Gerrit Code Review: https://www.gerritcodereview.com/
# Check if there are local changes that may have been forgotten
changes = branch.project.UncommitedFiles()
if opt.ignore_untracked_files:
untracked = set(branch.project.UntrackedFiles())
changes = [x for x in changes if x not in untracked]
if changes:
key = 'review.%s.autoupload' % branch.project.remote.review
answer = branch.project.config.GetBoolean(key)
@ -421,12 +431,6 @@ Gerrit Code Review: https://www.gerritcodereview.com/
labels = set(_ExpandCommaList(branch.project.config.GetString(key)))
for label in opt.labels:
labels.update(_ExpandCommaList(label))
# Basic sanity check on label syntax.
for label in labels:
if not re.match(r'^.+[+-][0-9]+$', label):
print('repo: error: invalid label syntax "%s": labels use forms '
'like CodeReview+1 or Verified-1' % (label,), file=sys.stderr)
sys.exit(1)
# Handle e-mail notifications.
if opt.notify is False:

View File

@ -24,7 +24,6 @@ from unittest import mock
import git_superproject
import git_trace2_event_log
import manifest_xml
import platform_utils
from test_manifest_xml import sort_attributes
@ -38,7 +37,8 @@ class SuperprojectTestCase(unittest.TestCase):
def setUp(self):
"""Set up superproject every time."""
self.tempdir = tempfile.mkdtemp(prefix='repo_tests')
self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests')
self.tempdir = self.tempdirobj.name
self.repodir = os.path.join(self.tempdir, '.repo')
self.manifest_file = os.path.join(
self.repodir, manifest_xml.MANIFEST_FILE_NAME)
@ -75,7 +75,7 @@ class SuperprojectTestCase(unittest.TestCase):
def tearDown(self):
"""Tear down superproject every time."""
platform_utils.rmtree(self.tempdir)
self.tempdirobj.cleanup()
def getXmlManifest(self, data):
"""Helper to initialize a manifest for testing."""
@ -312,9 +312,6 @@ class SuperprojectTestCase(unittest.TestCase):
'<project groups="notdefault,platform-' + self.platform + '" '
'name="platform/art" path="art" '
'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea" upstream="refs/heads/main"/>'
'<project clone-depth="1" groups="' + local_group + '" '
'name="platform/vendor/x" path="vendor/x" remote="goog" '
'revision="master-with-vendor"/>'
'<superproject name="superproject"/>'
'</manifest>')

View File

@ -17,7 +17,6 @@
import os
import platform
import re
import shutil
import tempfile
import unittest
import xml.dom.minidom
@ -92,7 +91,8 @@ class ManifestParseTestCase(unittest.TestCase):
"""TestCase for parsing manifests."""
def setUp(self):
self.tempdir = tempfile.mkdtemp(prefix='repo_tests')
self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests')
self.tempdir = self.tempdirobj.name
self.repodir = os.path.join(self.tempdir, '.repo')
self.manifest_dir = os.path.join(self.repodir, 'manifests')
self.manifest_file = os.path.join(
@ -111,7 +111,7 @@ class ManifestParseTestCase(unittest.TestCase):
""")
def tearDown(self):
shutil.rmtree(self.tempdir, ignore_errors=True)
self.tempdirobj.cleanup()
def getXmlManifest(self, data):
"""Helper to initialize a manifest for testing."""
@ -252,6 +252,37 @@ class XmlManifestTests(ManifestParseTestCase):
'<manifest></manifest>')
self.assertEqual(manifest.ToDict(), {})
def test_toxml_omit_local(self):
"""Does not include local_manifests projects when omit_local=True."""
manifest = self.getXmlManifest(
'<?xml version="1.0" encoding="UTF-8"?><manifest>'
'<remote name="a" fetch=".."/><default remote="a" revision="r"/>'
'<project name="p" groups="local::me"/>'
'<project name="q"/>'
'<project name="r" groups="keep"/>'
'</manifest>')
self.assertEqual(
manifest.ToXml(omit_local=True).toxml(),
'<?xml version="1.0" ?><manifest>'
'<remote name="a" fetch=".."/><default remote="a" revision="r"/>'
'<project name="q"/><project name="r" groups="keep"/></manifest>')
def test_toxml_with_local(self):
"""Does include local_manifests projects when omit_local=False."""
manifest = self.getXmlManifest(
'<?xml version="1.0" encoding="UTF-8"?><manifest>'
'<remote name="a" fetch=".."/><default remote="a" revision="r"/>'
'<project name="p" groups="local::me"/>'
'<project name="q"/>'
'<project name="r" groups="keep"/>'
'</manifest>')
self.assertEqual(
manifest.ToXml(omit_local=False).toxml(),
'<?xml version="1.0" ?><manifest>'
'<remote name="a" fetch=".."/><default remote="a" revision="r"/>'
'<project name="p" groups="local::me"/>'
'<project name="q"/><project name="r" groups="keep"/></manifest>')
def test_repo_hooks(self):
"""Check repo-hooks settings."""
manifest = self.getXmlManifest("""

View File

@ -17,7 +17,6 @@
import contextlib
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
@ -32,11 +31,7 @@ import project
@contextlib.contextmanager
def TempGitTree():
"""Create a new empty git checkout for testing."""
# TODO(vapier): Convert this to tempfile.TemporaryDirectory once we drop
# Python 2 support entirely.
try:
tempdir = tempfile.mkdtemp(prefix='repo-tests')
with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir:
# Tests need to assume, that main is default branch at init,
# which is not supported in config until 2.28.
cmd = ['git', 'init']
@ -50,8 +45,6 @@ def TempGitTree():
cmd += ['--template', templatedir]
subprocess.check_call(cmd, cwd=tempdir)
yield tempdir
finally:
platform_utils.rmtree(tempdir)
class FakeProject(object):
@ -124,14 +117,15 @@ class CopyLinkTestCase(unittest.TestCase):
"""
def setUp(self):
self.tempdir = tempfile.mkdtemp(prefix='repo_tests')
self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests')
self.tempdir = self.tempdirobj.name
self.topdir = os.path.join(self.tempdir, 'checkout')
self.worktree = os.path.join(self.topdir, 'git-project')
os.makedirs(self.topdir)
os.makedirs(self.worktree)
def tearDown(self):
shutil.rmtree(self.tempdir, ignore_errors=True)
self.tempdirobj.cleanup()
@staticmethod
def touch(path):

View File

@ -42,4 +42,4 @@ def test_get_current_branch_only(use_superproject, cli_args, result):
opts, _ = cmd.OptionParser.parse_args(cli_args)
with mock.patch('git_superproject.UseSuperproject', return_value=use_superproject):
assert cmd._GetCurrentBranchOnly(opts) == result
assert cmd._GetCurrentBranchOnly(opts, cmd.manifest) == result

View File

@ -14,11 +14,9 @@
"""Unittests for the wrapper.py module."""
import contextlib
from io import StringIO
import os
import re
import shutil
import sys
import tempfile
import unittest
@ -26,22 +24,9 @@ from unittest import mock
import git_command
import main
import platform_utils
import wrapper
@contextlib.contextmanager
def TemporaryDirectory():
"""Create a new empty git checkout for testing."""
# TODO(vapier): Convert this to tempfile.TemporaryDirectory once we drop
# Python 2 support entirely.
try:
tempdir = tempfile.mkdtemp(prefix='repo-tests')
yield tempdir
finally:
platform_utils.rmtree(tempdir)
def fixture(*paths):
"""Return a path relative to tests/fixtures.
"""
@ -336,19 +321,19 @@ class NeedSetupGnuPG(RepoWrapperTestCase):
def test_missing_dir(self):
"""The ~/.repoconfig tree doesn't exist yet."""
with TemporaryDirectory() as tempdir:
with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir:
self.wrapper.home_dot_repo = os.path.join(tempdir, 'foo')
self.assertTrue(self.wrapper.NeedSetupGnuPG())
def test_missing_keyring(self):
"""The keyring-version file doesn't exist yet."""
with TemporaryDirectory() as tempdir:
with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir:
self.wrapper.home_dot_repo = tempdir
self.assertTrue(self.wrapper.NeedSetupGnuPG())
def test_empty_keyring(self):
"""The keyring-version file exists, but is empty."""
with TemporaryDirectory() as tempdir:
with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir:
self.wrapper.home_dot_repo = tempdir
with open(os.path.join(tempdir, 'keyring-version'), 'w'):
pass
@ -356,7 +341,7 @@ class NeedSetupGnuPG(RepoWrapperTestCase):
def test_old_keyring(self):
"""The keyring-version file exists, but it's old."""
with TemporaryDirectory() as tempdir:
with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir:
self.wrapper.home_dot_repo = tempdir
with open(os.path.join(tempdir, 'keyring-version'), 'w') as fp:
fp.write('1.0\n')
@ -364,7 +349,7 @@ class NeedSetupGnuPG(RepoWrapperTestCase):
def test_new_keyring(self):
"""The keyring-version file exists, and is up-to-date."""
with TemporaryDirectory() as tempdir:
with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir:
self.wrapper.home_dot_repo = tempdir
with open(os.path.join(tempdir, 'keyring-version'), 'w') as fp:
fp.write('1000.0\n')
@ -376,7 +361,7 @@ class SetupGnuPG(RepoWrapperTestCase):
def test_full(self):
"""Make sure it works completely."""
with TemporaryDirectory() as tempdir:
with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir:
self.wrapper.home_dot_repo = tempdir
self.wrapper.gpg_dir = os.path.join(self.wrapper.home_dot_repo, 'gnupg')
self.assertTrue(self.wrapper.SetupGnuPG(True))
@ -426,7 +411,8 @@ class GitCheckoutTestCase(RepoWrapperTestCase):
@classmethod
def setUpClass(cls):
# Create a repo to operate on, but do it once per-class.
cls.GIT_DIR = tempfile.mkdtemp(prefix='repo-rev-tests')
cls.tempdirobj = tempfile.TemporaryDirectory(prefix='repo-rev-tests')
cls.GIT_DIR = cls.tempdirobj.name
run_git = wrapper.Wrapper().run_git
remote = os.path.join(cls.GIT_DIR, 'remote')
@ -455,10 +441,10 @@ class GitCheckoutTestCase(RepoWrapperTestCase):
@classmethod
def tearDownClass(cls):
if not cls.GIT_DIR:
if not cls.tempdirobj:
return
shutil.rmtree(cls.GIT_DIR)
cls.tempdirobj.cleanup()
class ResolveRepoRev(GitCheckoutTestCase):