roots: list only blobs in the commit diff, not every subdirectory
commit
8f5861broots: list only blobs in the commit diff, not every subdirectory
The tree walk names each intermediate directory as its own change; those rendered as bare file headers per subdirectory.
Assisted-by: Claude:claude-fable-5
Reviews
No reviews of this commit yet — record a verdict below.
Start a review
crates/cli/ents-web/tests/router.rs
@@ -1705,6 +1705,45 @@
);
}
+/// `GET /commit/{oid}` renders one `.file` header per changed blob and
+/// none for the intermediate directories the tree walk also names --
+/// each subdirectory used to appear as its own bare file section.
+#[tokio::test]
+async fn commit_diff_lists_files_not_intermediate_directories() {
+ let dir = seed_repo(&[("crates/foo/src/lib.rs", "pub fn f() {}\n")]);
+ let oid = head_oid(dir.path());
+ let state = build_state_at(
+ FixtureIdentity {
+ name: "local-user",
+ key: Keypair::from_seed(1),
+ },
+ dir.path().to_owned(),
+ );
+ let router = ents_web::router(state);
+
+ let response = router
+ .oneshot(
+ Request::get(format!("/commit/{oid}"))
+ .body(Body::empty())
+ .expect("request"),
+ )
+ .await
+ .expect("in-process call");
+ assert_eq!(response.status(), StatusCode::OK);
+ let body = response
+ .into_body()
+ .collect()
+ .await
+ .expect("body")
+ .to_bytes();
+ let body = String::from_utf8(body.to_vec()).expect("utf8 html");
+ assert_eq!(
+ body.matches("class=\"ln file\"").count(),
+ 1,
+ "one changed blob means exactly one file header"
+ );
+}
+
/// `GET /commit/{oid}` lists, under a "conversation" heading, every
/// comment whose anchor was captured against that exact commit -- and
/// none captured against a different one, even a later commit on the same
crates/cli/ents-web/src/pages/commits.rs
@@ -596,6 +596,13 @@
if truncated {
return Ok::<_, std::convert::Infallible>(std::ops::ControlFlow::Break(()));
}
+ // The walk yields every intermediate directory as its own tree
+ // change; only blob (and link) entries are files a reader can
+ // diff, so a tree entry renders nothing rather than a bare
+ // header per subdirectory.
+ if is_tree_change(&change) {
+ return Ok(std::ops::ControlFlow::Continue(()));
+ }
let (section, bytes) = render_change(repo, &change);
total = total.saturating_add(bytes);
sections.push(section);
@@ -607,6 +614,18 @@
(html! { @for section in §ions { (section) } }, truncated)
}
+/// Whether `change` is a tree (directory) entry rather than a blob or
+/// link -- [`diff_sections`] skips these, since the walk names every
+/// intermediate directory on the way to a changed file.
+fn is_tree_change(change: &Change<'_, '_, '_>) -> bool {
+ match *change {
+ Change::Addition { entry_mode, .. }
+ | Change::Deletion { entry_mode, .. }
+ | Change::Modification { entry_mode, .. }
+ | Change::Rewrite { entry_mode, .. } => entry_mode.is_tree(),
+ }
+}
+
/// One changed file's `.diff` section: a `.file`-classed header naming the
/// path (and, on a rename, the old path it moved from), followed by either
/// a `.meta`-classed "binary file changed" notice or the colorized unified