Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e48826e

Browse files
committedNov 14, 2022
Add :rev(...) filter
Change: start-filter
1 parent e084d74 commit e48826e

File tree

10 files changed

+418
-5
lines changed

10 files changed

+418
-5
lines changed
 

‎Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ handlebars = "4.3.5"
3131
hex = "0.4.3"
3232
hyper-reverse-proxy = "0.5.1"
3333
indoc = "1.0.7"
34+
itertools = "0.9"
3435
juniper = "0.15.10"
3536
lazy_static = "1.4.0"
3637
log = "0.4.17"

‎docs/src/reference/filters.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ These filter do not modify git trees, but instead only operate on the commit gra
9191
Produce a filtered history that does not contain any merge commits. This is done by
9292
simply dropping all parents except the first on every commit.
9393

94+
### Filter specific part of the history **:at_commit=<sha>[:filter]**
95+
Produce a history where the commit specified by `<sha>` is replaced by the result of applying
96+
`:filter` to it.
97+
This means also all parents of this specific commit appear filtered with `:filter` and all
98+
descendent commits will be left unchanged. However all commits hashes will still be different
99+
due to the filtered parents.
100+
94101
Filter order matters
95102
--------------------
96103

‎src/filter/grammar.pest

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ char = {
1818

1919
filter_spec = { (
2020
filter_group
21+
| filter_rev
2122
| filter_presub
2223
| filter_subdir
2324
| filter_nop
@@ -32,6 +33,15 @@ filter_presub = { CMD_START ~ ":" ~ argument }
3233
filter = { CMD_START ~ cmd ~ "=" ~ (argument ~ (";" ~ argument)*)? }
3334
filter_noarg = { CMD_START ~ cmd }
3435

36+
filter_rev = {
37+
CMD_START ~ "rev" ~ "("
38+
~ NEWLINE*
39+
~ (argument ~ filter_spec)?
40+
~ (CMD_SEP+ ~ (argument ~ filter_spec))*
41+
~ NEWLINE*
42+
~ ")"
43+
}
44+
3545
argument = { string | PATH }
3646

3747
cmd = { ALNUM+ }

‎src/filter/mod.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ enum Op {
9898
Fold,
9999
Paths,
100100
Squash(Option<std::collections::HashMap<git2::Oid, (String, String, String)>>),
101+
Rev(std::collections::HashMap<git2::Oid, Filter>),
101102
Linear,
102103

103104
RegexReplace(regex::Regex, String),
@@ -194,6 +195,13 @@ fn nesting2(op: &Op) -> usize {
194195
Op::Workspace(_) => usize::MAX,
195196
Op::Chain(a, b) => 1 + nesting(*a).max(nesting(*b)),
196197
Op::Subtract(a, b) => 1 + nesting(*a).max(nesting(*b)),
198+
Op::Rev(filters) => {
199+
1 + filters
200+
.values()
201+
.map(|filter| nesting(*filter))
202+
.max()
203+
.unwrap_or(0)
204+
}
197205
_ => 0,
198206
}
199207
}
@@ -223,12 +231,20 @@ fn spec2(op: &Op) -> String {
223231
Op::Exclude(b) => {
224232
format!(":exclude[{}]", spec(*b))
225233
}
234+
Op::Rev(filters) => {
235+
let mut v = filters
236+
.iter()
237+
.map(|(k, v)| format!("{}{}", k, spec(*v)))
238+
.collect::<Vec<_>>();
239+
v.sort();
240+
format!(":rev({})", v.join(","))
241+
}
226242
Op::Workspace(path) => {
227243
format!(":workspace={}", parse::quote(&path.to_string_lossy()))
228244
}
229245
Op::RegexReplace(regex, replacement) => {
230246
format!(
231-
":replace={},{}",
247+
":replace={};{}",
232248
parse::quote(&regex.to_string()),
233249
parse::quote(&replacement)
234250
)
@@ -384,6 +400,27 @@ fn apply_to_commit2(
384400
rs_tracing::trace_scoped!("apply_to_commit", "spec": spec(filter), "commit": commit.id().to_string());
385401

386402
let filtered_tree = match &to_op(filter) {
403+
Op::Rev(filters) => {
404+
let nf = *filters
405+
.get(&git2::Oid::zero())
406+
.unwrap_or(&to_filter(Op::Nop));
407+
408+
for (id, startfilter) in filters {
409+
if *id == commit.id() {
410+
let mut f2 = filters.clone();
411+
f2.remove(id);
412+
f2.insert(git2::Oid::zero(), *startfilter);
413+
if let Some(start) = apply_to_commit2(&Op::Rev(f2), &commit, transaction)? {
414+
transaction.insert(filter, commit.id(), start, true);
415+
return Ok(Some(start));
416+
} else {
417+
return Ok(None);
418+
}
419+
}
420+
}
421+
422+
apply(transaction, nf, commit.tree()?)?
423+
}
387424
Op::Squash(Some(ids)) => {
388425
if let Some(_) = ids.get(&commit.id()) {
389426
commit.tree()?
@@ -619,7 +656,7 @@ fn apply2<'a>(
619656
Op::Squash(None) => Ok(tree),
620657
Op::Squash(Some(_)) => Err(josh_error("not applicable to tree")),
621658
Op::Linear => Ok(tree),
622-
659+
Op::Rev(_) => Err(josh_error("not applicable to tree")),
623660
Op::RegexReplace(regex, replacement) => {
624661
tree::regex_replace(tree.id(), &regex, &replacement, transaction)
625662
}
@@ -712,7 +749,11 @@ pub fn unapply<'a>(
712749
parent_tree: git2::Tree<'a>,
713750
) -> JoshResult<git2::Tree<'a>> {
714751
if let Ok(inverted) = invert(filter) {
715-
let matching = apply(transaction, chain(filter, inverted), parent_tree.clone())?;
752+
let matching = apply(
753+
transaction,
754+
chain(invert(inverted)?, inverted),
755+
parent_tree.clone(),
756+
)?;
716757
let stripped = tree::subtract(transaction, parent_tree.id(), matching.id())?;
717758
let new_tree = apply(transaction, inverted, tree)?;
718759

@@ -733,7 +774,8 @@ pub fn unapply<'a>(
733774
}
734775

735776
if let Op::Chain(a, b) = to_op(filter) {
736-
let p = apply(transaction, a, parent_tree.clone())?;
777+
let i = if let Ok(i) = invert(a) { invert(i)? } else { a };
778+
let p = apply(transaction, i, parent_tree.clone())?;
737779
return unapply(
738780
transaction,
739781
a,

‎src/filter/opt.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ fn step(filter: Filter) -> Filter {
328328
Op::Prefix(path)
329329
}
330330
}
331+
//Op::Rev(id, filter) => Op::Rev(id, step(filter)),
331332
Op::Compose(filters) if filters.is_empty() => Op::Empty,
332333
Op::Compose(filters) if filters.len() == 1 => to_op(filters[0]),
333334
Op::Compose(mut filters) => {
@@ -426,6 +427,7 @@ pub fn invert(filter: Filter) -> JoshResult<Filter> {
426427
Op::File(path) => Some(Op::File(path)),
427428
Op::Prefix(path) => Some(Op::Subdir(path)),
428429
Op::Glob(pattern) => Some(Op::Glob(pattern)),
430+
Op::Rev(_) => Some(Op::Nop),
429431
_ => None,
430432
};
431433

‎src/filter/parse.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::*;
22
use indoc::{formatdoc, indoc};
3+
use itertools::Itertools;
34

45
fn make_op(args: &[&str]) -> JoshResult<Op> {
56
match args {
@@ -103,6 +104,17 @@ fn parse_item(pair: pest::iterators::Pair<Rule>) -> JoshResult<Op> {
103104
_ => Err(josh_error("parse_item: no match {:?}")),
104105
}
105106
}
107+
Rule::filter_rev => {
108+
let v: Vec<_> = pair.into_inner().map(|x| unquote(x.as_str())).collect();
109+
110+
let hm = v
111+
.iter()
112+
.tuples()
113+
.map(|(oid, filter)| Ok((git2::Oid::from_str(oid)?, parse(filter)?)))
114+
.collect::<JoshResult<_>>()?;
115+
116+
Ok(Op::Rev(hm))
117+
}
106118
_ => Err(josh_error("parse_item: no match")),
107119
}
108120
}

‎tests/filter/start.t

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
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 branch2
12+
Switched to a new branch 'branch2'
13+
14+
$ echo contents2 > file1
15+
$ git add .
16+
$ git commit -m "mod file1" 1> /dev/null
17+
18+
$ echo contents3 > file3
19+
$ git add .
20+
$ git commit -m "mod file3" 1> /dev/null
21+
22+
$ git checkout master
23+
Switched to branch 'master'
24+
25+
$ echo contents3 > file2
26+
$ git add .
27+
$ git commit -m "add file2" 1> /dev/null
28+
29+
$ git merge -q branch2 --no-ff
30+
31+
$ git log --graph --pretty=%H
32+
* 1d69b7d2651f744be3416f2ad526aeccefb99310
33+
|\
34+
| * 86871b8775ad3baca86484337d1072aa1d386f7e
35+
| * 975d4c4975912729482cc864d321c5196a969271
36+
* | e707f76bb6a1390f28b2162da5b5eb6933009070
37+
|/
38+
* 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb
39+
$ josh-filter -s :prefix=x/y --update refs/heads/filtered
40+
[5] :prefix=x
41+
[5] :prefix=y
42+
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
43+
* 37f8b29c9e892ea0eb7abac2759ddc6fb0337203:dcbbddf47649f8e73f59fae92896c0d2cd02b6ec
44+
|\
45+
| * 714ed7037ce6a45f7342e2cc1a9bb644bb616c45:67e0ba73689ea02220cb270c5b5db564e520fce3
46+
| * cc0382917c6488d69dca4d6a147d55251b06ac08:8408d8fc882cba8e945b16bc69e3b475d65ecbeb
47+
* | 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44
48+
|/
49+
* 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
50+
51+
$ josh-filter -s ":rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)" --update refs/heads/filtered
52+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y)
53+
[5] :prefix=x
54+
[5] :prefix=y
55+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
56+
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
57+
* 8b4097f3318cdf47e46266fc7fef5331bf189b6c:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
58+
|\
59+
| * ee931ac07e4a953d1d2e0f65968946f5c09b0f4c:5d0da4f47308da86193b53b3374f5630c5a0fa3e
60+
| * cc0382917c6488d69dca4d6a147d55251b06ac08:8408d8fc882cba8e945b16bc69e3b475d65ecbeb
61+
| * 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
62+
* e707f76bb6a1390f28b2162da5b5eb6933009070:5d8a699f74b48c9c595f4615dd3755244e11d176
63+
* 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:3d77ff51363c9825cc2a221fc0ba5a883a1a2c72
64+
65+
66+
$ josh-filter -s ":rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)" --update refs/heads/filtered
67+
[3] :rev(0000000000000000000000000000000000000000:prefix=x/y)
68+
[5] :prefix=x
69+
[5] :prefix=y
70+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
71+
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
72+
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
73+
* dbc12216fd70cd41937b99940b1f74dde60b4f44:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
74+
|\
75+
| * 86871b8775ad3baca86484337d1072aa1d386f7e:5d0da4f47308da86193b53b3374f5630c5a0fa3e
76+
| * 975d4c4975912729482cc864d321c5196a969271:de6937d89a7433c80125962616db5dca6c206d9d
77+
| * 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:3d77ff51363c9825cc2a221fc0ba5a883a1a2c72
78+
* 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44
79+
* 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
80+
$ cat > filter.josh <<EOF
81+
> :rev(
82+
> e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y
83+
> 975d4c4975912729482cc864d321c5196a969271:prefix=x/y
84+
> )
85+
> EOF
86+
$ josh-filter -s --file filter.josh --update refs/heads/filtered
87+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
88+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
89+
[3] :rev(0000000000000000000000000000000000000000:prefix=x/y)
90+
[5] :prefix=x
91+
[5] :prefix=y
92+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
93+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
94+
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
95+
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
96+
* 63fea1234f375bd09019b676da8291f28d2ddb43:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
97+
|\
98+
| * ee931ac07e4a953d1d2e0f65968946f5c09b0f4c:5d0da4f47308da86193b53b3374f5630c5a0fa3e
99+
| * cc0382917c6488d69dca4d6a147d55251b06ac08:8408d8fc882cba8e945b16bc69e3b475d65ecbeb
100+
* | 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44
101+
|/
102+
* 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
103+
$ cat > filter.josh <<EOF
104+
> :rev(
105+
> e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y
106+
> 975d4c4975912729482cc864d321c5196a969271:prefix=x/z
107+
> )
108+
> EOF
109+
$ josh-filter -s --file filter.josh --update refs/heads/filtered
110+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
111+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z)
112+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
113+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
114+
[3] :rev(0000000000000000000000000000000000000000:prefix=x/y)
115+
[5] :prefix=x
116+
[5] :prefix=y
117+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
118+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
119+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
120+
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
121+
$ cat > filter.josh <<EOF
122+
> :rev(
123+
> e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y
124+
> 975d4c4975912729482cc864d321c5196a969271:prefix=x/z
125+
> )
126+
> EOF
127+
$ josh-filter -s --file filter.josh --update refs/heads/filtered
128+
Warning: reference refs/heads/filtered wasn't updated
129+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
130+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z)
131+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
132+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
133+
[3] :rev(0000000000000000000000000000000000000000:prefix=x/y)
134+
[5] :prefix=x
135+
[5] :prefix=y
136+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
137+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
138+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
139+
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
140+
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
141+
* e8b8c260e894186db18bffef15da3f5d292902f8:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
142+
|\
143+
| * d817c466a639fca29059705144ef9f63e194c3b5:5d0da4f47308da86193b53b3374f5630c5a0fa3e
144+
| * 28b0f8962384c35ff4f370c0fb8d75bc9b035248:b9d380f578c1cb2bb5039977f64ccf1a804a91de
145+
| * 26cbb56df84c5e9fdce7afc7855025862e835ee2:105b58b790c53d350e23a51ad763a88e6b977ae7
146+
* 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44
147+
* 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
148+
149+
$ josh-filter -s :linear --update refs/heads/filtered
150+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
151+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z)
152+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
153+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
154+
[3] :linear
155+
[3] :rev(0000000000000000000000000000000000000000:prefix=x/y)
156+
[5] :prefix=x
157+
[5] :prefix=y
158+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
159+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
160+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
161+
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
162+
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
163+
* f8e8bc9daf54340c9fce647be467d2577b623bbe:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
164+
* e707f76bb6a1390f28b2162da5b5eb6933009070:5d8a699f74b48c9c595f4615dd3755244e11d176
165+
* 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:3d77ff51363c9825cc2a221fc0ba5a883a1a2c72
166+
167+
$ git diff --stat ${EMPTY_TREE}..f8e8bc9daf54340c9fce647be467d2577b623bbe
168+
file1 | 1 +
169+
file2 | 1 +
170+
file3 | 1 +
171+
3 files changed, 3 insertions(+)
172+
$ git diff --stat ${EMPTY_TREE}..e707f76bb6a1390f28b2162da5b5eb6933009070
173+
file1 | 1 +
174+
file2 | 1 +
175+
2 files changed, 2 insertions(+)
176+
$ git diff --stat ${EMPTY_TREE}..0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb
177+
file1 | 1 +
178+
1 file changed, 1 insertion(+)
179+
180+
$ cat > filter.josh <<EOF
181+
> :linear:rev(
182+
> 0000000000000000000000000000000000000000:prefix=x
183+
> e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=y
184+
> 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:prefix=z
185+
> )
186+
> EOF
187+
$ josh-filter -s --file filter.josh --update refs/heads/filtered
188+
[1] :rev(0000000000000000000000000000000000000000:prefix=z)
189+
[1] :rev(0000000000000000000000000000000000000000:prefix=z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=y)
190+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
191+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z)
192+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
193+
[2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
194+
[2] :rev(0000000000000000000000000000000000000000:prefix=y,0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:prefix=z)
195+
[3] :linear
196+
[3] :rev(0000000000000000000000000000000000000000:prefix=x,0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:prefix=z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=y)
197+
[3] :rev(0000000000000000000000000000000000000000:prefix=x/y)
198+
[5] :prefix=x
199+
[5] :prefix=y
200+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
201+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
202+
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
203+
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
204+
205+
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
206+
* 2944f04c33ea037f7696282bf20b2e570524552e:047b1b6f39e8d95b62ef7f136189005d0e3c80b3
207+
* 3c2304baa035aa9c8e7e0f1fff5d7410be55f069:6300cae79def8ee31701b104857ff4338b6079aa
208+
* 67480de4b94241494bfb0d7f606d421d8ed4f7e6:2fd6d8f78756533e937e3f168eb58e0fd8b1512c
209+
210+
$ git diff --stat ${EMPTY_TREE}..refs/heads/filtered
211+
x/file1 | 1 +
212+
x/file2 | 1 +
213+
x/file3 | 1 +
214+
3 files changed, 3 insertions(+)
215+
$ git diff --stat ${EMPTY_TREE}..refs/heads/filtered~1
216+
y/file1 | 1 +
217+
y/file2 | 1 +
218+
2 files changed, 2 insertions(+)
219+
$ git diff --stat ${EMPTY_TREE}..refs/heads/filtered~2
220+
z/file1 | 1 +
221+
1 file changed, 1 insertion(+)

‎tests/filter/subtree_prefix.t

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
$ git init -q 1>/dev/null
2+
3+
Initial commit of main branch
4+
$ echo contents1 > file1
5+
$ git add .
6+
$ git commit -m "add file1" 1>/dev/null
7+
8+
Initial commit of subtree branch
9+
$ git checkout --orphan subtree
10+
Switched to a new branch 'subtree'
11+
$ rm file*
12+
$ echo contents2 > file2
13+
$ git add .
14+
$ git commit -m "add file2 (in subtree)" 1>/dev/null
15+
$ export SUBTREE_TIP=$(git rev-parse HEAD)
16+
17+
Articially create a subtree merge
18+
(merge commit has subtree files in subfolder but has subtree commit as a parent)
19+
$ git checkout master
20+
Switched to branch 'master'
21+
$ git merge subtree --allow-unrelated-histories 1>/dev/null
22+
$ mkdir subtree
23+
$ git mv file2 subtree/
24+
$ git add subtree
25+
$ git commit -a --amend -m "subtree merge" 1>/dev/null
26+
$ tree
27+
.
28+
|-- file1
29+
`-- subtree
30+
`-- file2
31+
32+
1 directory, 2 files
33+
$ git log --graph --pretty=%s
34+
* subtree merge
35+
|\
36+
| * add file2 (in subtree)
37+
* add file1
38+
39+
Change subtree file
40+
$ echo more contents >> subtree/file2
41+
$ git commit -a -m "subtree edit from main repo" 1>/dev/null
42+
43+
Rewrite the subtree part of the history
44+
$ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree)" refs/heads/master --update refs/heads/filtered
45+
[1] :prefix=subtree
46+
[4] :rev(c036f944faafb865e0585e4fa5e005afa0aeea3f:prefix=subtree)
47+
48+
$ git log --graph --pretty=%s refs/heads/filtered
49+
* subtree edit from main repo
50+
* subtree merge
51+
|\
52+
| * add file2 (in subtree)
53+
* add file1
54+
55+
Compare input and result. ^^2 is the 2nd parent of the first parent, i.e., the 'in subtree' commit.
56+
$ git ls-tree --name-only -r refs/heads/filtered
57+
file1
58+
subtree/file2
59+
$ git diff refs/heads/master refs/heads/filtered
60+
$ git ls-tree --name-only -r refs/heads/filtered^^2
61+
subtree/file2
62+
$ git diff refs/heads/master^^2 refs/heads/filtered^^2
63+
diff --git a/file2 b/subtree/file2
64+
similarity index 100%
65+
rename from file2
66+
rename to subtree/file2
67+
68+
Extract the subtree history
69+
$ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree):/subtree" refs/heads/master --update refs/heads/subtree
70+
[1] :prefix=subtree
71+
[4] :/subtree
72+
[4] :rev(c036f944faafb865e0585e4fa5e005afa0aeea3f:prefix=subtree)
73+
$ git checkout subtree
74+
Switched to branch 'subtree'
75+
$ cat file2
76+
contents2
77+
more contents
78+
79+
Work in the subtree, and sync that back.
80+
$ echo even more contents >> file2
81+
$ git commit -am "add even more content" 1>/dev/null
82+
$ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree):/subtree" refs/heads/master --update refs/heads/subtree --reverse
83+
[1] :prefix=subtree
84+
[4] :/subtree
85+
[4] :rev(c036f944faafb865e0585e4fa5e005afa0aeea3f:prefix=subtree)
86+
$ git log --graph --pretty=%s refs/heads/master
87+
* add even more content
88+
* subtree edit from main repo
89+
* subtree merge
90+
|\
91+
| * add file2 (in subtree)
92+
* add file1
93+
$ git ls-tree --name-only -r refs/heads/master
94+
file1
95+
subtree/file2
96+
$ git checkout master
97+
Switched to branch 'master'
98+
$ cat subtree/file2
99+
contents2
100+
more contents
101+
even more contents
102+
103+
And then re-extract, which should re-construct the same subtree.
104+
$ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree):/subtree" refs/heads/master --update refs/heads/subtree2
105+
[1] :prefix=subtree
106+
[5] :/subtree
107+
[5] :rev(c036f944faafb865e0585e4fa5e005afa0aeea3f:prefix=subtree)
108+
$ test $(git rev-parse subtree) = $(git rev-parse subtree2)

‎tests/proxy/workspace_errors.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ Error in filter
104104
remote: 1 | a/b = :b/sub2
105105
remote: | ^---
106106
remote: |
107-
remote: = expected EOI, filter_group, filter_subdir, filter_nop, filter_presub, filter, or filter_noarg
107+
remote: = expected EOI, filter_group, filter_subdir, filter_nop, filter_presub, filter, filter_noarg, or filter_rev
108108
remote:
109109
remote: a/b = :b/sub2
110110
remote: c = :/sub1

0 commit comments

Comments
 (0)
Please sign in to comment.