Skip to content

Commit ffe4854

Browse files
Implement :from filter
1 parent c084f68 commit ffe4854

File tree

6 files changed

+158
-1
lines changed

6 files changed

+158
-1
lines changed

docs/src/reference/filters.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ commits that don't match any of the other shas.
137137
Produce the history that would be the result of pushing the passed branches with the
138138
passed filters into the upstream.
139139

140+
### Start filtering from a specific commit **:from(<sha>:filter)**
141+
142+
Produce a history that keeps the original history up to and including the specified commit `<sha>` unchanged,
143+
but applies the given `:filter` to all commits after that commit.
144+
140145
### Prune trivial merge commits **:prune=trivial-merge**
141146

142147
Produce a history that skips all merge commits whose tree is identical to the first parents

josh-core/src/filter/grammar.pest

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ filter_spec = { (
2424
filter_group
2525
| filter_message
2626
| filter_rev
27+
| filter_from
28+
| filter_concat
2729
| filter_join
2830
| filter_replace
2931
| filter_squash
@@ -53,6 +55,24 @@ filter_rev = {
5355
~ ")"
5456
}
5557

58+
filter_from = {
59+
CMD_START ~ "from" ~ "("
60+
~ NEWLINE*
61+
~ (rev ~ filter_spec)?
62+
~ (CMD_SEP+ ~ (rev ~ filter_spec))*
63+
~ NEWLINE*
64+
~ ")"
65+
}
66+
67+
filter_concat = {
68+
CMD_START ~ "concat" ~ "("
69+
~ NEWLINE*
70+
~ (rev ~ filter_spec)?
71+
~ (CMD_SEP+ ~ (rev ~ filter_spec))*
72+
~ NEWLINE*
73+
~ ")"
74+
}
75+
5676
filter_join = {
5777
CMD_START ~ "join" ~ "("
5878
~ NEWLINE*
@@ -62,7 +82,6 @@ filter_join = {
6282
~ ")"
6383
}
6484

65-
6685
filter_replace = {
6786
CMD_START ~ "replace" ~ "("
6887
~ NEWLINE*

josh-core/src/filter/mod.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ enum Op {
317317
Pattern(String),
318318
Message(String, regex::Regex),
319319

320+
HistoryConcat(LazyRef, Filter),
321+
320322
Compose(Vec<Filter>),
321323
Chain(Filter, Filter),
322324
Subtract(Filter, Filter),
@@ -441,6 +443,14 @@ fn lazy_refs2(op: &Op) -> Vec<String> {
441443
av
442444
}
443445
Op::Rev(filters) => lazy_refs2(&Op::Join(filters.clone())),
446+
Op::HistoryConcat(r, f) => {
447+
let mut lr = Vec::new();
448+
if let LazyRef::Lazy(s) = r {
449+
lr.push(s.to_owned());
450+
}
451+
lr.append(&mut lazy_refs(*f));
452+
lr
453+
}
444454
Op::Join(filters) => {
445455
let mut lr = lazy_refs2(&Op::Compose(filters.values().copied().collect()));
446456
lr.extend(filters.keys().filter_map(|x| {
@@ -501,6 +511,19 @@ fn resolve_refs2(refs: &std::collections::HashMap<String, git2::Oid>, op: &Op) -
501511
.collect();
502512
Op::Rev(lr)
503513
}
514+
Op::HistoryConcat(r, filter) => {
515+
let f = resolve_refs(refs, *filter);
516+
let resolved_ref = if let LazyRef::Lazy(s) = r {
517+
if let Some(res) = refs.get(s) {
518+
LazyRef::Resolved(*res)
519+
} else {
520+
r.clone()
521+
}
522+
} else {
523+
r.clone()
524+
};
525+
Op::HistoryConcat(resolved_ref, f)
526+
}
504527
Op::Join(filters) => {
505528
let lr = filters
506529
.iter()
@@ -658,6 +681,9 @@ fn spec2(op: &Op) -> String {
658681
Op::Message(m, r) => {
659682
format!(":{};{}", parse::quote(m), parse::quote(r.as_str()))
660683
}
684+
Op::HistoryConcat(r, filter) => {
685+
format!(":concat({}{})", r.to_string(), spec(*filter))
686+
}
661687
Op::Hook(hook) => {
662688
format!(":hook={}", parse::quote(hook))
663689
}
@@ -1113,6 +1139,19 @@ fn apply_to_commit2(
11131139

11141140
return per_rev_filter(transaction, commit, filter, commit_filter, parent_filters);
11151141
}
1142+
Op::HistoryConcat(r, f) => {
1143+
if let LazyRef::Resolved(c) = r {
1144+
let a = apply_to_commit2(&to_op(*f), &repo.find_commit(*c)?, transaction)?;
1145+
let a = some_or!(a, { return Ok(None) });
1146+
if commit.id() == a {
1147+
transaction.insert(filter, commit.id(), *c, true);
1148+
return Ok(Some(*c));
1149+
}
1150+
} else {
1151+
return Err(josh_error("unresolved lazy ref"));
1152+
}
1153+
Apply::from_commit(commit)?
1154+
}
11161155
_ => apply(transaction, filter, Apply::from_commit(commit)?)?,
11171156
};
11181157

@@ -1177,6 +1216,7 @@ fn apply2<'a>(transaction: &'a cache::Transaction, op: &Op, x: Apply<'a>) -> Jos
11771216

11781217
Ok(x.with_message(text::transform_with_template(&r, &m, &message, &hm)?))
11791218
}
1219+
Op::HistoryConcat(..) => Ok(x),
11801220
Op::Linear => Ok(x),
11811221
Op::Prune => Ok(x),
11821222
Op::Unsign => Ok(x),

josh-core/src/filter/parse.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,31 @@ fn parse_item(pair: pest::iterators::Pair<Rule>) -> JoshResult<Op> {
178178

179179
Ok(Op::Rev(hm))
180180
}
181+
Rule::filter_from => {
182+
let v: Vec<_> = pair.into_inner().map(|x| x.as_str()).collect();
183+
184+
if v.len() == 2 {
185+
let oid = LazyRef::parse(v[0])?;
186+
let filter = parse(v[1])?;
187+
Ok(Op::Chain(
188+
filter,
189+
filter::to_filter(Op::HistoryConcat(oid, filter)),
190+
))
191+
} else {
192+
Err(josh_error("wrong argument count for :from"))
193+
}
194+
}
195+
Rule::filter_concat => {
196+
let v: Vec<_> = pair.into_inner().map(|x| x.as_str()).collect();
197+
198+
if v.len() == 2 {
199+
let oid = LazyRef::parse(v[0])?;
200+
let filter = parse(v[1])?;
201+
Ok(Op::HistoryConcat(oid, filter))
202+
} else {
203+
Err(josh_error("wrong argument count for :concat"))
204+
}
205+
}
181206
Rule::filter_replace => {
182207
let replacements = pair
183208
.into_inner()

josh-core/src/filter/persist.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,10 @@ impl InMemoryBuilder {
290290
let params_tree = self.build_rev_params(&v)?;
291291
push_tree_entries(&mut entries, [("join", params_tree)]);
292292
}
293+
Op::HistoryConcat(lr, f) => {
294+
let params_tree = self.build_rev_params(&[(lr.to_string(), *f)])?;
295+
push_tree_entries(&mut entries, [("concat", params_tree)]);
296+
}
293297
Op::Squash(Some(ids)) => {
294298
let mut v = ids
295299
.iter()
@@ -635,6 +639,28 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
635639
}
636640
Ok(Op::Join(filters))
637641
}
642+
"concat" => {
643+
let concat_tree = repo.find_tree(entry.id())?;
644+
let entry = concat_tree
645+
.get(0)
646+
.ok_or_else(|| josh_error("concat: missing entry"))?;
647+
let inner_tree = repo.find_tree(entry.id())?;
648+
let key_blob = repo.find_blob(
649+
inner_tree
650+
.get_name("o")
651+
.ok_or_else(|| josh_error("concat: missing key"))?
652+
.id(),
653+
)?;
654+
let filter_tree = repo.find_tree(
655+
inner_tree
656+
.get_name("f")
657+
.ok_or_else(|| josh_error("concat: missing filter"))?
658+
.id(),
659+
)?;
660+
let key = std::str::from_utf8(key_blob.content())?.to_string();
661+
let filter = from_tree2(repo, filter_tree.id())?;
662+
Ok(Op::HistoryConcat(LazyRef::parse(&key)?, to_filter(filter)))
663+
}
638664
"squash" => {
639665
// blob -> Squash(None), tree -> Squash(Some(...))
640666
if let Some(kind) = entry.kind() {

tests/filter/concat.t

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
$ export TESTTMP=${PWD}
2+
3+
$ cd ${TESTTMP}
4+
$ git init -q libs 1> /dev/null
5+
$ cd libs
6+
7+
$ mkdir sub1
8+
$ echo contents1 > sub1/file1
9+
$ git add sub1
10+
$ git commit -m "add file1" 1> /dev/null
11+
12+
$ echo contents2 > sub1/file2
13+
$ git add sub1
14+
$ git commit -m "add file2" 1> /dev/null
15+
$ git update-ref refs/heads/from_here HEAD
16+
17+
18+
$ mkdir sub2
19+
$ echo contents1 > sub2/file3
20+
$ git add sub2
21+
$ git commit -m "add file3" 1> /dev/null
22+
23+
$ josh-filter ":\"x\""
24+
25+
$ git log --graph --pretty=%s:%H HEAD
26+
* add file3:667a912db7482f3c8023082c9b4c7b267792633a
27+
* add file2:81b10fb4984d20142cd275b89c91c346e536876a
28+
* add file1:bb282e9cdc1b972fffd08fd21eead43bc0c83cb8
29+
30+
$ git log --graph --pretty=%s:%H FILTERED_HEAD
31+
* x:9d117d96dfdba145df43ebe37d9e526acac4b17c
32+
* x:b232aa8eefaadfb5e38b3ad7355118aa59fb651e
33+
* x:6b4d1f87c2be08f7d0f9d40b6679aab612e259b1
34+
35+
$ josh-filter -p ":from(81b10fb4984d20142cd275b89c91c346e536876a:\"x\")"
36+
:"x":concat(81b10fb4984d20142cd275b89c91c346e536876a:"x")
37+
$ josh-filter ":from(81b10fb4984d20142cd275b89c91c346e536876a:\"x\")"
38+
39+
$ git log --graph --pretty=%s FILTERED_HEAD
40+
* x
41+
* add file2
42+
* add file1

0 commit comments

Comments
 (0)