project: Use dicts to keep track of copyfiles and linkfiles

This avoids copying/linking the same file/link multiple times if a
copyfile/linkfile element with the same values has been specifed
multiple times. This can happen when including a common manifest that
uses an extend-project element that has a copyfile/linkfile element.

This uses dicts rather than sets to store the copyfiles and linkfiles to
make sure the order they are specified in the manifest is maintained.
For Python 3.7+, maintaining the order that keys are added to dicts is
guaranteed, and for Python 3.6 it happened to be true.

The _CopyFile class and the _LinkFile class are changed to inherit from
NamedTuple to be able to store them in dicts.

Change-Id: I9f5a80298b875251a81c5fe7d353e262d104fae4
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/525322
Reviewed-by: Mike Frysinger <vapier@google.com>
Reviewed-by: Gavin Mak <gavinmak@google.com>
Tested-by: Peter Kjellerstedt <peter.kjellerstedt@axis.com>
Commit-Queue: Peter Kjellerstedt <peter.kjellerstedt@axis.com>
This commit is contained in:
Peter Kjellerstedt
2025-11-08 00:06:16 +01:00
committed by LUCI
parent 47c24b5c40
commit 412367bfaf
4 changed files with 131 additions and 40 deletions

View File

@@ -390,22 +390,17 @@ def _SafeExpandPath(base, subpath, skipfinal=False):
return path
class _CopyFile:
class _CopyFile(NamedTuple):
"""Container for <copyfile> manifest element."""
def __init__(self, git_worktree, src, topdir, dest):
"""Register a <copyfile> request.
Args:
git_worktree: Absolute path to the git project checkout.
src: Relative path under |git_worktree| of file to read.
topdir: Absolute path to the top of the repo client checkout.
dest: Relative path under |topdir| of file to write.
"""
self.git_worktree = git_worktree
self.topdir = topdir
self.src = src
self.dest = dest
# Absolute path to the git project checkout.
git_worktree: str
# Relative path under |git_worktree| of file to read.
src: str
# Absolute path to the top of the repo client checkout.
topdir: str
# Relative path under |topdir| of file to write.
dest: str
def _Copy(self):
src = _SafeExpandPath(self.git_worktree, self.src)
@@ -439,22 +434,17 @@ class _CopyFile:
logger.error("error: Cannot copy file %s to %s", src, dest)
class _LinkFile:
class _LinkFile(NamedTuple):
"""Container for <linkfile> manifest element."""
def __init__(self, git_worktree, src, topdir, dest):
"""Register a <linkfile> request.
Args:
git_worktree: Absolute path to the git project checkout.
src: Target of symlink relative to path under |git_worktree|.
topdir: Absolute path to the top of the repo client checkout.
dest: Relative path under |topdir| of symlink to create.
"""
self.git_worktree = git_worktree
self.topdir = topdir
self.src = src
self.dest = dest
# Absolute path to the git project checkout.
git_worktree: str
# Target of symlink relative to path under |git_worktree|.
src: str
# Absolute path to the top of the repo client checkout.
topdir: str
# Relative path under |topdir| of symlink to create.
dest: str
def __linkIt(self, relSrc, absDest):
# Link file if it does not exist or is out of date.
@@ -633,8 +623,9 @@ class Project:
self.subprojects = []
self.snapshots = {}
self.copyfiles = []
self.linkfiles = []
# Use dicts to dedupe while maintaining declared order.
self.copyfiles = {}
self.linkfiles = {}
self.annotations = []
self.dest_branch = dest_branch
@@ -1794,7 +1785,7 @@ class Project:
Paths should have basic validation run on them before being queued.
Further checking will be handled when the actual copy happens.
"""
self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
self.copyfiles[_CopyFile(self.worktree, src, topdir, dest)] = True
def AddLinkFile(self, src, dest, topdir):
"""Mark |dest| to create a symlink (relative to |topdir|) pointing to
@@ -1805,7 +1796,7 @@ class Project:
Paths should have basic validation run on them before being queued.
Further checking will be handled when the actual link happens.
"""
self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
self.linkfiles[_LinkFile(self.worktree, src, topdir, dest)] = True
def AddAnnotation(self, name, value, keep):
self.annotations.append(Annotation(name, value, keep))