From 978adb7ea504ae5c9238d03cea63f916035765c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Malmstr=C3=B6m?= Date: Mon, 13 Jul 2026 12:50:52 +0200 Subject: [PATCH] sync: Add CLI flag for globally disabling submodule fetch A global setting for disabling fetching of submodules is useful since this can currently otherwise only be done by modifying the manifest, or by explicitly providing projects on command line. Add this setting as --no-fetch-submodules to mirror the existing --fetch-submodules. Change-Id: Ic727c54f11a594aa52315751284b87138cf246bb Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/607641 Commit-Queue: Josef Malmstrom Reviewed-by: Gavin Mak Tested-by: Josef Malmstrom Reviewed-by: Mike Frysinger --- command.py | 20 ++++++++++++++++---- completion.zsh | 1 + man/repo-smartsync.1 | 3 +++ man/repo-sync.1 | 10 +++++++--- subcmds/sync.py | 9 ++++++++- tests/test_command.py | 31 +++++++++++++++++++++++++++++++ tests/test_subcmds_sync.py | 17 +++++++++++++++++ 7 files changed, 83 insertions(+), 8 deletions(-) diff --git a/command.py b/command.py index 7da063a25..bfafcd449 100644 --- a/command.py +++ b/command.py @@ -17,6 +17,7 @@ import multiprocessing import optparse import os import re +from typing import TYPE_CHECKING from error import InvalidProjectGroupsError from error import NoSuchProjectError @@ -25,6 +26,10 @@ from event_log import EventLog import progress +if TYPE_CHECKING: + from project import Project + + # Are we generating man-pages? GENERATE_MANPAGES = os.environ.get("_REPO_GENERATE_MANPAGES_") == " indeed! " @@ -375,7 +380,7 @@ class Command: manifest=None, groups="", missing_ok=False, - submodules_ok=False, + submodules_ok=None, all_manifests=False, ): """A list of projects that match the arguments. @@ -385,7 +390,9 @@ class Command: 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. + submodules_ok: whether to allow submodules. True allows them for + all projects, False disallows them for all projects, and None + defers to each project's sync-s setting. all_manifests: a boolean, if True then all manifests and submanifests are used. If False, then only the local (sub)manifest is used. @@ -403,6 +410,11 @@ class Command: all_projects_list = manifest.projects result = [] + def should_include_submodules(project: "Project") -> bool: + if submodules_ok is None: + return project.sync_s + return submodules_ok + if not groups: groups = manifest.GetManifestGroupsStr() groups = [x for x in re.split(r"[,\s]+", groups) if x] @@ -410,7 +422,7 @@ class Command: if not args: derived_projects = {} for project in all_projects_list: - if submodules_ok or project.sync_s: + if should_include_submodules(project): derived_projects.update( (p.RelPath(local=False), p) for p in project.GetDerivedSubprojects() @@ -452,7 +464,7 @@ class Command: if ( project and not project.Derived - and (submodules_ok or project.sync_s) + and should_include_submodules(project) ): search_again = False for subproject in project.GetDerivedSubprojects(): diff --git a/completion.zsh b/completion.zsh index 507ed2bb2..8c0322f77 100644 --- a/completion.zsh +++ b/completion.zsh @@ -323,6 +323,7 @@ _repo() { '(-u --manifest-server-username)'{-u,--manifest-server-username=}'[Username for manifest server]:username:' \ '(-p --manifest-server-password)'{-p,--manifest-server-password=}'[Password for manifest server]:password:' \ '--fetch-submodules[Fetch submodules]' \ + '--no-fetch-submodules[Do not fetch submodules]' \ '--use-superproject[Use superproject]' \ '--no-use-superproject[Do not use superproject]' \ '--tags[Sync tags]' \ diff --git a/man/repo-smartsync.1 b/man/repo-smartsync.1 index ef4c112df..07bab536b 100644 --- a/man/repo-smartsync.1 +++ b/man/repo-smartsync.1 @@ -83,6 +83,9 @@ password to authenticate with the manifest server \fB\-\-fetch\-submodules\fR fetch submodules from server .TP +\fB\-\-no\-fetch\-submodules\fR +don't fetch submodules from server +.TP \fB\-\-use\-superproject\fR use the manifest superproject to sync projects; implies \fB\-c\fR .TP diff --git a/man/repo-sync.1 b/man/repo-sync.1 index 43d959500..68d1e5e6a 100644 --- a/man/repo-sync.1 +++ b/man/repo-sync.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man. -.TH REPO "1" "June 2026" "repo sync" "Repo Manual" +.TH REPO "1" "July 2026" "repo sync" "Repo Manual" .SH NAME repo \- repo sync - manual page for repo sync .SH SYNOPSIS @@ -83,6 +83,9 @@ password to authenticate with the manifest server \fB\-\-fetch\-submodules\fR fetch submodules from server .TP +\fB\-\-no\-fetch\-submodules\fR +don't fetch submodules from server +.TP \fB\-\-use\-superproject\fR use the manifest superproject to sync projects; implies \fB\-c\fR .TP @@ -212,8 +215,9 @@ bootstrap a new Git repository from a resumeable bundle file on a content delivery network. This may be necessary if there are problems with the local Python HTTP client or proxy configuration, but the Git binary works. .PP -The \fB\-\-fetch\-submodules\fR option enables fetching Git submodules of a project from -server. +The \fB\-\-fetch\-submodules\fR option enables fetching Git submodules of all projects +from the server. The \fB\-\-no\-fetch\-submodules\fR option disables fetching Git +submodules, even when a project has sync\-s="true" in the manifest. .PP The \fB\-c\fR/\-\-current\-branch option can be used to only fetch objects that are on the branch specified by a project's revision. diff --git a/subcmds/sync.py b/subcmds/sync.py index 2b9cafe2d..5a635fa97 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py @@ -379,7 +379,8 @@ may be necessary if there are problems with the local Python HTTP client or proxy configuration, but the Git binary works. The --fetch-submodules option enables fetching Git submodules -of a project from server. +of all projects from the server. The --no-fetch-submodules option disables +fetching Git submodules, even when a project has sync-s="true" in the manifest. The -c/--current-branch option can be used to only fetch objects that are on the branch specified by a project's revision. @@ -573,6 +574,12 @@ later is required to fix a server side protocol bug. action="store_true", help="fetch submodules from server", ) + p.add_option( + "--no-fetch-submodules", + dest="fetch_submodules", + action="store_false", + help="don't fetch submodules from server", + ) p.add_option( "--use-superproject", action="store_true", diff --git a/tests/test_command.py b/tests/test_command.py index bd2a47f71..d7938c5f2 100644 --- a/tests/test_command.py +++ b/tests/test_command.py @@ -14,6 +14,8 @@ """Unittests for the command.py module.""" +import pytest + from command import Command @@ -86,3 +88,32 @@ def test_get_projects_keeps_derived_subprojects_for_repeated_repo(): projects = cmd.GetProjects([]) assert set(projects) == {project_a, project_b, submodule_a, submodule_b} + + +@pytest.mark.parametrize( + "submodules_ok, sync_s, includes_submodule", + [ + (None, False, False), + (None, True, True), + (True, False, True), + (True, True, True), + (False, False, False), + (False, True, False), + ], +) +def test_get_projects_submodule_override( + submodules_ok, sync_s, includes_submodule +): + """The CLI override takes precedence over a project's sync-s setting.""" + submodule = FakeProject("submodule", "project/submodule") + project = FakeProject( + "project", + "project", + derived_subprojects=[submodule], + sync_s=sync_s, + ) + cmd = Command(manifest=FakeManifest([project])) + + projects = cmd.GetProjects([], submodules_ok=submodules_ok) + + assert (submodule in projects) is includes_submodule diff --git a/tests/test_subcmds_sync.py b/tests/test_subcmds_sync.py index f8588465c..1fb462a60 100644 --- a/tests/test_subcmds_sync.py +++ b/tests/test_subcmds_sync.py @@ -30,6 +30,23 @@ from project import SyncNetworkHalfResult from subcmds import sync +@pytest.mark.parametrize( + "cli_args, expected", + [ + ([], None), + (["--fetch-submodules"], True), + (["--no-fetch-submodules"], False), + ], +) +def test_fetch_submodules_option(cli_args, expected): + """The fetch-submodules flags preserve an unset manifest-driven state.""" + cmd = sync.Sync() + + opts, _ = cmd.OptionParser.parse_args(cli_args) + + assert opts.fetch_submodules is expected + + @pytest.mark.parametrize( "use_superproject, cli_args, result", [