From bc1bd5a5a776c4c053c214965e7afbf9a1e16af1 Mon Sep 17 00:00:00 2001 From: Nikolaus Wittenstein Date: Sun, 18 Feb 2024 22:55:51 -0800 Subject: [PATCH] Add new 'smartlog --exact' flag to skip adding main and HEAD --- git-branchless-navigation/src/lib.rs | 1 + git-branchless-opts/src/lib.rs | 5 ++ git-branchless-smartlog/src/lib.rs | 28 ++++++-- .../tests/test_smartlog.rs | 70 +++++++++++++++++++ git-branchless-undo/src/lib.rs | 10 ++- git-branchless/src/commands/bug_report.rs | 10 ++- git-branchless/tests/test_init.rs | 4 +- 7 files changed, 119 insertions(+), 9 deletions(-) diff --git a/git-branchless-navigation/src/lib.rs b/git-branchless-navigation/src/lib.rs index 78e02a4c9..8361ee730 100644 --- a/git-branchless-navigation/src/lib.rs +++ b/git-branchless-navigation/src/lib.rs @@ -507,6 +507,7 @@ pub fn switch( &event_replayer, event_cursor, &commits, + false, )?; let initial_query = match switch_options { diff --git a/git-branchless-opts/src/lib.rs b/git-branchless-opts/src/lib.rs index f41b3c54e..131eea0e1 100644 --- a/git-branchless-opts/src/lib.rs +++ b/git-branchless-opts/src/lib.rs @@ -345,6 +345,11 @@ pub struct SmartlogArgs { #[clap(long)] pub reverse: bool, + /// Don't automatically add HEAD and the main branch to the list of commits + /// to present. They will still be added if included in the revset. + #[clap(long)] + pub exact: bool, + /// Options for resolving revset expressions. #[clap(flatten)] pub resolve_revset_options: ResolveRevsetOptions, diff --git a/git-branchless-smartlog/src/lib.rs b/git-branchless-smartlog/src/lib.rs index 868b96897..03167a86c 100644 --- a/git-branchless-smartlog/src/lib.rs +++ b/git-branchless-smartlog/src/lib.rs @@ -163,11 +163,17 @@ mod graph { dag: &Dag, commits: &CommitSet, ) -> eyre::Result> { + let commits_include_main = + !dag.set_is_empty(&dag.main_branch_commit.intersection(commits))?; let mut graph: HashMap = { let mut result = HashMap::new(); for vertex in dag.commit_set_to_vec(commits)? { let vertex = CommitSet::from(vertex); - let merge_bases = dag.query_gca_all(dag.main_branch_commit.union(&vertex))?; + let merge_bases = if commits_include_main { + dag.query_gca_all(dag.main_branch_commit.union(&vertex))? + } else { + dag.query_gca_all(commits.union(&vertex))? + }; let vertices = vertex.union(&merge_bases); for oid in dag.commit_set_to_vec(&vertices)? { @@ -341,16 +347,21 @@ mod graph { event_replayer: &EventReplayer, event_cursor: EventCursor, commits: &CommitSet, + exact: bool, ) -> eyre::Result> { let (effects, _progress) = effects.start_operation(OperationType::MakeGraph); let mut graph = { let (effects, _progress) = effects.start_operation(OperationType::WalkCommits); - // HEAD and main head must be included - let commits = commits - .union(&dag.head_commit) - .union(&dag.main_branch_commit); + // HEAD and main head are automatically included unless `exact` is set + let commits = if exact { + commits.clone() + } else { + commits + .union(&dag.head_commit) + .union(&dag.main_branch_commit) + }; for oid in dag.commit_set_to_vec(&commits)? { mark_commit_reachable(repo, oid)?; @@ -741,6 +752,9 @@ mod render { /// Reverse the ordering of items in the smartlog output, list the most /// recent commits first. pub reverse: bool, + + /// Normally HEAD and the main branch are included. Set this to exclude them. + pub exact: bool, } } @@ -756,6 +770,7 @@ pub fn smartlog( revset, resolve_revset_options, reverse, + exact, } = options; let repo = Repo::from_dir(&git_run_info.working_directory)?; @@ -809,6 +824,7 @@ pub fn smartlog( &event_replayer, event_cursor, &commits, + exact, )?; let mut lines = render_graph( @@ -901,6 +917,7 @@ pub fn command_main(ctx: CommandContext, args: SmartlogArgs) -> EyreExitOr<()> { revset, resolve_revset_options, reverse, + exact, } = args; smartlog( @@ -911,6 +928,7 @@ pub fn command_main(ctx: CommandContext, args: SmartlogArgs) -> EyreExitOr<()> { revset, resolve_revset_options, reverse, + exact, }, ) } diff --git a/git-branchless-smartlog/tests/test_smartlog.rs b/git-branchless-smartlog/tests/test_smartlog.rs index 09aecf7aa..2b53bd6e3 100644 --- a/git-branchless-smartlog/tests/test_smartlog.rs +++ b/git-branchless-smartlog/tests/test_smartlog.rs @@ -742,3 +742,73 @@ fn test_default_smartlog_revset() -> eyre::Result<()> { Ok(()) } + +#[test] +fn test_exact() -> eyre::Result<()> { + let git = make_git()?; + git.init_repo()?; + + git.commit_file("test1", 1)?; + git.detach_head()?; + git.commit_file("test2", 2)?; + git.commit_file("test3", 3)?; + git.commit_file("test4", 4)?; + git.run(&["checkout", "master"])?; + git.commit_file("test5", 5)?; + git.detach_head()?; + git.commit_file("test6", 6)?; + git.run(&["checkout", "96d1c37"])?; + + { + // Here '--exact' doesn't change anything because draft() covers all these commits + let (stdout, _stderr) = git.branchless("smartlog", &["--exact"])?; + insta::assert_snapshot!(stdout, @r###" + : + O 62fc20d create test1.txt + |\ + | @ 96d1c37 create test2.txt + | | + | o 70deb1e create test3.txt + | | + | o 355e173 create test4.txt + | + O ea7aa06 (master) create test5.txt + | + o da42aeb create test6.txt + "###); + } + + { + // Show no commits + let (stdout, _stderr) = git.branchless("smartlog", &["--exact", "none()"])?; + insta::assert_snapshot!(stdout, @""); + } + + { + // Show one commit - no master or HEAD + let (stdout, _stderr) = git.branchless("smartlog", &["--exact", "70deb1e"])?; + insta::assert_snapshot!(stdout, @r###" + : + o 70deb1e create test3.txt + : + # 1 omitted descendant commit + "###); + } + + { + // Show head commits and their common ancestor, which is not main. + let (stdout, _stderr) = git.branchless("smartlog", &["--exact", "heads(draft())"])?; + insta::assert_snapshot!(stdout, @r###" + : + O 62fc20d create test1.txt + |\ + : # 2 omitted commits + : : + : o 355e173 create test4.txt + : + o da42aeb create test6.txt + "###); + } + + Ok(()) +} diff --git a/git-branchless-undo/src/lib.rs b/git-branchless-undo/src/lib.rs index 86248be8d..4dc39dcf2 100644 --- a/git-branchless-undo/src/lib.rs +++ b/git-branchless-undo/src/lib.rs @@ -77,7 +77,15 @@ fn render_cursor_smartlog( }; let commits = resolve_default_smartlog_commits(effects, repo, &mut dag)?; - let graph = make_smartlog_graph(effects, repo, &dag, event_replayer, event_cursor, &commits)?; + let graph = make_smartlog_graph( + effects, + repo, + &dag, + event_replayer, + event_cursor, + &commits, + false, + )?; let result = render_graph( effects, repo, diff --git a/git-branchless/src/commands/bug_report.rs b/git-branchless/src/commands/bug_report.rs index a48ea6cc5..15d0c2a38 100644 --- a/git-branchless/src/commands/bug_report.rs +++ b/git-branchless/src/commands/bug_report.rs @@ -133,7 +133,15 @@ fn describe_event_cursor( let glyphs = Glyphs::text(); let effects = Effects::new(glyphs.clone()); let commits = resolve_default_smartlog_commits(&effects, repo, dag)?; - let graph = make_smartlog_graph(&effects, repo, dag, event_replayer, event_cursor, &commits)?; + let graph = make_smartlog_graph( + &effects, + repo, + dag, + event_replayer, + event_cursor, + &commits, + false, + )?; let graph_lines = render_graph( &effects, repo, diff --git a/git-branchless/tests/test_init.rs b/git-branchless/tests/test_init.rs index ebdf64aa5..d0d8d8b14 100644 --- a/git-branchless/tests/test_init.rs +++ b/git-branchless/tests/test_init.rs @@ -316,9 +316,9 @@ fn test_main_branch_not_found_error_message() -> eyre::Result<()> { 0: branchless::core::eventlog::from_event_log_db with effects= repo=/.git/"> event_log_db=/.git/branchless/db.sqlite3")> at some/file/path.rs:123 - 1: git_branchless_smartlog::smartlog with effects= git_run_info= options=SmartlogOptions { event_id: None, revset: None, resolve_revset_options: ResolveRevsetOptions { show_hidden_commits: false }, reverse: false } + 1: git_branchless_smartlog::smartlog with effects= git_run_info= options=SmartlogOptions { event_id: None, revset: None, resolve_revset_options: ResolveRevsetOptions { show_hidden_commits: false }, reverse: false, exact: false } at some/file/path.rs:123 - 2: git_branchless_smartlog::command_main with ctx=CommandContext { effects: , git_run_info: } args=SmartlogArgs { event_id: None, revset: None, reverse: false, resolve_revset_options: ResolveRevsetOptions { show_hidden_commits: false } } + 2: git_branchless_smartlog::command_main with ctx=CommandContext { effects: , git_run_info: } args=SmartlogArgs { event_id: None, revset: None, reverse: false, exact: false, resolve_revset_options: ResolveRevsetOptions { show_hidden_commits: false } } at some/file/path.rs:123 Suggestion: