From e48826e58ff93c3d202decdc1cc07842bef9f77a Mon Sep 17 00:00:00 2001 From: Christian Schilling Date: Sun, 2 Oct 2022 12:13:36 +0200 Subject: [PATCH] Add :rev(...) filter Change: start-filter --- Cargo.lock | 10 ++ Cargo.toml | 1 + docs/src/reference/filters.md | 7 ++ src/filter/grammar.pest | 10 ++ src/filter/mod.rs | 50 +++++++- src/filter/opt.rs | 2 + src/filter/parse.rs | 12 ++ tests/filter/start.t | 221 +++++++++++++++++++++++++++++++++ tests/filter/subtree_prefix.t | 108 ++++++++++++++++ tests/proxy/workspace_errors.t | 2 +- 10 files changed, 418 insertions(+), 5 deletions(-) create mode 100644 tests/filter/start.t create mode 100644 tests/filter/subtree_prefix.t diff --git a/Cargo.lock b/Cargo.lock index 244ac7a9d..b5e95eca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1005,6 +1005,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.4" @@ -1038,6 +1047,7 @@ dependencies = [ "hex", "hyper-reverse-proxy", "indoc", + "itertools", "juniper", "lazy_static", "log", diff --git a/Cargo.toml b/Cargo.toml index 27f537912..eff5eb36b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ handlebars = "4.3.5" hex = "0.4.3" hyper-reverse-proxy = "0.5.1" indoc = "1.0.7" +itertools = "0.9" juniper = "0.15.10" lazy_static = "1.4.0" log = "0.4.17" diff --git a/docs/src/reference/filters.md b/docs/src/reference/filters.md index 5091bf29a..cd97cc690 100644 --- a/docs/src/reference/filters.md +++ b/docs/src/reference/filters.md @@ -91,6 +91,13 @@ These filter do not modify git trees, but instead only operate on the commit gra Produce a filtered history that does not contain any merge commits. This is done by simply dropping all parents except the first on every commit. +### Filter specific part of the history **:at_commit=[:filter]** +Produce a history where the commit specified by `` is replaced by the result of applying +`:filter` to it. +This means also all parents of this specific commit appear filtered with `:filter` and all +descendent commits will be left unchanged. However all commits hashes will still be different +due to the filtered parents. + Filter order matters -------------------- diff --git a/src/filter/grammar.pest b/src/filter/grammar.pest index 77d3a4444..f2b0ea35c 100644 --- a/src/filter/grammar.pest +++ b/src/filter/grammar.pest @@ -18,6 +18,7 @@ char = { filter_spec = { ( filter_group + | filter_rev | filter_presub | filter_subdir | filter_nop @@ -32,6 +33,15 @@ filter_presub = { CMD_START ~ ":" ~ argument } filter = { CMD_START ~ cmd ~ "=" ~ (argument ~ (";" ~ argument)*)? } filter_noarg = { CMD_START ~ cmd } +filter_rev = { + CMD_START ~ "rev" ~ "(" + ~ NEWLINE* + ~ (argument ~ filter_spec)? + ~ (CMD_SEP+ ~ (argument ~ filter_spec))* + ~ NEWLINE* + ~ ")" +} + argument = { string | PATH } cmd = { ALNUM+ } diff --git a/src/filter/mod.rs b/src/filter/mod.rs index 54e41ca3d..c394e8110 100644 --- a/src/filter/mod.rs +++ b/src/filter/mod.rs @@ -98,6 +98,7 @@ enum Op { Fold, Paths, Squash(Option>), + Rev(std::collections::HashMap), Linear, RegexReplace(regex::Regex, String), @@ -194,6 +195,13 @@ fn nesting2(op: &Op) -> usize { Op::Workspace(_) => usize::MAX, Op::Chain(a, b) => 1 + nesting(*a).max(nesting(*b)), Op::Subtract(a, b) => 1 + nesting(*a).max(nesting(*b)), + Op::Rev(filters) => { + 1 + filters + .values() + .map(|filter| nesting(*filter)) + .max() + .unwrap_or(0) + } _ => 0, } } @@ -223,12 +231,20 @@ fn spec2(op: &Op) -> String { Op::Exclude(b) => { format!(":exclude[{}]", spec(*b)) } + Op::Rev(filters) => { + let mut v = filters + .iter() + .map(|(k, v)| format!("{}{}", k, spec(*v))) + .collect::>(); + v.sort(); + format!(":rev({})", v.join(",")) + } Op::Workspace(path) => { format!(":workspace={}", parse::quote(&path.to_string_lossy())) } Op::RegexReplace(regex, replacement) => { format!( - ":replace={},{}", + ":replace={};{}", parse::quote(®ex.to_string()), parse::quote(&replacement) ) @@ -384,6 +400,27 @@ fn apply_to_commit2( rs_tracing::trace_scoped!("apply_to_commit", "spec": spec(filter), "commit": commit.id().to_string()); let filtered_tree = match &to_op(filter) { + Op::Rev(filters) => { + let nf = *filters + .get(&git2::Oid::zero()) + .unwrap_or(&to_filter(Op::Nop)); + + for (id, startfilter) in filters { + if *id == commit.id() { + let mut f2 = filters.clone(); + f2.remove(id); + f2.insert(git2::Oid::zero(), *startfilter); + if let Some(start) = apply_to_commit2(&Op::Rev(f2), &commit, transaction)? { + transaction.insert(filter, commit.id(), start, true); + return Ok(Some(start)); + } else { + return Ok(None); + } + } + } + + apply(transaction, nf, commit.tree()?)? + } Op::Squash(Some(ids)) => { if let Some(_) = ids.get(&commit.id()) { commit.tree()? @@ -619,7 +656,7 @@ fn apply2<'a>( Op::Squash(None) => Ok(tree), Op::Squash(Some(_)) => Err(josh_error("not applicable to tree")), Op::Linear => Ok(tree), - + Op::Rev(_) => Err(josh_error("not applicable to tree")), Op::RegexReplace(regex, replacement) => { tree::regex_replace(tree.id(), ®ex, &replacement, transaction) } @@ -712,7 +749,11 @@ pub fn unapply<'a>( parent_tree: git2::Tree<'a>, ) -> JoshResult> { if let Ok(inverted) = invert(filter) { - let matching = apply(transaction, chain(filter, inverted), parent_tree.clone())?; + let matching = apply( + transaction, + chain(invert(inverted)?, inverted), + parent_tree.clone(), + )?; let stripped = tree::subtract(transaction, parent_tree.id(), matching.id())?; let new_tree = apply(transaction, inverted, tree)?; @@ -733,7 +774,8 @@ pub fn unapply<'a>( } if let Op::Chain(a, b) = to_op(filter) { - let p = apply(transaction, a, parent_tree.clone())?; + let i = if let Ok(i) = invert(a) { invert(i)? } else { a }; + let p = apply(transaction, i, parent_tree.clone())?; return unapply( transaction, a, diff --git a/src/filter/opt.rs b/src/filter/opt.rs index 9ed8d9574..a1165b3eb 100644 --- a/src/filter/opt.rs +++ b/src/filter/opt.rs @@ -328,6 +328,7 @@ fn step(filter: Filter) -> Filter { Op::Prefix(path) } } + //Op::Rev(id, filter) => Op::Rev(id, step(filter)), Op::Compose(filters) if filters.is_empty() => Op::Empty, Op::Compose(filters) if filters.len() == 1 => to_op(filters[0]), Op::Compose(mut filters) => { @@ -426,6 +427,7 @@ pub fn invert(filter: Filter) -> JoshResult { Op::File(path) => Some(Op::File(path)), Op::Prefix(path) => Some(Op::Subdir(path)), Op::Glob(pattern) => Some(Op::Glob(pattern)), + Op::Rev(_) => Some(Op::Nop), _ => None, }; diff --git a/src/filter/parse.rs b/src/filter/parse.rs index 4da3ed418..dbf084568 100644 --- a/src/filter/parse.rs +++ b/src/filter/parse.rs @@ -1,5 +1,6 @@ use super::*; use indoc::{formatdoc, indoc}; +use itertools::Itertools; fn make_op(args: &[&str]) -> JoshResult { match args { @@ -103,6 +104,17 @@ fn parse_item(pair: pest::iterators::Pair) -> JoshResult { _ => Err(josh_error("parse_item: no match {:?}")), } } + Rule::filter_rev => { + let v: Vec<_> = pair.into_inner().map(|x| unquote(x.as_str())).collect(); + + let hm = v + .iter() + .tuples() + .map(|(oid, filter)| Ok((git2::Oid::from_str(oid)?, parse(filter)?))) + .collect::>()?; + + Ok(Op::Rev(hm)) + } _ => Err(josh_error("parse_item: no match")), } } diff --git a/tests/filter/start.t b/tests/filter/start.t new file mode 100644 index 000000000..c695eeb40 --- /dev/null +++ b/tests/filter/start.t @@ -0,0 +1,221 @@ + $ export RUST_BACKTRACE=1 + $ git init -q 1> /dev/null + + $ echo contents1 > file1 + $ git add . + $ git commit -m "add file1" 1> /dev/null + + $ git log --graph --pretty=%s + * add file1 + + $ git checkout -b branch2 + Switched to a new branch 'branch2' + + $ echo contents2 > file1 + $ git add . + $ git commit -m "mod file1" 1> /dev/null + + $ echo contents3 > file3 + $ git add . + $ git commit -m "mod file3" 1> /dev/null + + $ git checkout master + Switched to branch 'master' + + $ echo contents3 > file2 + $ git add . + $ git commit -m "add file2" 1> /dev/null + + $ git merge -q branch2 --no-ff + + $ git log --graph --pretty=%H + * 1d69b7d2651f744be3416f2ad526aeccefb99310 + |\ + | * 86871b8775ad3baca86484337d1072aa1d386f7e + | * 975d4c4975912729482cc864d321c5196a969271 + * | e707f76bb6a1390f28b2162da5b5eb6933009070 + |/ + * 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb + $ josh-filter -s :prefix=x/y --update refs/heads/filtered + [5] :prefix=x + [5] :prefix=y + $ git log --graph --decorate --pretty=%H:%T refs/heads/filtered + * 37f8b29c9e892ea0eb7abac2759ddc6fb0337203:dcbbddf47649f8e73f59fae92896c0d2cd02b6ec + |\ + | * 714ed7037ce6a45f7342e2cc1a9bb644bb616c45:67e0ba73689ea02220cb270c5b5db564e520fce3 + | * cc0382917c6488d69dca4d6a147d55251b06ac08:8408d8fc882cba8e945b16bc69e3b475d65ecbeb + * | 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44 + |/ + * 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54 + + $ josh-filter -s ":rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)" --update refs/heads/filtered + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y) + [5] :prefix=x + [5] :prefix=y + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + $ git log --graph --decorate --pretty=%H:%T refs/heads/filtered + * 8b4097f3318cdf47e46266fc7fef5331bf189b6c:5f47d9fdffdc726bb8ebcfea67531d2574243c5d + |\ + | * ee931ac07e4a953d1d2e0f65968946f5c09b0f4c:5d0da4f47308da86193b53b3374f5630c5a0fa3e + | * cc0382917c6488d69dca4d6a147d55251b06ac08:8408d8fc882cba8e945b16bc69e3b475d65ecbeb + | * 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54 + * e707f76bb6a1390f28b2162da5b5eb6933009070:5d8a699f74b48c9c595f4615dd3755244e11d176 + * 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:3d77ff51363c9825cc2a221fc0ba5a883a1a2c72 + + + $ josh-filter -s ":rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)" --update refs/heads/filtered + [3] :rev(0000000000000000000000000000000000000000:prefix=x/y) + [5] :prefix=x + [5] :prefix=y + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + $ git log --graph --decorate --pretty=%H:%T refs/heads/filtered + * dbc12216fd70cd41937b99940b1f74dde60b4f44:5f47d9fdffdc726bb8ebcfea67531d2574243c5d + |\ + | * 86871b8775ad3baca86484337d1072aa1d386f7e:5d0da4f47308da86193b53b3374f5630c5a0fa3e + | * 975d4c4975912729482cc864d321c5196a969271:de6937d89a7433c80125962616db5dca6c206d9d + | * 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:3d77ff51363c9825cc2a221fc0ba5a883a1a2c72 + * 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44 + * 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54 + $ cat > filter.josh < :rev( + > e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y + > 975d4c4975912729482cc864d321c5196a969271:prefix=x/y + > ) + > EOF + $ josh-filter -s --file filter.josh --update refs/heads/filtered + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [3] :rev(0000000000000000000000000000000000000000:prefix=x/y) + [5] :prefix=x + [5] :prefix=y + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + $ git log --graph --decorate --pretty=%H:%T refs/heads/filtered + * 63fea1234f375bd09019b676da8291f28d2ddb43:5f47d9fdffdc726bb8ebcfea67531d2574243c5d + |\ + | * ee931ac07e4a953d1d2e0f65968946f5c09b0f4c:5d0da4f47308da86193b53b3374f5630c5a0fa3e + | * cc0382917c6488d69dca4d6a147d55251b06ac08:8408d8fc882cba8e945b16bc69e3b475d65ecbeb + * | 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44 + |/ + * 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54 + $ cat > filter.josh < :rev( + > e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y + > 975d4c4975912729482cc864d321c5196a969271:prefix=x/z + > ) + > EOF + $ josh-filter -s --file filter.josh --update refs/heads/filtered + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [3] :rev(0000000000000000000000000000000000000000:prefix=x/y) + [5] :prefix=x + [5] :prefix=y + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + $ cat > filter.josh < :rev( + > e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y + > 975d4c4975912729482cc864d321c5196a969271:prefix=x/z + > ) + > EOF + $ josh-filter -s --file filter.josh --update refs/heads/filtered + Warning: reference refs/heads/filtered wasn't updated + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [3] :rev(0000000000000000000000000000000000000000:prefix=x/y) + [5] :prefix=x + [5] :prefix=y + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + $ git log --graph --decorate --pretty=%H:%T refs/heads/filtered + * e8b8c260e894186db18bffef15da3f5d292902f8:5f47d9fdffdc726bb8ebcfea67531d2574243c5d + |\ + | * d817c466a639fca29059705144ef9f63e194c3b5:5d0da4f47308da86193b53b3374f5630c5a0fa3e + | * 28b0f8962384c35ff4f370c0fb8d75bc9b035248:b9d380f578c1cb2bb5039977f64ccf1a804a91de + | * 26cbb56df84c5e9fdce7afc7855025862e835ee2:105b58b790c53d350e23a51ad763a88e6b977ae7 + * 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44 + * 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54 + + $ josh-filter -s :linear --update refs/heads/filtered + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [3] :linear + [3] :rev(0000000000000000000000000000000000000000:prefix=x/y) + [5] :prefix=x + [5] :prefix=y + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + $ git log --graph --decorate --pretty=%H:%T refs/heads/filtered + * f8e8bc9daf54340c9fce647be467d2577b623bbe:5f47d9fdffdc726bb8ebcfea67531d2574243c5d + * e707f76bb6a1390f28b2162da5b5eb6933009070:5d8a699f74b48c9c595f4615dd3755244e11d176 + * 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:3d77ff51363c9825cc2a221fc0ba5a883a1a2c72 + + $ git diff --stat ${EMPTY_TREE}..f8e8bc9daf54340c9fce647be467d2577b623bbe + file1 | 1 + + file2 | 1 + + file3 | 1 + + 3 files changed, 3 insertions(+) + $ git diff --stat ${EMPTY_TREE}..e707f76bb6a1390f28b2162da5b5eb6933009070 + file1 | 1 + + file2 | 1 + + 2 files changed, 2 insertions(+) + $ git diff --stat ${EMPTY_TREE}..0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb + file1 | 1 + + 1 file changed, 1 insertion(+) + + $ cat > filter.josh < :linear:rev( + > 0000000000000000000000000000000000000000:prefix=x + > e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=y + > 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:prefix=z + > ) + > EOF + $ josh-filter -s --file filter.josh --update refs/heads/filtered + [1] :rev(0000000000000000000000000000000000000000:prefix=z) + [1] :rev(0000000000000000000000000000000000000000:prefix=z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=y) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [2] :rev(0000000000000000000000000000000000000000:prefix=y,0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:prefix=z) + [3] :linear + [3] :rev(0000000000000000000000000000000000000000:prefix=x,0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:prefix=z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=y) + [3] :rev(0000000000000000000000000000000000000000:prefix=x/y) + [5] :prefix=x + [5] :prefix=y + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y) + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + [5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y) + + $ git log --graph --decorate --pretty=%H:%T refs/heads/filtered + * 2944f04c33ea037f7696282bf20b2e570524552e:047b1b6f39e8d95b62ef7f136189005d0e3c80b3 + * 3c2304baa035aa9c8e7e0f1fff5d7410be55f069:6300cae79def8ee31701b104857ff4338b6079aa + * 67480de4b94241494bfb0d7f606d421d8ed4f7e6:2fd6d8f78756533e937e3f168eb58e0fd8b1512c + + $ git diff --stat ${EMPTY_TREE}..refs/heads/filtered + x/file1 | 1 + + x/file2 | 1 + + x/file3 | 1 + + 3 files changed, 3 insertions(+) + $ git diff --stat ${EMPTY_TREE}..refs/heads/filtered~1 + y/file1 | 1 + + y/file2 | 1 + + 2 files changed, 2 insertions(+) + $ git diff --stat ${EMPTY_TREE}..refs/heads/filtered~2 + z/file1 | 1 + + 1 file changed, 1 insertion(+) diff --git a/tests/filter/subtree_prefix.t b/tests/filter/subtree_prefix.t new file mode 100644 index 000000000..28510f24d --- /dev/null +++ b/tests/filter/subtree_prefix.t @@ -0,0 +1,108 @@ + $ git init -q 1>/dev/null + +Initial commit of main branch + $ echo contents1 > file1 + $ git add . + $ git commit -m "add file1" 1>/dev/null + +Initial commit of subtree branch + $ git checkout --orphan subtree + Switched to a new branch 'subtree' + $ rm file* + $ echo contents2 > file2 + $ git add . + $ git commit -m "add file2 (in subtree)" 1>/dev/null + $ export SUBTREE_TIP=$(git rev-parse HEAD) + +Articially create a subtree merge +(merge commit has subtree files in subfolder but has subtree commit as a parent) + $ git checkout master + Switched to branch 'master' + $ git merge subtree --allow-unrelated-histories 1>/dev/null + $ mkdir subtree + $ git mv file2 subtree/ + $ git add subtree + $ git commit -a --amend -m "subtree merge" 1>/dev/null + $ tree + . + |-- file1 + `-- subtree + `-- file2 + + 1 directory, 2 files + $ git log --graph --pretty=%s + * subtree merge + |\ + | * add file2 (in subtree) + * add file1 + +Change subtree file + $ echo more contents >> subtree/file2 + $ git commit -a -m "subtree edit from main repo" 1>/dev/null + +Rewrite the subtree part of the history + $ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree)" refs/heads/master --update refs/heads/filtered + [1] :prefix=subtree + [4] :rev(c036f944faafb865e0585e4fa5e005afa0aeea3f:prefix=subtree) + + $ git log --graph --pretty=%s refs/heads/filtered + * subtree edit from main repo + * subtree merge + |\ + | * add file2 (in subtree) + * add file1 + +Compare input and result. ^^2 is the 2nd parent of the first parent, i.e., the 'in subtree' commit. + $ git ls-tree --name-only -r refs/heads/filtered + file1 + subtree/file2 + $ git diff refs/heads/master refs/heads/filtered + $ git ls-tree --name-only -r refs/heads/filtered^^2 + subtree/file2 + $ git diff refs/heads/master^^2 refs/heads/filtered^^2 + diff --git a/file2 b/subtree/file2 + similarity index 100% + rename from file2 + rename to subtree/file2 + +Extract the subtree history + $ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree):/subtree" refs/heads/master --update refs/heads/subtree + [1] :prefix=subtree + [4] :/subtree + [4] :rev(c036f944faafb865e0585e4fa5e005afa0aeea3f:prefix=subtree) + $ git checkout subtree + Switched to branch 'subtree' + $ cat file2 + contents2 + more contents + +Work in the subtree, and sync that back. + $ echo even more contents >> file2 + $ git commit -am "add even more content" 1>/dev/null + $ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree):/subtree" refs/heads/master --update refs/heads/subtree --reverse + [1] :prefix=subtree + [4] :/subtree + [4] :rev(c036f944faafb865e0585e4fa5e005afa0aeea3f:prefix=subtree) + $ git log --graph --pretty=%s refs/heads/master + * add even more content + * subtree edit from main repo + * subtree merge + |\ + | * add file2 (in subtree) + * add file1 + $ git ls-tree --name-only -r refs/heads/master + file1 + subtree/file2 + $ git checkout master + Switched to branch 'master' + $ cat subtree/file2 + contents2 + more contents + even more contents + +And then re-extract, which should re-construct the same subtree. + $ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree):/subtree" refs/heads/master --update refs/heads/subtree2 + [1] :prefix=subtree + [5] :/subtree + [5] :rev(c036f944faafb865e0585e4fa5e005afa0aeea3f:prefix=subtree) + $ test $(git rev-parse subtree) = $(git rev-parse subtree2) diff --git a/tests/proxy/workspace_errors.t b/tests/proxy/workspace_errors.t index d928dd78c..2d1ab8dc5 100644 --- a/tests/proxy/workspace_errors.t +++ b/tests/proxy/workspace_errors.t @@ -104,7 +104,7 @@ Error in filter remote: 1 | a/b = :b/sub2 remote: | ^--- remote: | - remote: = expected EOI, filter_group, filter_subdir, filter_nop, filter_presub, filter, or filter_noarg + remote: = expected EOI, filter_group, filter_subdir, filter_nop, filter_presub, filter, filter_noarg, or filter_rev remote: remote: a/b = :b/sub2 remote: c = :/sub1