sync: keep local sync state paths relative to repo root

`LocalSyncState` uses `Project.relpath` to identify projects, but that
path is relative to each project's own manifest. Projects in different
manifests can have the same relpath, so their fetch and checkout
timestamps overwrite each other. This can make a later partial sync look
like a full sync.

Pruning has a related problem. It treats the stored paths as relative to
the current manifest's topdir, so when sync starts from the outermost
manifest it can look for submanifest projects in the wrong place and
remove valid state.

Use `Project.RelPath(local=False)` to identify projects in local sync
state and check stored paths from the repo root when pruning.

Add tests for same-relpath projects, partial-sync detection, and pruning
when sync starts from either the outermost manifest or a submanifest.

Change-Id: I24485c315f1277a14913443674245cedd20820fe
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/632421
Reviewed-by: Gavin Mak <gavinmak@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: Victor Pushkarev <corvinus.v@gmail.com>
Commit-Queue: Victor Pushkarev <corvinus.v@gmail.com>
This commit is contained in:
Victor Pushkarev authored and gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com committed 2026-09-26 01:17:45 -07:00
1 parent 9066409c2d
commit ccf9ce39b8
2 files changed
+83 -15

No files matched your search

+4 -3
View File
@@ -3365,13 +3365,13 @@ class LocalSyncState:
def _Get(self, project, key):
self._Load()
p = project.relpath
p = project.RelPath(local=False)
if p not in self._state:
return
return self._state[p].get(key)
def _Set(self, project, key):
p = project.relpath
p = project.RelPath(local=False)
if p not in self._state:
self._state[p] = {}
self._state[p][key] = self._time
@@ -3399,8 +3399,9 @@ class LocalSyncState:
if not self._state:
return
delete = set()
outer_topdir = self._manifest.outer_client.topdir
for path in self._state:
gitdir = os.path.join(self._manifest.topdir, path, ".git")
gitdir = os.path.join(outer_topdir, path, ".git")
if not os.path.exists(gitdir) or os.path.islink(gitdir):
delete.add(path)
if not delete: