diff --git a/swh/loader/svn/ra.py b/swh/loader/svn/ra.py --- a/swh/loader/svn/ra.py +++ b/swh/loader/svn/ra.py @@ -136,9 +136,13 @@ svn_special_path_non_link_data: Optional[bytes] = None """keep track of non link file content with svn:special property set""" + # default value: 0, 1: set the flag, 2: remove the exec flag executable: int = DEFAULT_FLAG """keep track if file is executable when setting svn:executable property""" + link: bool = False + """keep track if file is a svn link when setting svn:special property""" + class FileEditor: """File Editor in charge of updating file on disk and memory objects. @@ -157,8 +161,6 @@ def __init__(self, directory, rootpath, path, state: FileState): self.directory = directory self.path = path - # default value: 0, 1: set the flag, 2: remove the exec flag - self.link = None self.fullpath = os.path.join(rootpath, path) self.state = state @@ -171,7 +173,7 @@ elif key == properties.PROP_SPECIAL: # Possibly a symbolic link. We cannot check further at # that moment though, patch(s) not being applied yet - self.link = value is not None + self.state.link = value is not None elif key == SVN_PROPERTY_EOL: # backup end of line style for file self.state.eol_style = value @@ -217,7 +219,7 @@ # real svn symlink for potential patching in later # commits sbuf = self.__make_svnlink() - self.link = True + self.state.link = True else: with open(self.fullpath, "rb") as f: sbuf = f.read() @@ -238,16 +240,14 @@ computation purposes) """ - is_link = None - if self.link: + if self.state.link: # can only check now that the link is a real one # since patch has been applied is_link, src = is_file_an_svnlink_p(self.fullpath) if is_link: self.__make_symlink(src) else: # not a real link ... - self.link = False # when a file with the svn:special property set is not a svn link, # the svn export operation will extract a truncated version of that file # if it contains a null byte (see create_special_file_from_stream @@ -273,6 +273,7 @@ f.write(self.state.svn_special_path_non_link_data) self.state.svn_special_path_non_link_data = None + is_link = os.path.islink(self.fullpath) if not is_link: # if a link, do nothing regarding flag if self.state.executable == EXEC_FLAG: os.chmod(self.fullpath, 0o755) 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 @@ -1678,3 +1678,53 @@ assert_last_visit_matches( loader.storage, repo_url, status="full", type="svn", ) + + +def test_loader_svn_add_property_on_link(swh_storage, tmp_path): + # create a repository + repo_path = os.path.join(tmp_path, "tmprepo") + repos.create(repo_path) + repo_url = f"file://{repo_path}" + + # first commit + add_commit( + repo_url, + "Add an executable file and a svn link to it.", + [ + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="hello-world", + properties={"svn:executable": "*"}, + data=b"#!/bin/bash\necho Hello World !", + ), + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="hello", + properties={"svn:special": "*"}, + data=b"link hello-world", + ), + ], + ) + + # second commit + add_commit( + repo_url, + "Set svn:eol-style property on link", + [ + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="hello", + properties={"svn:eol-style": "native"}, + ), + ], + ) + + # instantiate a svn loader checking after each processed revision that + # the repository filesystem it reconstructed does not differ from a subversion + # export of that revision + 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", + )