Skip to content

Commit

Permalink
Implement ":replace" filter
Browse files Browse the repository at this point in the history
Allows replacement inside of files using a regular expression.

Change-Id: regex-replace
  • Loading branch information
christian-schilling committed Jun 24, 2022
1 parent a8834bf commit ca6cc62
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/src/reference/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ The resulting tree will contain the contents of the
workspace root as well as additional files specifed in the ``workspace.josh`` file.
(see [Workspaces](./workspace.md))

### Text replacement **`:replace="regex","replacement"`
Applies the supplied regular expression to every file in the input tree.

## Pattern filters

The following filters accept a glob like pattern ``X`` that can contain ``*`` to
Expand Down
13 changes: 13 additions & 0 deletions src/filter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ enum Op {
Squash,
Linear,

RegexReplace(regex::Regex, String),

#[cfg(feature = "search")]
Index,
Invert,
Expand Down Expand Up @@ -190,6 +192,13 @@ fn spec2(op: &Op) -> String {
Op::Workspace(path) => {
format!(":workspace={}", parse::quote(&path.to_string_lossy()))
}
Op::RegexReplace(regex, replacement) => {
format!(
":replace={},{}",
parse::quote(&regex.to_string()),
parse::quote(&replacement)
)
}

Op::Chain(a, b) => match (to_op(*a), to_op(*b)) {
(Op::Subdir(p1), Op::Prefix(p2)) if p1 == p2 => {
Expand Down Expand Up @@ -526,6 +535,10 @@ fn apply2<'a>(
Op::Squash => Ok(tree),
Op::Linear => Ok(tree),

Op::RegexReplace(regex, replacement) => {
tree::regex_replace(tree.id(), &regex, &replacement, transaction)
}

Op::Glob(pattern) => {
let pattern = glob::Pattern::new(pattern)?;
let options = glob::MatchOptions {
Expand Down
1 change: 1 addition & 0 deletions src/filter/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ pub fn invert(filter: Filter) -> JoshResult<Filter> {
Op::File(path) => Some(Op::File(path)),
Op::Prefix(path) => Some(Op::Subdir(path)),
Op::Glob(pattern) => Some(Op::Glob(pattern)),
Op::RegexReplace(_, _) => Some(Op::Nop),
_ => None,
};

Expand Down
4 changes: 4 additions & 0 deletions src/filter/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ fn make_op(args: &[&str]) -> JoshResult<Op> {
["nop"] => Ok(Op::Nop),
["empty"] => Ok(Op::Empty),
["prefix", arg] => Ok(Op::Prefix(Path::new(arg).to_owned())),
["replace", regex, replacement] => Ok(Op::RegexReplace(
regex::Regex::new(regex)?,
replacement.to_string(),
)),
["workspace", arg] => Ok(Op::Workspace(Path::new(arg).to_owned())),
["prefix"] => Err(josh_error(indoc!(
r#"
Expand Down
37 changes: 37 additions & 0 deletions src/filter/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,43 @@ pub fn pathstree<'a>(
Ok(result)
}

pub fn regex_replace<'a>(
input: git2::Oid,
regex: &regex::Regex,
replacement: &str,
transaction: &'a cache::Transaction,
) -> super::JoshResult<git2::Tree<'a>> {
let repo = transaction.repo();

let tree = repo.find_tree(input)?;
let mut result = tree::empty(repo);

for entry in tree.iter() {
let name = entry.name().ok_or(super::josh_error("no name"))?;
if entry.kind() == Some(git2::ObjectType::Blob) {
let file_contents = get_blob(repo, &tree, std::path::Path::new(&name));
let replaced = regex.replacen(&file_contents, 0, replacement);

result = replace_child(
repo,
std::path::Path::new(name),
repo.blob(replaced.as_bytes())?,
entry.filemode(),
&result,
)?;
}

if entry.kind() == Some(git2::ObjectType::Tree) {
let s = regex_replace(entry.id(), regex, replacement, transaction)?.id();

if s != tree::empty_id() {
result = replace_child(repo, std::path::Path::new(name), s, 0o0040000, &result)?;
}
}
}
Ok(result)
}

pub fn remove_pred<'a>(
transaction: &'a cache::Transaction,
root: &str,
Expand Down
53 changes: 53 additions & 0 deletions tests/filter/replace.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

$ export TESTTMP=${PWD}

$ cd ${TESTTMP}
$ git init -q repo 1>/dev/null
$ cd repo

$ echo "hello world" > hw.txt
$ mkdir subdir
$ echo "hello moon" > subdir/hw.txt

$ git add .
$ git commit -m initial
[master (root-commit) 79f224d] initial
2 files changed, 2 insertions(+)
create mode 100644 hw.txt
create mode 100644 subdir/hw.txt

$ git diff ${EMPTY_TREE}..HEAD
diff --git a/hw.txt b/hw.txt
new file mode 100644
index 0000000..3b18e51
--- /dev/null
+++ b/hw.txt
@@ -0,0 +1 @@
+hello world
diff --git a/subdir/hw.txt b/subdir/hw.txt
new file mode 100644
index 0000000..1b95c6e
--- /dev/null
+++ b/subdir/hw.txt
@@ -0,0 +1 @@
+hello moon

$ josh-filter -p ':replace="hello","bye":replace="^(?P<l>.*(?m))$","$l!"'
:replace=hello,bye:replace="^(?P<l>.*(?m))$","$l!"
$ josh-filter ':replace="hello","bye":replace="(?m)^(?P<l>.+)$","$l!"'

$ git diff ${EMPTY_TREE}..FILTERED_HEAD
diff --git a/hw.txt b/hw.txt
new file mode 100644
index 0000000..9836695
--- /dev/null
+++ b/hw.txt
@@ -0,0 +1 @@
+bye world!
diff --git a/subdir/hw.txt b/subdir/hw.txt
new file mode 100644
index 0000000..cb72486
--- /dev/null
+++ b/subdir/hw.txt
@@ -0,0 +1 @@
+bye moon!

0 comments on commit ca6cc62

Please sign in to comment.