diff --git a/CHANGELOG.md b/CHANGELOG.md index f11668c1e..a2870c5c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Aliases can now call other aliases. +* `jj log` now accepts a `--reversed` option, which will show older commits + first. + ### Fixed bugs * When rebasing a conflict where one side modified a file and the other side diff --git a/lib/src/revset.rs b/lib/src/revset.rs index 783554e17..131b7325f 100644 --- a/lib/src/revset.rs +++ b/lib/src/revset.rs @@ -833,6 +833,12 @@ impl<'revset, 'repo> RevsetIterator<'revset, 'repo> { } } + pub fn reversed(self) -> ReverseRevsetIterator<'repo> { + ReverseRevsetIterator { + entries: self.into_iter().collect_vec(), + } + } + pub fn graph(self) -> RevsetGraphIterator<'revset, 'repo> { RevsetGraphIterator::new(self) } @@ -873,6 +879,18 @@ impl Iterator for RevsetCommitIterator<'_, '_> { } } +pub struct ReverseRevsetIterator<'repo> { + entries: Vec>, +} + +impl<'repo> Iterator for ReverseRevsetIterator<'repo> { + type Item = IndexEntry<'repo>; + + fn next(&mut self) -> Option { + self.entries.pop() + } +} + struct EagerRevset<'repo> { index_entries: Vec>, } diff --git a/src/commands.rs b/src/commands.rs index 31f70673d..a859882b5 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -42,7 +42,7 @@ use jujutsu_lib::diff::{Diff, DiffHunk}; use jujutsu_lib::files::DiffLine; use jujutsu_lib::git::{GitExportError, GitFetchError, GitImportError, GitRefUpdate}; use jujutsu_lib::gitignore::GitIgnoreFile; -use jujutsu_lib::index::HexPrefix; +use jujutsu_lib::index::{HexPrefix, IndexEntry}; use jujutsu_lib::matchers::{EverythingMatcher, Matcher, PrefixMatcher, Visit}; use jujutsu_lib::op_heads_store::{OpHeadResolutionError, OpHeads, OpHeadsStore}; use jujutsu_lib::op_store::{OpStore, OpStoreError, OperationId, RefTarget, WorkspaceId}; @@ -51,7 +51,7 @@ use jujutsu_lib::refs::{classify_branch_push_action, BranchPushAction, BranchPus use jujutsu_lib::repo::{MutableRepo, ReadonlyRepo, RepoRef}; use jujutsu_lib::repo_path::RepoPath; use jujutsu_lib::revset::{RevsetError, RevsetExpression, RevsetParseError}; -use jujutsu_lib::revset_graph_iterator::RevsetGraphEdgeType; +use jujutsu_lib::revset_graph_iterator::{RevsetGraphEdge, RevsetGraphEdgeType}; use jujutsu_lib::rewrite::{back_out_commit, merge_commit_trees, rebase_commit, DescendantRebaser}; use jujutsu_lib::settings::UserSettings; use jujutsu_lib::store::Store; @@ -1280,6 +1280,9 @@ struct LogArgs { default_value = "remote_branches().. | (remote_branches()..)-" )] revisions: String, + /// Show revisions in the opposite order (older revisions first) + #[clap(long)] + reversed: bool, /// Don't show the graph, show a flat list of revisions #[clap(long)] no_graph: bool, @@ -3016,7 +3019,12 @@ fn cmd_log(ui: &mut Ui, command: &CommandHelper, args: &LogArgs) -> Result<(), C if !args.no_graph { let mut graph = AsciiGraphDrawer::new(&mut formatter); - for (index_entry, edges) in revset.iter().graph() { + let iter: Box)>> = if args.reversed { + Box::new(revset.iter().graph().reversed()) + } else { + Box::new(revset.iter().graph()) + }; + for (index_entry, edges) in iter { let mut graphlog_edges = vec![]; // TODO: Should we update RevsetGraphIterator to yield this flag instead of all // the missing edges since we don't care about where they point here @@ -3072,7 +3080,12 @@ fn cmd_log(ui: &mut Ui, command: &CommandHelper, args: &LogArgs) -> Result<(), C )?; } } else { - for index_entry in revset.iter() { + let iter: Box> = if args.reversed { + Box::new(revset.iter().reversed()) + } else { + Box::new(revset.iter()) + }; + for index_entry in iter { let commit = store.get_commit(&index_entry.commit_id())?; template.format(&commit, formatter)?; if let Some(diff_format) = diff_format { diff --git a/tests/test_log_command.rs b/tests/test_log_command.rs index a4f176f17..8db9df48f 100644 --- a/tests/test_log_command.rs +++ b/tests/test_log_command.rs @@ -128,3 +128,30 @@ fn test_log_with_or_without_diff() { +bar "###); } + +#[test] +fn test_log_reversed() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]); + let repo_path = test_env.env_root().join("repo"); + + test_env.jj_cmd_success(&repo_path, &["describe", "-m", "first"]); + test_env.jj_cmd_success(&repo_path, &["new", "-m", "second"]); + + let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "description", "--reversed"]); + insta::assert_snapshot!(stdout, @r###" + o + o first + @ second + "###); + + let stdout = test_env.jj_cmd_success( + &repo_path, + &["log", "-T", "description", "--reversed", "--no-graph"], + ); + insta::assert_snapshot!(stdout, @r###" + + first + second + "###); +}