Skip to content

Commit 4d17695

Browse files
Add :prune=trivial-merge filter
The default history simplification will not drop merge commits if their tree differs from any parent. In most cases however merges that don't differ from the first parent are not wanted in the output history. This new filter allows to prune them. Change: prune-trivial-merges
1 parent 03ad5cc commit 4d17695

File tree

5 files changed

+101
-0
lines changed

5 files changed

+101
-0
lines changed

docs/src/reference/filters.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ commits that don't match any of the other shas.
114114
Produce the history that would be the result of pushing the passed branches with the
115115
passed filters into the upstream.
116116

117+
### Prune trivial merge commits **:prune=trivial-merge**
118+
119+
Produce a history that skips all merge commits whose tree is identical to the first parents
120+
tree.
121+
Normally Josh will keep all commits in the filtered history whose tree differs from any of it's
122+
parents.
123+
117124
Filter order matters
118125
--------------------
119126

josh-core/src/filter/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ enum Op {
144144
Rev(std::collections::BTreeMap<LazyRef, Filter>),
145145
Join(std::collections::BTreeMap<LazyRef, Filter>),
146146
Linear,
147+
Prune,
147148
Unsign,
148149

149150
RegexReplace(Vec<(regex::Regex, String)>),
@@ -485,6 +486,7 @@ fn spec2(op: &Op) -> String {
485486
Op::Unsign => ":unsign".to_string(),
486487
Op::Subdir(path) => format!(":/{}", parse::quote_if(&path.to_string_lossy())),
487488
Op::File(path) => format!("::{}", parse::quote_if(&path.to_string_lossy())),
489+
Op::Prune => ":prune=trivial-merge".to_string(),
488490
Op::Prefix(path) => format!(":prefix={}", parse::quote_if(&path.to_string_lossy())),
489491
Op::Pattern(pattern) => format!("::{}", parse::quote_if(pattern)),
490492
Op::Author(author, email) => {
@@ -811,6 +813,33 @@ fn apply_to_commit2(
811813
))
812814
.transpose();
813815
}
816+
Op::Prune => {
817+
let p: Vec<_> = commit.parent_ids().collect();
818+
819+
if p.len() > 0 {
820+
let parent = some_or!(transaction.get(filter, p[0]), {
821+
return Ok(None);
822+
});
823+
824+
let parent_tree = transaction.repo().find_commit(parent)?.tree_id();
825+
826+
if parent_tree == commit.tree_id() {
827+
return Ok(Some(history::drop_commit(
828+
commit,
829+
vec![parent],
830+
transaction,
831+
filter,
832+
)?));
833+
}
834+
}
835+
836+
RewriteData {
837+
tree: commit.tree()?,
838+
message: None,
839+
author: None,
840+
committer: None,
841+
}
842+
}
814843
Op::Unsign => {
815844
let parents: Vec<_> = commit.parent_ids().collect();
816845

@@ -998,6 +1027,7 @@ fn apply2<'a>(
9981027
Op::Committer(_, _) => Ok(tree),
9991028
Op::Squash(Some(_)) => Err(josh_error("not applicable to tree")),
10001029
Op::Linear => Ok(tree),
1030+
Op::Prune => Ok(tree),
10011031
Op::Unsign => Ok(tree),
10021032
Op::Rev(_) => Err(josh_error("not applicable to tree")),
10031033
Op::Join(_) => Err(josh_error("not applicable to tree")),

josh-core/src/filter/opt.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ pub fn invert(filter: Filter) -> JoshResult<Filter> {
489489
let result = match to_op(filter) {
490490
Op::Nop => Some(Op::Nop),
491491
Op::Linear => Some(Op::Nop),
492+
Op::Prune => Some(Op::Prune),
492493
Op::Unsign => Some(Op::Unsign),
493494
Op::Empty => Some(Op::Empty),
494495
Op::Subdir(path) => Some(Op::Prefix(path)),

josh-core/src/filter/parse.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@ fn make_op(args: &[&str]) -> JoshResult<Op> {
3535
["SQUASH"] => Ok(Op::Squash(None)),
3636
["SQUASH", _ids @ ..] => Err(josh_error("SQUASH with ids can't be parsed")),
3737
["linear"] => Ok(Op::Linear),
38+
["prune", "trivial-merge"] => Ok(Op::Prune),
39+
["prune"] => Err(josh_error(indoc!(
40+
r#"
41+
Filter ":prune" requires an argument.
42+
43+
Note: use "=" to provide the argument value:
44+
45+
:prune=trivial-merge
46+
"#
47+
))),
48+
["prune", _] => Err(josh_error(indoc!(
49+
r#"
50+
Filter ":prune" only supports "trivial-merge"
51+
as arguement value.
52+
"#
53+
))),
3854
["unsign"] => Ok(Op::Unsign),
3955
["PATHS"] => Ok(Op::Paths),
4056
["INDEX"] => Ok(Op::Index),

tests/filter/prune_trivial_merge.t

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
$ export RUST_BACKTRACE=1
2+
$ git init -q 1> /dev/null
3+
4+
$ echo contents1 > file1
5+
$ git add .
6+
$ git commit -m "add file1" 1> /dev/null
7+
8+
$ git log --graph --pretty=%s
9+
* add file1
10+
11+
$ git checkout -b branch1
12+
Switched to a new branch 'branch1'
13+
$ echo contents2 > file2
14+
$ git add .
15+
$ git commit -m "add file2" 1> /dev/null
16+
17+
$ git checkout master
18+
Switched to branch 'master'
19+
20+
$ echo contents3 > file1
21+
$ git add .
22+
$ git commit -m "mod file1" 1> /dev/null
23+
24+
$ git merge -q branch1 --no-ff
25+
$ git log --graph --pretty=%s
26+
* Merge branch 'branch1'
27+
|\
28+
| * add file2
29+
* | mod file1
30+
|/
31+
* add file1
32+
33+
$ josh-filter -s ::file1
34+
[3] ::file1
35+
$ git log --graph --pretty=%s FILTERED_HEAD
36+
* Merge branch 'branch1'
37+
|\
38+
* | mod file1
39+
|/
40+
* add file1
41+
$ josh-filter -s ::file1:prune=trivial-merge
42+
[2] :prune=trivial-merge
43+
[3] ::file1
44+
45+
$ git log --graph --pretty=%s FILTERED_HEAD
46+
* mod file1
47+
* add file1

0 commit comments

Comments
 (0)