sync, init: support repo.smartsync config and --head alias

Allow users to configure Smart Sync as the default for a checkout:

* Add `ManifestProject.smart_sync` backed by
  `self.config.GetBoolean("repo.smartsync")`.
* Add `-s`/`--smart-sync` and `--no-smart-sync`/`--head` to `repo init`
  to persist `repo.smartsync` in the manifest project config.
* Add `--head` as an alias for `--no-smart-sync` in `repo sync` and
  `repo init`.
* Update `Sync._ResolveSmartSyncOption` so `ManifestProject.smart_sync`
  takes precedence over `<default sync-smartsync="true" />` while
  remaining overridable by per-invocation `repo sync` CLI flags.

Bug: 569927750
Test: pytest tests/test_project.py tests/test_subcmds_init.py tests/test_subcmds_sync.py
Change-Id: Ib40efc530336efb59a28acfed3d582713e9d67b2
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/643303
Reviewed-by: Mike Frysinger <vapier@google.com>
Commit-Queue: LaMont Jones <lamontjones@google.com>
Tested-by: LaMont Jones <lamontjones@google.com>
This commit is contained in:
LaMont Jones authored and gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com committed 2026-10-21 11:22:58 -07:00
1 parent 46e9866ca0
commit f0124d0fd5
12 files changed
+158 -14

No files matched your search

+4
View File
@@ -236,6 +236,9 @@ _repo() {
'--clone-filter=[Clone filter]:filter:' \
'--use-superproject[Use superproject]' \
'--no-use-superproject[Do not use superproject]' \
'(-s --smart-sync)'{-s,--smart-sync}'[Default to smart sync]' \
'--no-smart-sync[Do not default to smart sync]' \
'--head[Do not default to smart sync]' \
'--clone-bundle[Use clone bundle]' \
'--no-clone-bundle[Do not use clone bundle]' \
'--git-lfs[Use git lfs]' \
@@ -338,6 +341,7 @@ _repo() {
'--no-auto-gc[Do not run auto gc]' \
'(-s --smart-sync)'{-s,--smart-sync}'[Smart sync]' \
'--no-smart-sync[Do not smart sync]' \
'--head[Do not smart sync]' \
'(-t --smart-tag)'{-t,--smart-tag=}'[Smart tag]:tag:' \
'--no-repo-verify[Do not verify repo]' \
'--no-verify[Do not verify]' \
+1
View File
@@ -176,6 +176,7 @@ User controlled settings are initialized when running `repo init`.
| repo.partialclone | `--partial-clone` | Create [partial git clones] |
| repo.partialcloneexclude | `--partial-clone-exclude` | Comma-delimited list of project names (not paths) to exclude while using [partial git clones] |
| repo.reference | `--reference` | Reference repo client checkout |
| repo.smartsync | `--smart-sync` | Default to [Smart Sync](./smart-sync.md) when syncing |
| repo.submodules | `--submodules` | Sync git submodules |
| repo.superproject | `--use-superproject` | Sync [superproject] |
| repo.worktree | `--worktree` | Use [git worktree] for checkouts |
+25 -3
View File
@@ -182,6 +182,28 @@ affect Smart Sync: the default Smart Sync from `sync-smartsync` (see below)
still runs, and an existing `smart_sync_override.xml` is replaced or removed as
in a regular sync.
### `repo init --[no-]smart-sync`
Users can configure Smart Sync as the default for a checkout by passing
`-s`/`--smart-sync` (or `--no-smart-sync`/`--head`) to `repo init` (this can
also be re-run in an existing checkout to change the setting).
When resolving whether to perform a Smart Sync by default, `repo sync` uses the
following precedence order (highest to lowest):
1. Per-invocation CLI flags (`-s`/`--smart-sync`, `--no-smart-sync`/`--head`,
or target selection flags like `-t`, `-m`, `--superproject-revision`, `-l`).
2. Checkout `repo init` setting (`repo init --[no-]smart-sync`).
3. [Per-user `sync.smartsync` setting](#Per_user-default-sync_smartsync).
4. Manifest [`sync-smartsync`](#sync-smartsync-manifest-attribute) setting.
Running `repo init --no-smart-sync` (or `repo init --head`) disables default
Smart Sync for the checkout even when enabled by the user setting or manifest.
As with the [manifest setting](#sync-smartsync-manifest-attribute), a
checkout-enabled default Smart Sync falls back to a regular ToT sync if the
manifest does not define a `<manifest-server>` or the manifest server cannot
provide a usable manifest.
### sync-smartsync (Manifest Attribute)
The manifest can enable Smart Sync by default for all `repo sync` invocations
@@ -230,10 +252,10 @@ match the settings Android build environments automatically setup.
3. `${TARGET_PRODUCT}-${TARGET_BUILD_VARIANT}`: If these variables are all
defined, then they are merged with `-` and used.
### --no-smart-sync
### --no-smart-sync / --head
Explicitly disables Smart Sync, overriding any `sync-smartsync="true"` setting
declared in the manifest `<default>` element.
Explicitly disables Smart Sync, overriding any `repo.smartsync` git config or
`sync-smartsync="true"` setting declared in the manifest `<default>` element.
### Per-user default (sync.smartsync)
+6
View File
@@ -103,6 +103,12 @@ use the manifest superproject to sync projects; implies \fB\-c\fR
\fB\-\-no\-use\-superproject\fR
disable use of manifest superprojects
.TP
\fB\-s\fR, \fB\-\-smart\-sync\fR
default to smart sync when syncing projects
.TP
\fB\-\-no\-smart\-sync\fR, \fB\-\-head\fR
do not default to smart sync when syncing projects
.TP
\fB\-\-clone\-bundle\fR
enable use of \fI\,/clone.bundle\/\fP on HTTP/HTTPS (default if not \fB\-\-partial\-clone\fR)
.TP
+1 -1
View File
@@ -125,7 +125,7 @@ do not run garbage collection on any projects (default)
\fB\-s\fR, \fB\-\-smart\-sync\fR
smart sync using manifest from the latest known good build
.TP
\fB\-\-no\-smart\-sync\fR
\fB\-\-no\-smart\-sync\fR, \fB\-\-head\fR
disable smart sync and sync to ToT instead
.TP
\fB\-t\fR SMART_TAG, \fB\-\-smart\-tag\fR=\fI\,SMART_TAG\/\fR
+13
View File
@@ -5607,6 +5607,11 @@ class ManifestProject(MetaProject):
"""Whether we use superproject."""
return self.config.GetBoolean("repo.superproject")
@property
def smart_sync(self) -> Optional[bool]:
"""Whether we use smart sync by default."""
return self.config.GetBoolean("repo.smartsync")
@property
def partial_clone(self):
"""Whether this is a partial clone."""
@@ -5701,6 +5706,7 @@ class ManifestProject(MetaProject):
clone_bundle=mp.clone_bundle,
git_lfs=mp.git_lfs,
use_superproject=mp.use_superproject,
smart_sync=mp.smart_sync,
verbose=verbose,
current_branch_only=current_branch_only,
tags=tags,
@@ -5732,6 +5738,7 @@ class ManifestProject(MetaProject):
clone_bundle=None,
git_lfs=None,
use_superproject=None,
smart_sync: Optional[bool] = None,
verbose=False,
current_branch_only=False,
git_event_log=None,
@@ -5773,6 +5780,7 @@ class ManifestProject(MetaProject):
git_lfs: a boolean, whether to enable git LFS support.
use_superproject: a boolean, whether to use the manifest
superproject to sync projects.
smart_sync: a boolean, whether to default to smart sync.
verbose: a boolean, whether to show all output, rather than only
errors.
current_branch_only: a boolean, whether to only fetch the current
@@ -5820,6 +5828,7 @@ class ManifestProject(MetaProject):
clone_bundle=clone_bundle,
git_lfs=git_lfs,
use_superproject=use_superproject,
smart_sync=smart_sync,
verbose=verbose,
current_branch_only=current_branch_only,
tags=tags,
@@ -6090,6 +6099,9 @@ class ManifestProject(MetaProject):
if use_superproject is not None:
self.config.SetBoolean("repo.superproject", use_superproject)
if smart_sync is not None:
self.config.SetBoolean("repo.smartsync", smart_sync)
if not standalone_manifest:
success = self.Sync_NetworkHalf(
is_new=is_new,
@@ -6173,6 +6185,7 @@ class ManifestProject(MetaProject):
clone_bundle=clone_bundle,
git_lfs=git_lfs,
use_superproject=use_superproject,
smart_sync=smart_sync,
verbose=verbose,
current_branch_only=current_branch_only,
tags=tags,
+15 -1
View File
@@ -129,7 +129,7 @@ if not REPO_REV:
BUG_URL = "https://issues.gerritcodereview.com/issues/new?component=1370071"
# increment this whenever we make important changes to this script
VERSION = (2, 69)
VERSION = (2, 71)
# increment this if the MAINTAINER_KEYS block is modified
KEYRING_VERSION = (2, 3)
@@ -443,6 +443,20 @@ def InitParser(parser):
dest="use_superproject",
help="disable use of manifest superprojects",
)
group.add_option(
"-s",
"--smart-sync",
action="store_true",
default=None,
help="default to smart sync when syncing projects",
)
group.add_option(
"--no-smart-sync",
"--head",
action="store_false",
dest="smart_sync",
help="do not default to smart sync when syncing projects",
)
group.add_option(
"--clone-bundle",
action="store_true",
+1
View File
@@ -164,6 +164,7 @@ to update the working directory files.
clone_bundle=opt.clone_bundle,
git_lfs=opt.git_lfs,
use_superproject=opt.use_superproject,
smart_sync=opt.smart_sync,
verbose=opt.verbose,
current_branch_only=opt.current_branch_only,
tags=opt.tags,
+20 -8
View File
@@ -839,6 +839,7 @@ later is required to fix a server side protocol bug.
)
p.add_option(
"--no-smart-sync",
"--head",
dest="smart_sync",
action="store_false",
help="disable smart sync and sync to ToT instead",
@@ -2527,16 +2528,20 @@ later is required to fix a server side protocol bug.
def _ResolveSmartSyncOption(
self, opt: optparse.Values, manifest: XmlManifest
) -> bool:
"""Resolve opt.smart_sync from CLI flags, user config and manifest.
"""Resolve opt.smart_sync from CLI flags, config, and manifest.
Precedence: CLI flags > per-user settings > per-manifest settings.
Precedence: CLI flags > per-checkout settings > per-user settings >
per-manifest settings.
See docs/repoconfig.md and docs/smart-sync.md for details.
Returns:
True if smart_sync was enabled implicitly by the manifest default,
False otherwise. Smart sync enabled via per-user settings counts
as an explicit user request, not implicit.
True if smart_sync was enabled implicitly by checkout or manifest
default, False otherwise. Smart sync enabled via per-user settings
counts as an explicit user request, not implicit.
"""
checkout_pref = getattr(manifest.manifestProject, "smart_sync", None)
if not isinstance(checkout_pref, bool):
checkout_pref = None
user_pref = RepoConfig.ForUser().GetBoolean("sync.smartsync")
default_allowed = (
opt.smart_sync is None
@@ -2545,13 +2550,20 @@ later is required to fix a server side protocol bug.
and not opt.superproject_revision
and not opt.local_only
)
default_smart_sync = (
checkout_pref
if checkout_pref is not None
else getattr(manifest.default, "sync_smartsync", False) is True
)
implicit = (
default_allowed
and user_pref is None
and getattr(manifest.default, "sync_smartsync", False) is True
and (checkout_pref is not None or user_pref is None)
and default_smart_sync
)
if opt.smart_sync is None:
if user_pref is False:
if checkout_pref is not None:
opt.smart_sync = implicit
elif user_pref is False:
opt.smart_sync = False
elif user_pref is True:
opt.smart_sync = default_allowed
+3
View File
@@ -1822,6 +1822,9 @@ class ManifestPropertiesFetchedCorrectly(unittest.TestCase):
fakeproj.config.SetBoolean("repo.superproject", False)
self.assertFalse(fakeproj.use_superproject)
fakeproj.config.SetBoolean("repo.smartsync", False)
self.assertFalse(fakeproj.smart_sync)
fakeproj.config.SetBoolean("repo.partialclone", False)
self.assertFalse(fakeproj.partial_clone)
+7 -1
View File
@@ -23,7 +23,13 @@ from subcmds import init
@pytest.mark.parametrize(
"argv",
([],),
(
[],
["-s"],
["--smart-sync"],
["--no-smart-sync"],
["--head"],
),
)
def test_cli_parser_good(argv: List[str]) -> None:
"""Check valid command line options."""
+62
View File
@@ -465,6 +465,8 @@ def test_cli_jobs_sync_j_max(
(["--smart-sync"], True, True, False),
(["--no-smart-sync"], True, False, False),
(["--no-smart-sync"], False, False, False),
(["--head"], True, False, False),
(["--head"], False, False, False),
(["-t", "tag123"], True, False, False),
(["--smart-tag=tag123"], True, False, False),
(["-m", "other.xml"], True, False, False),
@@ -491,6 +493,7 @@ def test_cli_smart_sync(
"""
manifest = mock.MagicMock()
manifest.default.sync_smartsync = sync_smartsync_manifest
manifest.manifestProject.smart_sync = None
cmd = sync.Sync(manifest=manifest)
opts, args = cmd.OptionParser.parse_args(argv)
@@ -500,6 +503,65 @@ def test_cli_smart_sync(
assert implicit == expected_implicit
@pytest.mark.parametrize(
"argv, config_smart_sync, user_pref, sync_smartsync_manifest, "
"expected_smart_sync, expected_implicit",
[
# Config True overrides manifest False/None and user_pref False.
([], True, None, False, True, True),
([], True, None, None, True, True),
([], True, False, False, True, True),
# Config False overrides manifest True and user_pref True.
([], False, None, True, False, False),
([], False, True, True, False, False),
# Config None falls back to user_pref, then manifest.
([], None, True, False, True, False),
([], None, False, True, False, False),
([], None, None, True, True, True),
([], None, None, False, False, False),
# CLI flags override config.
(["-s"], False, None, False, True, False),
(["--smart-sync"], False, None, True, True, False),
(["--no-smart-sync"], True, None, True, False, False),
(["--no-smart-sync"], True, None, False, False, False),
(["--head"], True, None, True, False, False),
(["--head"], True, None, False, False, False),
# Conflicting flags suppress implicit smart sync from config.
(["-t", "tag123"], True, None, False, False, False),
(["-m", "other.xml"], True, None, False, False, False),
(["-l"], True, None, False, False, False),
(["--nmu"], True, None, False, True, True),
(["--superproject-revision=abc"], True, None, False, False, False),
(["-s", "-l"], True, None, False, True, False),
],
)
def test_cli_smart_sync_with_config(
argv: List[str],
config_smart_sync: Optional[bool],
user_pref: Optional[bool],
sync_smartsync_manifest: Optional[bool],
expected_smart_sync: bool,
expected_implicit: bool,
) -> None:
"""Tests repo.smartsync config precedence between CLI, user, and
manifest.
"""
manifest = mock.MagicMock()
manifest.default.sync_smartsync = sync_smartsync_manifest
manifest.manifestProject.smart_sync = config_smart_sync
user_cfg = mock.MagicMock()
user_cfg.GetBoolean.return_value = user_pref
cmd = sync.Sync(manifest=manifest)
opts, args = cmd.OptionParser.parse_args(argv)
cmd.ValidateOptions(opts, args)
with mock.patch.object(sync.RepoConfig, "ForUser", return_value=user_cfg):
implicit = cmd._ResolveSmartSyncOption(opts, manifest)
assert opts.smart_sync == expected_smart_sync
assert implicit == expected_implicit
@pytest.mark.parametrize(
"argv, user_pref, sync_smartsync_manifest, expected_smart_sync, "
"expected_implicit",