diff --git a/docs/design.md b/docs/design.md --- a/docs/design.md +++ b/docs/design.md @@ -131,6 +131,8 @@ ### `snp` nodes (snapshots) +TODO: update description + Snapshot nodes are represented on the file-system as directories with one entry for each branch in the snapshot. diff --git a/swh/fuse/fs/artifact.py b/swh/fuse/fs/artifact.py --- a/swh/fuse/fs/artifact.py +++ b/swh/fuse/fs/artifact.py @@ -9,7 +9,6 @@ import logging from pathlib import Path from typing import Any, AsyncIterator, Dict, List -import urllib.parse from swh.fuse.fs.entry import ( EntryMode, @@ -461,24 +460,43 @@ Attributes: swhid: Software Heritage persistent identifier - Snapshot nodes are represented on the file-system as directories with one - entry for each branch in the snapshot. Each entry is a symlink pointing into - `archive/` to the branch target SWHID. Branch names are URL encoded (hence - '/' are replaced with '%2F'). """ + Snapshot nodes are represented on the file-system as recursive directories + following the branch names structure. For example, a branch named + ``refs/tags/v1.0`` will be represented as a ``refs`` directory containing a + ``tags`` directory containing a ``v1.0`` symlink pointing to the branch + target SWHID. """ swhid: SWHID + prefix: str = field(default="") async def compute_entries(self) -> AsyncIterator[FuseEntry]: metadata = await self.fuse.get_metadata(self.swhid) root_path = self.get_relative_root_path() + subdirs = set() for branch_name, branch_meta in metadata.items(): - # Mangle branch name to create a valid UNIX filename - name = urllib.parse.quote_plus(branch_name) + if not branch_name.startswith(self.prefix): + continue + + next_subdirs = branch_name[len(self.prefix) :].split("/") + next_prefix = next_subdirs[0] + + if len(next_subdirs) == 1: + yield self.create_child( + FuseSymlinkEntry, + name=next_prefix, + target=Path(root_path, f"archive/{branch_meta['target']}"), + ) + else: + subdirs.add(next_prefix) + + for subdir in subdirs: yield self.create_child( - FuseSymlinkEntry, - name=name, - target=Path(root_path, f"archive/{branch_meta['target']}"), + Snapshot, + name=subdir, + mode=int(EntryMode.RDONLY_DIR), + swhid=self.swhid, + prefix=f"{self.prefix}{subdir}/", ) diff --git a/swh/fuse/tests/test_snapshot.py b/swh/fuse/tests/test_snapshot.py --- a/swh/fuse/tests/test_snapshot.py +++ b/swh/fuse/tests/test_snapshot.py @@ -1,22 +1,19 @@ import os -import urllib.parse +from pathlib import Path from swh.fuse.tests.common import get_data_from_web_archive from swh.fuse.tests.data.config import ROOT_SNP def test_list_branches(fuse_mntdir): + snp_dir = Path(fuse_mntdir / "archive" / ROOT_SNP) snp_meta = get_data_from_web_archive(ROOT_SNP) - expected = snp_meta.keys() - # Mangle branch name to create a valid UNIX filename - expected = [urllib.parse.quote_plus(x) for x in expected] - actual = os.listdir(fuse_mntdir / "archive" / ROOT_SNP) - assert set(actual) == set(expected) + for branch_name in snp_meta.keys(): + assert (snp_dir / branch_name).is_symlink() def test_access_rev_target(fuse_mntdir): - branch_name = urllib.parse.quote_plus("refs/heads/master") - dir_path = fuse_mntdir / "archive" / ROOT_SNP / branch_name + dir_path = fuse_mntdir / "archive" / ROOT_SNP / "refs/heads/master" expected = set(["meta.json", "root", "parent", "parents", "history"]) actual = set(os.listdir(dir_path)) assert expected.issubset(actual)