forked from mirrors/jj
revset: add a revset function for finding commits with conflicts
This adds `conflict()` revset that selects commits with conflicts. We may want to extend it later to consider only conflicts at certain paths.
This commit is contained in:
parent
f0cc4c3ae2
commit
24a512683b
6 changed files with 59 additions and 1 deletions
|
@ -65,6 +65,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
* Added `latest(x[, n])` revset function to select the latest `n` commits.
|
||||
|
||||
* Added `conflict()` revset function to select commits with conflicts.
|
||||
|
||||
* `jj squash` AKA `jj amend` now accepts a `--message` option to set the
|
||||
description of the squashed commit on the command-line.
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@ revsets (expressions) as arguments.
|
|||
user modifications and `root`.
|
||||
* `file(pattern..)`: Commits modifying the paths specified by the `pattern..`.
|
||||
Paths are relative to the directory `jj` was invoked from.
|
||||
* `conflict()`: Commits with conflicts.
|
||||
* `present(x)`: Same as `x`, but evaluated to `none()` if any of the commits
|
||||
in `x` doesn't exist (e.g. is an unknown branch name.)
|
||||
|
||||
|
|
|
@ -944,6 +944,10 @@ fn build_predicate_fn<'index>(
|
|||
has_diff_from_parent(&store, index, entry, matcher.as_ref())
|
||||
})
|
||||
}
|
||||
RevsetFilterPredicate::HasConflict => pure_predicate_fn(move |entry| {
|
||||
let commit = store.get_commit(&entry.commit_id()).unwrap();
|
||||
commit.tree().has_conflict()
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -205,6 +205,8 @@ pub enum RevsetFilterPredicate {
|
|||
Committer(String),
|
||||
/// Commits modifying the paths specified by the pattern.
|
||||
File(Option<Vec<RepoPath>>), // TODO: embed matcher expression?
|
||||
/// Commits with conflicts
|
||||
HasConflict,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
|
@ -928,6 +930,10 @@ static BUILTIN_FUNCTION_MAP: Lazy<HashMap<&'static str, RevsetFunction>> = Lazy:
|
|||
))
|
||||
}
|
||||
});
|
||||
map.insert("conflict", |name, arguments_pair, _state| {
|
||||
expect_no_arguments(name, arguments_pair)?;
|
||||
Ok(RevsetExpression::filter(RevsetFilterPredicate::HasConflict))
|
||||
});
|
||||
map.insert("present", |name, arguments_pair, state| {
|
||||
let arg = expect_one_argument(name, arguments_pair)?;
|
||||
let expression = parse_expression_rule(arg.into_inner(), state)?;
|
||||
|
|
|
@ -29,6 +29,7 @@ use jujutsu_lib::revset::{
|
|||
RevsetResolutionError, RevsetWorkspaceContext,
|
||||
};
|
||||
use jujutsu_lib::settings::GitSettings;
|
||||
use jujutsu_lib::tree::merge_trees;
|
||||
use jujutsu_lib::workspace::Workspace;
|
||||
use test_case::test_case;
|
||||
use testutils::{
|
||||
|
@ -2142,6 +2143,49 @@ fn test_evaluate_expression_file(use_git: bool) {
|
|||
);
|
||||
}
|
||||
|
||||
#[test_case(false ; "local backend")]
|
||||
#[test_case(true ; "git backend")]
|
||||
fn test_evaluate_expression_conflict(use_git: bool) {
|
||||
let settings = testutils::user_settings();
|
||||
let test_workspace = TestWorkspace::init(&settings, use_git);
|
||||
let repo = &test_workspace.repo;
|
||||
|
||||
let mut tx = repo.start_transaction(&settings, "test");
|
||||
let mut_repo = tx.mut_repo();
|
||||
|
||||
// Create a few trees, including one with a conflict in `file1`
|
||||
let file_path1 = RepoPath::from_internal_string("file1");
|
||||
let file_path2 = RepoPath::from_internal_string("file2");
|
||||
let tree1 = testutils::create_tree(repo, &[(&file_path1, "1"), (&file_path2, "1")]);
|
||||
let tree2 = testutils::create_tree(repo, &[(&file_path1, "2"), (&file_path2, "2")]);
|
||||
let tree3 = testutils::create_tree(repo, &[(&file_path1, "3"), (&file_path2, "1")]);
|
||||
let tree_id4 = merge_trees(&tree2, &tree1, &tree3).unwrap();
|
||||
let tree4 = mut_repo
|
||||
.store()
|
||||
.get_tree(&RepoPath::root(), &tree_id4)
|
||||
.unwrap();
|
||||
|
||||
let mut create_commit = |parent_ids, tree_id| {
|
||||
mut_repo
|
||||
.new_commit(&settings, parent_ids, tree_id)
|
||||
.write()
|
||||
.unwrap()
|
||||
};
|
||||
let commit1 = create_commit(
|
||||
vec![repo.store().root_commit_id().clone()],
|
||||
tree1.id().clone(),
|
||||
);
|
||||
let commit2 = create_commit(vec![commit1.id().clone()], tree2.id().clone());
|
||||
let commit3 = create_commit(vec![commit2.id().clone()], tree3.id().clone());
|
||||
let commit4 = create_commit(vec![commit3.id().clone()], tree4.id().clone());
|
||||
|
||||
// Only commit4 has a conflict
|
||||
assert_eq!(
|
||||
resolve_commit_ids(mut_repo, "conflict()"),
|
||||
vec![commit4.id().clone()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reverse_graph_iterator() {
|
||||
let settings = testutils::user_settings();
|
||||
|
|
|
@ -43,6 +43,7 @@ tags()+
|
|||
# Filter that doesn't read commit object
|
||||
merges()
|
||||
~merges()
|
||||
# Files are unbearably slow, so only filter within small set
|
||||
# These are unbearably slow, so only filter within small set
|
||||
file(Makefile) & v1.0.0..v1.2.0
|
||||
empty() & v1.0.0..v1.2.0
|
||||
conflict() & v1.0.0..v1.2.0
|
||||
|
|
Loading…
Reference in a new issue