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."""