From 2b535613cbef3b808c6ab97206a115ee3f7ecd7f Mon Sep 17 00:00:00 2001 From: Saurav Prakash Date: Thu, 17 Oct 2024 12:09:34 -0700 Subject: [PATCH] Handle redirection symlink in windows: Handle `eden rm` to not restrict redirected path deletion Summary: # Background We have seen tools like Unity messing up with already existing redirection symlink by deleting and/or creating a folder/file instead of symlink. # Solution When a create/delete operation is triggered for a redirected path, handle it in a way such that redirection stay intact. # This change Not letting redirected path gets deleted will cause eden rm to fail (Ref: D64366283). Removing all redirection target entries from the checkout config for eden rm to proceed. *There could be a cornercase scenario of partial success of eden rm where redirection targets might get deleted but user can still use the repo (eden rm did not succeed). In this case, normal source control operations should work as it is. Only this feature would not work as expected, which can be fixed by running `eden redirect fixup` or `eden doctor` as it is today Differential Revision: D64424509 fbshipit-source-id: 9a6cf7c9be7fb29e8a0ffaf2a1d999b374e7299a --- eden/fs/cli/config.py | 5 +++++ eden/fs/cli/doctor/test/lib/fake_eden_instance.py | 1 + eden/fs/cli/main.py | 8 ++++++++ eden/fs/cli/test/cli_test.py | 10 ++++++++++ 4 files changed, 24 insertions(+) diff --git a/eden/fs/cli/config.py b/eden/fs/cli/config.py index 81a58ac51f393..225cc805dc09b 100644 --- a/eden/fs/cli/config.py +++ b/eden/fs/cli/config.py @@ -191,6 +191,8 @@ class CheckoutConfig(typing.NamedTuple): - redirections: dict where keys are relative pathnames in the EdenFS mount and the values are RedirectionType enum values that describe the type of the redirection. + - redirection_targets: dict where keys are relative pathnames in the EdenFS mount + and the values are target path for corresponding symlink. """ backing_repo: Path @@ -201,6 +203,7 @@ class CheckoutConfig(typing.NamedTuple): require_utf8_path: bool default_revision: str redirections: Dict[str, "RedirectionType"] + redirection_targets: Dict[str, str] active_prefetch_profiles: List[str] predictive_prefetch_profiles_active: bool predictive_prefetch_num_dirs: int @@ -1530,6 +1533,7 @@ def get_field(key: str) -> str: require_utf8_path=require_utf8_path, mount_protocol=mount_protocol, redirections=redirections, + redirection_targets={}, # There is no need to read redirection targets in Python CLI for now. default_revision=( repository.get("default-revision") or DEFAULT_REVISION[scm_type] ), @@ -2140,6 +2144,7 @@ def create_checkout_config( require_utf8_path=True, default_revision=DEFAULT_REVISION[repo.type], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, diff --git a/eden/fs/cli/doctor/test/lib/fake_eden_instance.py b/eden/fs/cli/doctor/test/lib/fake_eden_instance.py index bf3a76e8e0dda..a0ae85c498b6b 100644 --- a/eden/fs/cli/doctor/test/lib/fake_eden_instance.py +++ b/eden/fs/cli/doctor/test/lib/fake_eden_instance.py @@ -138,6 +138,7 @@ def create_test_mount( require_utf8_path=True, default_revision=snapshot, redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, diff --git a/eden/fs/cli/main.py b/eden/fs/cli/main.py index d4df9485afe0e..e2ae810762080 100644 --- a/eden/fs/cli/main.py +++ b/eden/fs/cli/main.py @@ -1716,6 +1716,14 @@ def run(self, args: argparse.Namespace) -> int: exit_code = 0 for mount, remove_type in mounts: print(f"Removing {mount}...") + # Removing reidrection targets from checkout config to allow deletion of redirected paths + instance, checkout, _rel_path = require_checkout(args, mount) + config = checkout.get_config() + config._replace( + redirection_targets={}, + ) + checkout.save_config(config) + if remove_type == RemoveType.ACTIVE_MOUNT: try: # We don't bother complaining about removing redirections on Windows diff --git a/eden/fs/cli/test/cli_test.py b/eden/fs/cli/test/cli_test.py index 8a4c348e04975..42f636de2c239 100644 --- a/eden/fs/cli/test/cli_test.py +++ b/eden/fs/cli/test/cli_test.py @@ -84,6 +84,7 @@ def test_list_mounts_no_backing_repos(self) -> None: require_utf8_path=True, default_revision=DEFAULT_REVISION["hg"], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, @@ -110,6 +111,7 @@ def test_list_mounts_no_backing_repos(self) -> None: require_utf8_path=True, default_revision=DEFAULT_REVISION["git"], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, @@ -136,6 +138,7 @@ def test_list_mounts_no_backing_repos(self) -> None: require_utf8_path=True, default_revision=DEFAULT_REVISION["git"], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, @@ -162,6 +165,7 @@ def test_list_mounts_no_backing_repos(self) -> None: require_utf8_path=True, default_revision=DEFAULT_REVISION["git"], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, @@ -188,6 +192,7 @@ def test_list_mounts_no_backing_repos(self) -> None: require_utf8_path=True, default_revision=DEFAULT_REVISION["hg"], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, @@ -315,6 +320,7 @@ def test_list_mounts_no_state(self) -> None: require_utf8_path=True, default_revision=DEFAULT_REVISION["hg"], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, @@ -341,6 +347,7 @@ def test_list_mounts_no_state(self) -> None: require_utf8_path=True, default_revision=DEFAULT_REVISION["git"], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, @@ -367,6 +374,7 @@ def test_list_mounts_no_state(self) -> None: require_utf8_path=True, default_revision=DEFAULT_REVISION["hg"], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, @@ -485,6 +493,7 @@ def test_list_mounts_with_backing_repos(self) -> None: require_utf8_path=True, default_revision=DEFAULT_REVISION["hg"], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, @@ -511,6 +520,7 @@ def test_list_mounts_with_backing_repos(self) -> None: require_utf8_path=True, default_revision=DEFAULT_REVISION["git"], redirections={}, + redirection_targets={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0,