ok/jj
1
0
Fork 0
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:
Martin von Zweigbergk 2023-04-04 20:05:51 -07:00 committed by Martin von Zweigbergk
parent f0cc4c3ae2
commit 24a512683b
6 changed files with 59 additions and 1 deletions

View file

@ -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.

View file

@ -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.)

View file

@ -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()
}),
}
}

View file

@ -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)?;

View file

@ -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();

View file

@ -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