From 00991bfb42c4f3b0b7a50aa8f475ac1c8369924b Mon Sep 17 00:00:00 2001 From: Gavin Mak Date: Wed, 1 Apr 2026 23:03:03 +0000 Subject: [PATCH] manifest: Add `sync-strategy` attribute to project elements The only supported sync-strategy is "stateless". The intent is to keep the local workspace as small as possible by not keeping history during syncs. This prevents disk space waste for projects with large binaries where we only care about the current version. A follow up change will implement the logic. Bug: 498730431 Change-Id: I84a436a9ca2492893163c6cfda6c28dc62a568f0 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/568462 Tested-by: Gavin Mak Reviewed-by: Mike Frysinger Commit-Queue: Gavin Mak --- docs/manifest-format.md | 41 ++++++++++++++++++++++--------- man/repo-manifest.1 | 50 +++++++++++++++++++++++++++++++------- manifest_xml.py | 6 +++++ project.py | 2 ++ tests/test_manifest_xml.py | 23 ++++++++++++++++++ 5 files changed, 101 insertions(+), 21 deletions(-) diff --git a/docs/manifest-format.md b/docs/manifest-format.md index f0149dd80..42fb1bfe2 100644 --- a/docs/manifest-format.md +++ b/docs/manifest-format.md @@ -73,18 +73,19 @@ following DTD: project*, copyfile*, linkfile*)> - - - - - - - - - - - - + + + + + + + + + + + + + @@ -389,6 +390,22 @@ rather than the `name` attribute. This attribute only applies to the local mirrors syncing, it will be ignored when syncing the projects in a client working directory. +Attribute `sync-strategy`: Set the sync strategy used when fetching this +project. Currently the only supported value is `stateless`. When set to +`stateless`, repo will run a reflog expiration and aggressive garbage collection +at the end of the sync process. This is useful for projects that contain +large binary files and use `clone-depth="1"`, where garbage can accumulate +as binaries are added, deleted, or modified across successive syncs. + +During a stateless sync, repo checks the following before cleaning up: +1. The project does not share an object directory with other projects. +2. The working tree is clean (no uncommitted changes, no untracked files). +3. There are no unpushed local commits. +4. There is no Git stash. + +If any of these conditions are not met, repo falls back to a standard +sync without garbage collection. + ### Element extend-project Modify the attributes of the named project. diff --git a/man/repo-manifest.1 b/man/repo-manifest.1 index 4d74fde89..75c9fa9e1 100644 --- a/man/repo-manifest.1 +++ b/man/repo-manifest.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man. -.TH REPO "1" "March 2026" "repo manifest" "Repo Manual" +.TH REPO "1" "April 2026" "repo manifest" "Repo Manual" .SH NAME repo \- repo manifest - manual page for repo manifest .SH SYNOPSIS @@ -165,15 +165,32 @@ IDREF #IMPLIED> .TP +.TP + +.TP + +.TP + +.TP + +.TP + +.TP + +.TP + +.TP + .IP - - - - - - - - + .IP @@ -469,6 +486,21 @@ mirror repository according to its `path` attribute (if supplied) rather than the `name` attribute. This attribute only applies to the local mirrors syncing, it will be ignored when syncing the projects in a client working directory. .PP +Attribute `sync\-strategy`: Set the sync strategy used when fetching this +project. Currently the only supported value is `stateless`. When set to +`stateless`, repo will run a reflog expiration and aggressive garbage collection +at the end of the sync process. This is useful for projects that contain large +binary files and use `clone\-depth="1"`, where garbage can accumulate as binaries +are added, deleted, or modified across successive syncs. +.PP +During a stateless sync, repo checks the following before cleaning up: 1. The +project does not share an object directory with other projects. 2. The working +tree is clean (no uncommitted changes, no untracked files). 3. There are no +unpushed local commits. 4. There is no Git stash. +.PP +If any of these conditions are not met, repo falls back to a standard sync +without garbage collection. +.PP Element extend\-project .PP Modify the attributes of the named project. diff --git a/manifest_xml.py b/manifest_xml.py index c9bc6848b..5dc9d2fe9 100644 --- a/manifest_xml.py +++ b/manifest_xml.py @@ -759,6 +759,9 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md if p.clone_depth: e.setAttribute("clone-depth", str(p.clone_depth)) + if p.sync_strategy: + e.setAttribute("sync-strategy", str(p.sync_strategy)) + self._output_manifest_project_extras(p, e) if p.subprojects: @@ -1938,6 +1941,8 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md % (self.manifestFile, clone_depth) ) + sync_strategy = node.getAttribute("sync-strategy") or None + dest_branch = ( node.getAttribute("dest-branch") or self._default.destBranchExpr ) @@ -1984,6 +1989,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md sync_s=sync_s, sync_tags=sync_tags, clone_depth=clone_depth, + sync_strategy=sync_strategy, upstream=upstream, parent=parent, dest_branch=dest_branch, diff --git a/project.py b/project.py index 11b53c33c..1766c9a07 100644 --- a/project.py +++ b/project.py @@ -558,6 +558,7 @@ class Project: sync_s=False, sync_tags=True, clone_depth=None, + sync_strategy=None, upstream=None, parent=None, use_git_worktrees=False, @@ -610,6 +611,7 @@ class Project: self.sync_s = sync_s self.sync_tags = sync_tags self.clone_depth = clone_depth + self.sync_strategy = sync_strategy self.upstream = upstream self.parent = parent # NB: Do not use this setting in __init__ to change behavior so that the diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py index 473f781ba..c7352f89a 100644 --- a/tests/test_manifest_xml.py +++ b/tests/test_manifest_xml.py @@ -732,6 +732,29 @@ class TestProjectElement: "" ) + def test_sync_strategy(self, repo_client: RepoClient) -> None: + """Check setting of project's sync_strategy.""" + manifest = repo_client.get_xml_manifest( + """ + + + + + +""" + ) + assert len(manifest.projects) == 1 + project = manifest.projects[0] + assert project.sync_strategy == "stateless" + assert ( + sort_attributes(manifest.ToXml().toxml()) + == '' + '' + '' + '' + "" + ) + def test_trailing_slash(self, repo_client: RepoClient) -> None: """Check handling of trailing slashes in attributes."""