diff --git a/swh/web/browse/snapshot_context.py b/swh/web/browse/snapshot_context.py
--- a/swh/web/browse/snapshot_context.py
+++ b/swh/web/browse/snapshot_context.py
@@ -9,7 +9,7 @@
from typing import Any, Dict, List, Optional, Tuple
from django.http import HttpRequest, HttpResponse
-from django.shortcuts import render
+from django.shortcuts import redirect, render
from django.utils.html import escape
from swh.model.hashutil import hash_to_bytes
@@ -65,7 +65,7 @@
snapshot_id,
branches_from=branch_name,
branches_count=1,
- target_types=["revision", "alias"],
+ target_types=["revision", "alias", "content", "directory"],
# pull request branches must be browsable even if they are hidden
# by default in branches list
branch_name_exclude_prefix=None,
@@ -127,7 +127,7 @@
if branch_type == "branch":
branch_type = "Branch"
branch_type_plural = "branches"
- target_type = "revision"
+ target_type = "branch"
else:
branch_type = "Release"
branch_type_plural = "releases"
@@ -202,17 +202,19 @@
continue
target_id = target["target"]
target_type = target["target_type"]
- if target_type == "revision":
+ if target_type in ("content", "directory", "revision"):
branches[branch_name] = SnapshotBranchInfo(
name=branch_name,
alias=False,
- revision=target_id,
+ target_type=target_type,
+ target=target_id,
date=None,
directory=None,
message=None,
url=None,
)
- revision_to_branch[target_id].add(branch_name)
+ if target_type == "revision":
+ revision_to_branch[target_id].add(branch_name)
elif target_type == "release":
release_to_branch[target_id].add(branch_name)
elif target_type == "alias":
@@ -237,7 +239,8 @@
branches[branch] = SnapshotBranchInfo(
name=branch,
alias=alias,
- revision=revision["id"],
+ target_type="revision",
+ target=revision["id"],
directory=revision["directory"],
date=format_utc_iso_date(revision["date"]),
message=revision["message"],
@@ -268,7 +271,7 @@
resolved_aliases = {}
- for branch_alias, branch_target in branch_aliases.items():
+ for branch_alias, _ in branch_aliases.items():
resolved_alias = archive.lookup_snapshot_alias(snapshot["id"], branch_alias)
resolved_aliases[branch_alias] = resolved_alias
if resolved_alias is None:
@@ -283,6 +286,17 @@
elif target_type == "release":
release = archive.lookup_release(target)
_add_release_info(branch_alias, release, alias=True)
+ elif target_type in ("content", "directory"):
+ branches[branch_name] = SnapshotBranchInfo(
+ name=branch_alias,
+ alias=True,
+ target_type=target_type,
+ target=target,
+ date=None,
+ directory=None,
+ message=None,
+ url=None,
+ )
if branch_alias in branches:
branches[branch_alias]["name"] = branch_alias
@@ -468,7 +482,7 @@
)
visit_url = reverse("browse-origin-directory", query_params=query_params)
- visit_info["url"] = directory_url = visit_url
+ visit_info["url"] = browse_url = visit_url
branches_url = reverse("browse-origin-branches", query_params=query_params)
@@ -477,7 +491,7 @@
assert snapshot_id is not None
branches, releases, aliases = get_snapshot_content(snapshot_id)
url_args = {"snapshot_id": snapshot_id}
- directory_url = reverse("browse-snapshot-directory", url_args=url_args)
+ browse_url = reverse("browse-snapshot-directory", url_args=url_args)
branches_url = reverse("browse-snapshot-branches", url_args=url_args)
releases_url = reverse("browse-snapshot-releases", url_args=url_args)
@@ -490,7 +504,11 @@
snapshot_sizes = _get_snapshot_sizes(snapshot_id)
- is_empty = (snapshot_sizes["release"] + snapshot_sizes["revision"]) == 0
+ snapshot_total_size = sum(
+ v for k, v in snapshot_sizes.items() if k not in ("alias", "branch")
+ )
+
+ is_empty = snapshot_total_size == 0
swh_snp_id = str(
CoreSWHID(object_type=ObjectType.SNAPSHOT, object_id=hash_to_bytes(snapshot_id))
@@ -507,8 +525,6 @@
release_id = None
root_directory = None
- snapshot_total_size = snapshot_sizes["release"] + snapshot_sizes["revision"]
-
if path is not None:
query_params["path"] = path
@@ -520,7 +536,8 @@
SnapshotBranchInfo(
name=revision_id,
alias=False,
- revision=revision_id,
+ target_type="revision",
+ target=revision_id,
directory=root_directory,
date=revision["date"],
message=revision["message"],
@@ -568,17 +585,21 @@
)
else:
branch_name = branch["name"]
- revision_id = branch["revision"]
+ revision_id = branch["target"]
root_directory = branch["directory"]
elif head is not None:
# otherwise, browse branch targeted by the HEAD alias if it exists
- if head["target_type"] == "revision":
- # HEAD alias targets a revision
- head_rev = archive.lookup_revision(head["target"])
+ if head["target_type"] in ("content", "directory", "revision"):
branch_name = "HEAD"
- revision_id = head_rev["id"]
- root_directory = head_rev["directory"]
- else:
+ if head["target_type"] == "revision":
+ # HEAD alias targets a revision
+ head_rev = archive.lookup_revision(head["target"])
+ revision_id = head_rev["id"]
+ root_directory = head_rev["directory"]
+ elif head["target_type"] == "directory":
+ # HEAD alias targets a directory
+ root_directory = head["target"]
+ elif head["target_type"] == "release":
# HEAD alias targets a release
release_name = archive.lookup_release(head["target"])["name"]
head_rel = _get_release(releases, release_name, snapshot_id)
@@ -604,8 +625,13 @@
# fallback to browse first branch otherwise
branch = branches[0]
branch_name = branch["name"]
- revision_id = branch["revision"]
- root_directory = branch["directory"]
+ revision_id = (
+ branch["target"] if branch["target_type"] == "revision" else None
+ )
+ if branch["target_type"] == "revision":
+ root_directory = branch["directory"]
+ elif branch["target_type"] == "directory":
+ root_directory = branch["target"]
elif releases:
# fallback to browse last release otherwise
release = releases[-1]
@@ -621,7 +647,7 @@
for b in branches:
branch_query_params = dict(query_params)
branch_query_params.pop("release", None)
- if b["name"] != b["revision"]:
+ if b["name"] != b["target"]:
branch_query_params.pop("revision", None)
branch_query_params["branch"] = b["name"]
b["url"] = reverse(
@@ -657,7 +683,7 @@
revision_info["message_header"] = ""
snapshot_context = SnapshotContext(
- directory_url=directory_url,
+ browse_url=browse_url,
branch=branch_name,
branch_alias=branch_name in aliases,
branches=branches,
@@ -759,6 +785,27 @@
)
root_directory = snapshot_context["root_directory"]
+
+ if root_directory is None and snapshot_context["branch"] is not None:
+ branch_info = [
+ branch
+ for branch in snapshot_context["branches"]
+ if branch["name"] == snapshot_context["branch"]
+ ]
+ # special case where the branch to browse targets a content instead of a directory
+ if branch_info and branch_info[0]["target_type"] == "content":
+ # redirect to browse content view
+ if "origin_url" not in snapshot_context["query_params"]:
+ snapshot_id = snapshot_context["snapshot_id"]
+ snapshot_context["query_params"]["snapshot"] = snapshot_id
+ return redirect(
+ reverse(
+ "browse-content",
+ url_args={"query_string": f"sha1_git:{branch_info[0]['target']}"},
+ query_params=snapshot_context["query_params"],
+ )
+ )
+
sha1_git = root_directory
error_info: Dict[str, Any] = {
"status_code": 200,
@@ -1167,7 +1214,7 @@
revision_url = reverse(
"browse-revision",
- url_args={"sha1_git": branch["revision"]},
+ url_args={"sha1_git": branch["target"]},
query_params=query_params,
)
diff --git a/swh/web/browse/templates/includes/snapshot-context.html b/swh/web/browse/templates/includes/snapshot-context.html
--- a/swh/web/browse/templates/includes/snapshot-context.html
+++ b/swh/web/browse/templates/includes/snapshot-context.html
@@ -37,12 +37,12 @@
-
-
+
Code
- {% if not snapshot_context.snapshot_sizes.revision %}
+ {% if not snapshot_context.snapshot_sizes.branch %}
-
@@ -53,7 +53,7 @@
-
- Branches ({{ snapshot_context.snapshot_sizes.revision}})
+ Branches ({{ snapshot_context.snapshot_sizes.branch}})
{% endif %}
diff --git a/swh/web/browse/templates/includes/top-navigation.html b/swh/web/browse/templates/includes/top-navigation.html
--- a/swh/web/browse/templates/includes/top-navigation.html
+++ b/swh/web/browse/templates/includes/top-navigation.html
@@ -55,11 +55,11 @@
{% endfor %}
- {% if snapshot_context.branches|length < snapshot_context.snapshot_sizes.revision %}
+ {% if snapshot_context.branches|length < snapshot_context.snapshot_sizes.branch %}
-
Branches list truncated to {{ snapshot_context.branches|length }} entries,
- {{ snapshot_context.branches|length|mul:-1|add:snapshot_context.snapshot_sizes.revision }}
+ {{ snapshot_context.branches|length|mul:-1|add:snapshot_context.snapshot_sizes.branch }}
were omitted.
{% endif %}
diff --git a/swh/web/tests/browse/test_snapshot_context.py b/swh/web/tests/browse/test_snapshot_context.py
--- a/swh/web/tests/browse/test_snapshot_context.py
+++ b/swh/web/tests/browse/test_snapshot_context.py
@@ -42,7 +42,8 @@
SnapshotBranchInfo(
name=branch,
alias=alias,
- revision=branch_data["target"],
+ target_type="revision",
+ target=branch_data["target"],
directory=rev_data["directory"],
date=format_utc_iso_date(rev_data["date"]),
message=rev_data["message"],
@@ -108,7 +109,7 @@
root_directory = None
for branch in branches:
if branch["name"] == "HEAD":
- revision_id = branch["revision"]
+ revision_id = branch["target"]
root_directory = branch["directory"]
branch["url"] = reverse(
f"browse-snapshot-{browse_context}",
@@ -151,7 +152,7 @@
snapshot_swhid=snapshot_swhid,
url_args=url_args,
visit_info=None,
- directory_url=directory_url,
+ browse_url=directory_url,
)
if revision_id:
@@ -213,7 +214,7 @@
root_directory = None
for branch in branches:
if branch["name"] == "HEAD":
- revision_id = branch["revision"]
+ revision_id = branch["target"]
root_directory = branch["directory"]
branch["url"] = reverse(
f"browse-origin-{browse_context}",
@@ -266,7 +267,7 @@
snapshot_swhid=snapshot_swhid,
url_args={},
visit_info=visit_info,
- directory_url=directory_url,
+ browse_url=directory_url,
)
if revision_id:
@@ -307,14 +308,14 @@
expected_branch = dict(base_expected_context)
expected_branch["branch"] = branch["name"]
expected_branch["branch_alias"] = branch["alias"]
- expected_branch["revision_id"] = branch["revision"]
+ expected_branch["revision_id"] = branch["target"]
expected_branch["revision_info"] = _get_revision_info(
- archive_data, branch["revision"]
+ archive_data, branch["target"]
)
expected_branch["root_directory"] = branch["directory"]
expected_branch["query_params"] = {"branch": branch["name"], **query_params}
expected_branch["revision_info"]["revision_url"] = gen_revision_url(
- branch["revision"], expected_branch
+ branch["target"], expected_branch
)
assert snapshot_context == expected_branch
@@ -345,7 +346,7 @@
assert snapshot_context == expected_release
- revision_log = archive_data.revision_log(branch["revision"])
+ revision_log = archive_data.revision_log(branch["target"])
revision = revision_log[-1]
snapshot_context = get_snapshot_context(
@@ -369,7 +370,8 @@
SnapshotBranchInfo(
name=revision["id"],
alias=False,
- revision=revision["id"],
+ target_type="revision",
+ target=revision["id"],
directory=revision["directory"],
date=revision["date"],
message=revision["message"],
diff --git a/swh/web/tests/browse/views/test_content.py b/swh/web/tests/browse/views/test_content.py
--- a/swh/web/tests/browse/views/test_content.py
+++ b/swh/web/tests/browse/views/test_content.py
@@ -447,7 +447,7 @@
branches, releases, _ = process_snapshot_branches(snapshot)
branch_info = random.choice(branches)
- directory = archive_data.revision_get(branch_info["revision"])["directory"]
+ directory = archive_data.revision_get(branch_info["target"])["directory"]
directory_content = archive_data.directory_ls(directory)
directory_file = random.choice(
[e for e in directory_content if e["type"] == "file"]
@@ -480,7 +480,7 @@
metadata={
"origin": origin_url,
"visit": gen_swhid(ObjectType.SNAPSHOT, snapshot["id"]),
- "anchor": gen_swhid(ObjectType.REVISION, branch_info["revision"]),
+ "anchor": gen_swhid(ObjectType.REVISION, branch_info["target"]),
"path": f"/{directory_file['name']}",
},
)
@@ -492,14 +492,14 @@
metadata={
"origin": origin_url,
"visit": gen_swhid(ObjectType.SNAPSHOT, snapshot["id"]),
- "anchor": gen_swhid(ObjectType.REVISION, branch_info["revision"]),
+ "anchor": gen_swhid(ObjectType.REVISION, branch_info["target"]),
},
)
assert_contains(resp, dir_swhid)
rev_swhid = gen_swhid(
ObjectType.REVISION,
- branch_info["revision"],
+ branch_info["target"],
metadata={
"origin": origin_url,
"visit": gen_swhid(ObjectType.SNAPSHOT, snapshot["id"]),
diff --git a/swh/web/tests/browse/views/test_directory.py b/swh/web/tests/browse/views/test_directory.py
--- a/swh/web/tests/browse/views/test_directory.py
+++ b/swh/web/tests/browse/views/test_directory.py
@@ -206,7 +206,7 @@
branch for branch in branches if branch["name"] == "refs/heads/master"
)
- directory = archive_data.revision_get(branch_info["revision"])["directory"]
+ directory = archive_data.revision_get(branch_info["target"])["directory"]
directory_content = archive_data.directory_ls(directory)
directory_subdir = random.choice(
[e for e in directory_content if e["type"] == "dir"]
@@ -239,7 +239,7 @@
metadata={
"origin": origin_url,
"visit": gen_swhid(ObjectType.SNAPSHOT, snapshot["id"]),
- "anchor": gen_swhid(ObjectType.REVISION, branch_info["revision"]),
+ "anchor": gen_swhid(ObjectType.REVISION, branch_info["target"]),
"path": "/",
},
)
@@ -247,7 +247,7 @@
rev_swhid = gen_swhid(
ObjectType.REVISION,
- branch_info["revision"],
+ branch_info["target"],
metadata={
"origin": origin_url,
"visit": gen_swhid(ObjectType.SNAPSHOT, snapshot["id"]),
@@ -357,7 +357,7 @@
branch for branch in branches if branch["name"] == "refs/heads/master"
)
- directory = archive_data.revision_get(branch_info["revision"])["directory"]
+ directory = archive_data.revision_get(branch_info["target"])["directory"]
directory_content = archive_data.directory_ls(directory)
directory_subdir = random.choice(
[e for e in directory_content if e["type"] == "dir"]
@@ -369,7 +369,7 @@
query_params={
"origin_url": origin_url,
"snapshot": snapshot["id"],
- "revision": branch_info["revision"],
+ "revision": branch_info["target"],
"path": directory_subdir["name"],
},
)
@@ -378,7 +378,7 @@
client, url, status_code=200, template_used="browse-directory.html"
)
- assert_contains(resp, f"Revision: {branch_info['revision']}")
+ assert_contains(resp, f"Revision: {branch_info['target']}")
def _check_origin_snapshot_related_html(
diff --git a/swh/web/tests/browse/views/test_snapshot.py b/swh/web/tests/browse/views/test_snapshot.py
--- a/swh/web/tests/browse/views/test_snapshot.py
+++ b/swh/web/tests/browse/views/test_snapshot.py
@@ -16,6 +16,7 @@
from swh.model.hashutil import hash_to_bytes
from swh.model.model import (
ObjectType,
+ Origin,
OriginVisit,
OriginVisitStatus,
Release,
@@ -175,7 +176,7 @@
browse_revision_url = reverse(
"browse-revision",
- url_args={"sha1_git": branch["revision"]},
+ url_args={"sha1_git": branch["target"]},
query_params=query_params,
)
assert_contains(resp, '' % escape(browse_revision_url))
@@ -446,3 +447,179 @@
client, snp_url, status_code=200, template_used="browse-directory.html"
)
assert_contains(resp, log_url)
+
+
+@pytest.mark.parametrize(
+ "aliased,with_origin",
+ [
+ (False, False),
+ (False, True),
+ (True, False),
+ (True, True),
+ ],
+)
+def test_browse_snapshot_single_branch_targeting_content(
+ client, archive_data, content_text, aliased, with_origin
+):
+
+ if not aliased:
+ snapshot = Snapshot(
+ branches={
+ b"HEAD": SnapshotBranch(
+ target=hash_to_bytes(content_text["sha1_git"]),
+ target_type=TargetType.CONTENT,
+ ),
+ },
+ )
+ else:
+ snapshot = Snapshot(
+ branches={
+ b"HEAD": SnapshotBranch(
+ target=b"content",
+ target_type=TargetType.ALIAS,
+ ),
+ b"content": SnapshotBranch(
+ target=hash_to_bytes(content_text["sha1_git"]),
+ target_type=TargetType.CONTENT,
+ ),
+ },
+ )
+
+ archive_data.snapshot_add([snapshot])
+
+ if with_origin:
+ origin_url = "https://git.example.org/user/project"
+ archive_data.origin_add([Origin(url=origin_url)])
+ date = now()
+ visit = OriginVisit(origin=origin_url, date=date, type="git")
+ visit = archive_data.origin_visit_add([visit])[0]
+ visit_status = OriginVisitStatus(
+ origin=origin_url,
+ visit=visit.visit,
+ date=date,
+ status="full",
+ snapshot=snapshot.id,
+ )
+ archive_data.origin_visit_status_add([visit_status])
+ url = reverse(
+ "browse-origin-directory", query_params={"origin_url": origin_url}
+ )
+ else:
+ url = reverse(
+ "browse-snapshot-directory", url_args={"snapshot_id": snapshot.id.hex()}
+ )
+
+ resp = check_html_get_response(
+ client,
+ url,
+ status_code=302,
+ )
+
+ if with_origin:
+ query_params = {"origin_url": origin_url}
+ else:
+ query_params = {"snapshot": snapshot.id.hex()}
+
+ assert resp["location"] == reverse(
+ "browse-content",
+ url_args={"query_string": f"sha1_git:{content_text['sha1_git']}"},
+ query_params=query_params,
+ )
+
+ resp = check_html_get_response(
+ client,
+ resp["location"],
+ status_code=200,
+ )
+
+ assert_contains(resp, escape(content_text["raw_data"].decode()))
+
+ if with_origin:
+ log_url = reverse("browse-origin-log", query_params=query_params)
+ else:
+ log_url = reverse(
+ "browse-snapshot-log", url_args={"snapshot_id": snapshot.id.hex()}
+ )
+
+ assert_not_contains(resp, log_url)
+
+
+@pytest.mark.parametrize(
+ "aliased,with_origin",
+ [
+ (False, False),
+ (False, True),
+ (True, False),
+ (True, True),
+ ],
+)
+def test_browse_snapshot_single_branch_targeting_directory(
+ client, archive_data, directory, aliased, with_origin
+):
+
+ if not aliased:
+ snapshot = Snapshot(
+ branches={
+ b"HEAD": SnapshotBranch(
+ target=hash_to_bytes(directory),
+ target_type=TargetType.DIRECTORY,
+ ),
+ },
+ )
+ else:
+ snapshot = Snapshot(
+ branches={
+ b"HEAD": SnapshotBranch(
+ target=b"directory",
+ target_type=TargetType.ALIAS,
+ ),
+ b"directory": SnapshotBranch(
+ target=hash_to_bytes(directory),
+ target_type=TargetType.DIRECTORY,
+ ),
+ },
+ )
+
+ archive_data.snapshot_add([snapshot])
+
+ if with_origin:
+ origin_url = "https://git.example.org/user/project"
+ archive_data.origin_add([Origin(url=origin_url)])
+ date = now()
+ visit = OriginVisit(origin=origin_url, date=date, type="git")
+ visit = archive_data.origin_visit_add([visit])[0]
+ visit_status = OriginVisitStatus(
+ origin=origin_url,
+ visit=visit.visit,
+ date=date,
+ status="full",
+ snapshot=snapshot.id,
+ )
+ archive_data.origin_visit_status_add([visit_status])
+ url = reverse(
+ "browse-origin-directory", query_params={"origin_url": origin_url}
+ )
+ else:
+ url = reverse(
+ "browse-snapshot-directory", url_args={"snapshot_id": snapshot.id.hex()}
+ )
+
+ resp = check_html_get_response(
+ client,
+ url,
+ status_code=200,
+ )
+
+ directory_data = archive_data.directory_get(directory)
+
+ for entry in directory_data["content"]:
+ assert_contains(resp, entry["name"])
+
+ if with_origin:
+ log_url = reverse("browse-origin-log", query_params={"origin_url": origin_url})
+ else:
+ log_url = reverse(
+ "browse-snapshot-log", url_args={"snapshot_id": snapshot.id.hex()}
+ )
+
+ assert_not_contains(resp, log_url)
diff --git a/swh/web/tests/conftest.py b/swh/web/tests/conftest.py
--- a/swh/web/tests/conftest.py
+++ b/swh/web/tests/conftest.py
@@ -987,6 +987,10 @@
counts = dict.fromkeys(("alias", "release", "revision"), 0)
counts.update(self.storage.snapshot_count_branches(hash_to_bytes(snapshot_id)))
counts.pop(None, None)
+ counts["branch"] = sum(
+ counts.get(target_type, 0)
+ for target_type in ("content", "directory", "revision")
+ )
return counts
diff --git a/swh/web/tests/utils/test_archive.py b/swh/web/tests/utils/test_archive.py
--- a/swh/web/tests/utils/test_archive.py
+++ b/swh/web/tests/utils/test_archive.py
@@ -996,13 +996,16 @@
expected_sizes = {
"alias": 0,
+ "branch": 0,
"release": 0,
"revision": 0,
}
- for branch_name, branch_info in branches.items():
+ for _, branch_info in branches.items():
if branch_info is not None:
expected_sizes[branch_info["target_type"]] += 1
+ if branch_info["target_type"] in ("content", "directory", "revision"):
+ expected_sizes["branch"] += 1
assert archive.lookup_snapshot_sizes(snapshot) == expected_sizes
@@ -1031,7 +1034,7 @@
)
archive_data.snapshot_add([snapshot])
- expected_sizes = {"alias": 0, "release": 0, "revision": 2}
+ expected_sizes = {"alias": 0, "branch": 2, "release": 0, "revision": 2}
assert (
archive.lookup_snapshot_sizes(
diff --git a/swh/web/utils/archive.py b/swh/web/utils/archive.py
--- a/swh/web/utils/archive.py
+++ b/swh/web/utils/archive.py
@@ -1097,6 +1097,12 @@
# when null branches are present in the snapshot
branch_counts.pop(None, None)
snapshot_sizes.update(branch_counts)
+
+ snapshot_sizes["branch"] = sum(
+ snapshot_sizes.get(target_type, 0)
+ for target_type in ("content", "directory", "revision")
+ )
+
return snapshot_sizes
diff --git a/swh/web/utils/typing.py b/swh/web/utils/typing.py
--- a/swh/web/utils/typing.py
+++ b/swh/web/utils/typing.py
@@ -55,8 +55,10 @@
"""branch name"""
alias: bool
"""define if the branch is an alias"""
- revision: str
- """branch heading revision"""
+ target_type: str
+ """branch target type: content, directory or revision"""
+ target: str
+ """branch target id"""
url: Optional[str]
"""optional browse URL (content, directory, ...) scoped to branch"""
@@ -127,8 +129,8 @@
"""common URL arguments when browsing snapshot content"""
visit_info: Optional[OriginVisitInfo]
"""optional origin visit info associated to the snapshot"""
- directory_url: Optional[str]
- """optional root directory URL associated to the snapshot"""
+ browse_url: Optional[str]
+ """optional browse URL associated to the snapshot"""
class SWHObjectInfo(TypedDict):