diff --git a/swh/loader/svn/replay.py b/swh/loader/svn/replay.py --- a/swh/loader/svn/replay.py +++ b/swh/loader/svn/replay.py @@ -555,15 +555,21 @@ prev_externals = self.dir_states[self.path].externals if self.externals: - # externals definition list might have changed in the current processed + # externals definition list might have changed in the current replayed # revision, we need to determine if some were removed and delete the # associated paths + externals = self.externals old_externals = set(prev_externals) - set(self.externals) for old_external in old_externals: self.remove_external_path(os.fsencode(old_external)) + else: + # some external paths might have been removed in the current replayed + # revision by a delete operation on an overlapping versioned path so we + # need to restore them + externals = prev_externals # For each external, try to export it in reconstructed filesystem - for path, (external_url, revision, relative_url) in self.externals.items(): + for path, (external_url, revision, relative_url) in externals.items(): external = (external_url, revision) dest_path = os.fsencode(path) dest_fullpath = os.path.join(self.path, dest_path) diff --git a/swh/loader/svn/tests/test_loader.py b/swh/loader/svn/tests/test_loader.py --- a/swh/loader/svn/tests/test_loader.py +++ b/swh/loader/svn/tests/test_loader.py @@ -2460,3 +2460,66 @@ check_snapshot(loader.snapshot, loader.storage) assert (external_url, None) in loader.svnrepo.swhreplay.editor.externals_cache + + +def test_loader_remove_versioned_path_with_external_overlap( + swh_storage, repo_url, external_repo_url, tmp_path +): + # first commit on external + add_commit( + external_repo_url, + "Create a file in an external repository", + [ + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="code/hello.sh", + data=b"#!/bin/bash\necho Hello World !", + ), + ], + ) + + # first commit + add_commit( + repo_url, + "Add a file", + [ + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="trunk/project/script.sh", + data=b"#!/bin/bash\necho foo", + ), + ], + ) + + # second commit + add_commit( + repo_url, + "Set external on trunk overlapping versioned path", + [ + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="trunk/", + properties={ + "svn:externals": ( + f"{svn_urljoin(external_repo_url, 'code')} project/code" + ) + }, + ), + ], + ) + + # third commit + add_commit( + repo_url, + "Remove trunk/project/ versioned path", + [CommitChange(change_type=CommitChangeType.Delete, path="trunk/project/",),], + ) + + loader = SvnLoader( + swh_storage, repo_url, temp_directory=tmp_path, check_revision=1, + ) + assert loader.load() == {"status": "eventful"} + assert_last_visit_matches( + loader.storage, repo_url, status="full", type="svn", + ) + check_snapshot(loader.snapshot, loader.storage)