feat: show jj ancestry in the floating diff picker

This commit is contained in:
Valdemar Lorenzen
2026-10-23 17:08:53 +02:00
parent 496c93774f
commit e3d44f24c0
16 changed files with 882 additions and 50 deletions
+129 -5
View File
@@ -1951,6 +1951,7 @@ fn diff_catalog() -> crate::diff::Catalog {
Catalog {
kind: RepoKind::Git,
entries,
graph: Default::default(),
notice: None,
}
}
@@ -3501,7 +3502,7 @@ fn diff_picker_keeps_working_pinned_while_windowing_searching_and_changing_stage
assert!(text.contains("Choose Before"));
assert!(text.contains("Tab categories"));
assert!(text.contains("Enter select"));
assert!(text.contains("Working checkout · pinned"));
assert!(text.contains("Checkout snapshot · pinned"));
assert!(text.contains("> commit-19"));
assert!(!text.contains("commit-00"));
for ch in "needle".chars() {
@@ -3511,13 +3512,14 @@ fn diff_picker_keeps_working_pinned_while_windowing_searching_and_changing_stage
render_diff(&mut terminal, &diff, &mut panels);
let text = screen_text(&terminal);
assert!(text.contains("> needle"));
assert!(text.contains("Working checkout · pinned"));
assert!(text.contains("Checkout snapshot · pinned"));
assert!(text.contains("> commit-19"));
diff.handle(Input::Enter);
render_diff(&mut terminal, &diff, &mut panels);
let text = screen_text(&terminal);
assert!(text.contains("Choose After"));
assert!(text.contains("Branch changes · Ctrl+b toggle · Before: commit-19"));
assert!(text.contains("Branch changes · Ctrl+b toggle"));
assert!(!text.contains("Before:"));
assert!(screen_text(&terminal).contains("Tab categories"));
assert!(screen_text(&terminal).contains("Enter select"));
diff.handle(Input::Ctrl('u'));
@@ -3525,8 +3527,8 @@ fn diff_picker_keeps_working_pinned_while_windowing_searching_and_changing_stage
render_diff(&mut terminal, &diff, &mut panels);
let text = screen_text(&terminal);
assert!(text.contains("feature-branch"));
assert!(!text.contains("ordinary change"));
assert!(text.contains("Working checkout · pinned"));
assert!(text.contains("commit-19 [Before]"));
assert!(text.contains("Checkout snapshot · pinned"));
let buffer = terminal.backend().buffer();
assert!(
buffer
@@ -3536,6 +3538,128 @@ fn diff_picker_keeps_working_pinned_while_windowing_searching_and_changing_stage
);
}
#[test]
fn checkout_picker_labels_follow_the_configured_stage() {
let mut diff = DiffState::new();
diff.follow_working_copy = true;
diff.take_request();
diff.apply_result(1, Ok(crate::diff::Outcome::Catalog(diff_catalog())));
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(100, 16)).unwrap();
let mut panels = WheelPanels::default();
render_diff(&mut terminal, &diff, &mut panels);
assert!(
screen_text(&terminal).contains("Checkout snapshot · pinned · Live files at this checkout")
);
diff.handle(Input::Enter);
render_diff(&mut terminal, &diff, &mut panels);
assert!(screen_text(&terminal).contains("Follow checkout [Before] · pinned"));
assert!(screen_text(&terminal).contains("follows descendant checkouts"));
diff.follow_working_copy = false;
render_diff(&mut terminal, &diff, &mut panels);
assert!(screen_text(&terminal).contains("Checkout snapshot [Before] · pinned"));
assert!(!screen_text(&terminal).contains("Follow checkout"));
}
#[test]
fn jj_picker_renders_graph_before_badge_and_compacts_without_losing_controls() {
use crate::diff::{Category, RepoKind, Revision, RevisionGraph, RevisionTarget};
let mut catalog = diff_catalog();
catalog.kind = RepoKind::Jujutsu;
catalog.entries.truncate(1);
for (id, change, parent, current, immutable) in [
("aaaa1111", "change-a", Some("bbbb2222"), true, false),
("bbbb2222", "change-b", None, false, true),
] {
catalog.entries.push(Revision {
target: RevisionTarget::Commit(id.into()),
label: change.into(),
description: format!("{change} description"),
category: Category::Revisions,
});
catalog.graph.insert(
id.into(),
RevisionGraph {
change_id: change.into(),
description: format!("{change} description"),
parents: parent.into_iter().map(str::to_owned).collect(),
author: "alice".into(),
timestamp: "2026-10-23".into(),
bookmarks: if current {
"main".into()
} else {
String::new()
},
current,
immutable,
},
);
}
for name in ["main", "topic"] {
catalog.entries.push(Revision {
target: RevisionTarget::Bookmark {
name: name.into(),
remote: None,
},
label: name.into(),
description: "change-a aaaa1111 change-a description".into(),
category: Category::Bookmarks,
});
}
let mut diff = DiffState::new();
diff.take_request();
diff.apply_result(1, Ok(crate::diff::Outcome::Catalog(catalog)));
diff.handle(Input::Down);
diff.handle(Input::Enter);
diff.handle(Input::Down);
diff.handle(Input::Down);
let mut panels = WheelPanels::default();
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(100, 24)).unwrap();
render_diff(&mut terminal, &diff, &mut panels);
let text = screen_text(&terminal);
assert!(text.contains("Choose After"));
assert!(text.contains("@"));
assert!(text.contains("change-a [Before] alice 2026-10-23 [main] aaaa1111"));
assert!(text.contains("change-a description"));
assert!(!text.contains("Before:"));
let buffer = terminal.backend().buffer();
let selected_rows: Vec<_> = (0..buffer.area.height)
.filter(|&y| (0..buffer.area.width).any(|x| buffer[(x, y)].bg == Color::Blue))
.collect();
assert_eq!(selected_rows.len(), 2);
assert_eq!(selected_rows[1], selected_rows[0] + 1);
let before_row = (0..buffer.area.height)
.find(|&y| {
(0..buffer.area.width)
.map(|x| buffer[(x, y)].symbol())
.collect::<String>()
.contains("[Before]")
})
.unwrap();
assert!((0..buffer.area.width).all(|x| buffer[(x, before_row)].bg != Color::Blue));
for (width, height) in [(44, 12), (20, 8)] {
let mut terminal =
Terminal::new(ratatui::backend::TestBackend::new(width, height)).unwrap();
render_diff(&mut terminal, &diff, &mut panels);
let text = screen_text(&terminal);
assert!(text.contains("Choose After"));
assert!(text.contains("[Before]"));
assert!(text.contains("> "));
let buffer = terminal.backend().buffer();
assert_eq!(
(0..height)
.filter(|&y| { (0..width).any(|x| buffer[(x, y)].bg == Color::Blue) })
.count(),
1
);
}
diff.handle(Input::Char('\t'));
render_diff(&mut terminal, &diff, &mut panels);
let text = screen_text(&terminal);
assert!(text.contains("[main] change-a"));
assert!(text.contains("[topic] change-a"));
assert_eq!(text.matches("change-a description").count(), 2);
}
#[test]
fn pull_request_picker_renders_all_rows_without_a_pinned_offset() {
use crate::diff::{Category, PullRequestPage, RepoKind, Revision, RevisionTarget};