mirror of
https://gerrit.googlesource.com/git-repo
synced 2026-05-31 15:09:48 +00:00
linkfile: Handle directory-to-symlink transitions safely
When a manifest changes from individual linkfiles inside a directory
(e.g. dest=".llms/rules", dest=".llms/skills") to a single linkfile
for the whole directory (e.g. dest=".llms", src="dot-llms"), two
things need to happen:
1. __linkIt must replace a real directory with a symlink. Use
os.rmdir() instead of platform_utils.remove() for real directories.
rmdir only removes empty directories, so user-created content is
never deleted.
2. UpdateCopyLinkfileList must handle the cleanup correctly:
- Use os.rmdir() for directories (safe for non-empty)
- Remove empty parent directories after cleaning old dests
- Retry _CopyAndLinkFiles for all projects, since in interleaved
sync mode _CopyAndLinkFiles runs before cleanup and may have
failed because the directory was not yet empty
Change-Id: I0437b80beab98bce064cea81c11c47d699be91aa
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/569243
Tested-by: Carlos Fernandez <carlosfsanz@meta.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
Reviewed-by: Gavin Mak <gavinmak@google.com>
Commit-Queue: Carlos Fernandez <carlosfsanz@meta.com>
This commit is contained in:
committed by
gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
67e52a120b
commit
5534f164d6
+27
-7
@@ -1631,9 +1631,10 @@ later is required to fix a server side protocol bug.
|
||||
new_paths = {}
|
||||
new_linkfile_paths = []
|
||||
new_copyfile_paths = []
|
||||
for project in self.GetProjects(
|
||||
projects = self.GetProjects(
|
||||
None, missing_ok=True, manifest=manifest, all_manifests=False
|
||||
):
|
||||
)
|
||||
for project in projects:
|
||||
new_linkfile_paths.extend(x.dest for x in project.linkfiles)
|
||||
new_copyfile_paths.extend(x.dest for x in project.copyfiles)
|
||||
|
||||
@@ -1669,17 +1670,36 @@ later is required to fix a server side protocol bug.
|
||||
)
|
||||
|
||||
for need_remove_file in need_remove_files:
|
||||
# Try to remove the updated copyfile or linkfile.
|
||||
# So, if the file is not exist, nothing need to do.
|
||||
platform_utils.remove(
|
||||
os.path.join(self.client.topdir, need_remove_file),
|
||||
missing_ok=True,
|
||||
need_remove_path = os.path.join(
|
||||
self.client.topdir, need_remove_file
|
||||
)
|
||||
if os.path.isfile(need_remove_path):
|
||||
platform_utils.remove(need_remove_path)
|
||||
else:
|
||||
platform_utils.removedirs(need_remove_path)
|
||||
|
||||
# Also try to remove empty parent directories.
|
||||
parent = os.path.dirname(need_remove_path)
|
||||
while parent != self.client.topdir:
|
||||
try:
|
||||
os.rmdir(parent)
|
||||
except OSError:
|
||||
break
|
||||
parent = os.path.dirname(parent)
|
||||
|
||||
# Create copy-link-files.json, save dest path of "copyfile" and
|
||||
# "linkfile".
|
||||
with open(copylinkfile_path, "w", encoding="utf-8") as fp:
|
||||
json.dump(new_paths, fp)
|
||||
|
||||
# Retry linkfile/copyfile creation for all projects. In
|
||||
# interleaved sync mode, _CopyAndLinkFiles runs before this
|
||||
# cleanup, so linkfiles whose dest was blocked by an old
|
||||
# directory may have failed. _CopyAndLinkFiles is idempotent
|
||||
# and skips dests that are already correct.
|
||||
for project in projects:
|
||||
project._CopyAndLinkFiles()
|
||||
|
||||
return True
|
||||
|
||||
def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
|
||||
|
||||
Reference in New Issue
Block a user