Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions src/filter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ enum Op {
Prefix(std::path::PathBuf),
Subdir(std::path::PathBuf),
Workspace(std::path::PathBuf),
Include(std::path::PathBuf),

Glob(String),

Expand Down Expand Up @@ -169,6 +170,9 @@ fn spec2(op: &Op) -> String {
Op::Workspace(path) => {
format!(":workspace={}", path.to_string_lossy())
}
Op::Include(path) => {
format!(":include={}", path.to_string_lossy())
}

Op::Chain(a, b) => match (to_op(*a), to_op(*b)) {
(Op::Subdir(p1), Op::Prefix(p2)) if p1 == p2 => {
Expand Down Expand Up @@ -354,6 +358,54 @@ fn apply_to_commit2(
))
.transpose();
}
Op::Include(include_path) => {
let normal_parents = commit
.parent_ids()
.map(|parent| transaction.get(filter, parent))
.collect::<Option<Vec<git2::Oid>>>();

let normal_parents = some_or!(normal_parents, { return Ok(None) });

let cw = parse::parse(&tree::get_blob(repo, &commit.tree()?, &include_path))
.unwrap_or(to_filter(Op::Empty));

let extra_parents = commit
.parents()
.map(|parent| {
rs_tracing::trace_scoped!("parent", "id": parent.id().to_string());
let pcw = parse::parse(&tree::get_blob(
repo,
&parent.tree().unwrap_or(tree::empty(repo)),
&include_path,
))
.unwrap_or(to_filter(Op::Empty));

apply_to_commit2(
&to_op(opt::optimize(to_filter(Op::Subtract(cw, pcw)))),
&parent,
transaction,
)
})
.collect::<JoshResult<Option<Vec<_>>>>()?;

let extra_parents = some_or!(extra_parents, { return Ok(None) });

let filtered_parent_ids = normal_parents
.into_iter()
.chain(extra_parents.into_iter())
.collect();

let filtered_tree = apply(transaction, filter, commit.tree()?)?;

return Some(history::create_filtered_commit(
commit,
filtered_parent_ids,
filtered_tree,
transaction,
filter,
))
.transpose();
}
Op::Fold => {
let filtered_parent_ids = commit
.parents()
Expand Down Expand Up @@ -521,6 +573,15 @@ fn apply2<'a>(
}
}

Op::Include(path) => {
let file = to_filter(Op::File(path.to_owned()));
if let Ok(cw) = parse::parse(&tree::get_blob(repo, &tree, &path)) {
apply(transaction, compose(file, cw), tree)
} else {
apply(transaction, file, tree)
}
}

Op::Compose(filters) => {
let filtered: Vec<_> = filters
.iter()
Expand Down Expand Up @@ -626,6 +687,71 @@ fn unapply2<'a>(

return Ok(r);
}
Op::Include(path) => {
let root = to_filter(Op::File(path.to_owned()));
let mapped = &tree::get_blob(transaction.repo(), &tree, path);
let parsed = parse(mapped)?;

let mut blob = String::new();
if let Ok(c) = get_comments(mapped) {
if !c.is_empty() {
blob = c;
}
}
let blob = &format!("{}{}\n", &blob, pretty(parsed, 0));

// TODO: is this still necessary?
// Remove filters file from the tree to prevent it from being parsed again
// further down the callstack leading to endless recursion.
let tree = tree::insert(
transaction.repo(),
&tree,
path,
git2::Oid::zero(),
0o0100644,
)?;

// Insert a dummy file to prevent the directory from dissappearing through becoming
// empty.
let tree = tree::insert(
transaction.repo(),
&tree,
Path::new("DUMMY-df97a89d-b11f-4e1c-8400-345f895f0d40"),
transaction.repo().blob("".as_bytes())?,
0o0100644,
)?;

let r = unapply(
transaction,
compose(root, parsed),
tree.clone(),
parent_tree,
)?;

// Remove the dummy file inserted above
let r = tree::insert(
transaction.repo(),
&r,
&path.join("DUMMY-df97a89d-b11f-4e1c-8400-345f895f0d40"),
git2::Oid::zero(),
0o0100644,
)?;

// Put the filters file back to it's target location.
let r = if !mapped.is_empty() {
tree::insert(
transaction.repo(),
&r,
&path,
transaction.repo().blob(blob.as_bytes())?,
0o0100644, // Should this handle filemode?
)?
} else {
r
};

return Ok(r);
}
Op::Compose(filters) => {
let mut remaining = tree.clone();
let mut result = parent_tree.clone();
Expand Down Expand Up @@ -748,6 +874,16 @@ pub fn compute_warnings<'a>(
}
}

if let Op::Include(path) = to_op(filter) {
let full_filter = &tree::get_blob(transaction.repo(), &tree, &path);
if let Ok(res) = parse(full_filter) {
filter = res;
} else {
warnings.push("couldn't parse include file\n".to_string());
return warnings;
}
}

let filter = opt::flatten(filter);
if let Op::Compose(filters) = to_op(filter) {
for f in filters {
Expand Down
16 changes: 14 additions & 2 deletions src/filter/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ fn make_op(args: &[&str]) -> JoshResult<Op> {
["empty"] => Ok(Op::Empty),
["prefix", arg] => Ok(Op::Prefix(Path::new(arg).to_owned())),
["workspace", arg] => Ok(Op::Workspace(Path::new(arg).to_owned())),
["include", arg] => Ok(Op::Include(Path::new(arg).to_owned())),
["prefix"] => Err(josh_error(indoc!(
r#"
Filter ":prefix" requires an argument.
Expand All @@ -15,7 +16,7 @@ fn make_op(args: &[&str]) -> JoshResult<Op> {

:prefix=path

Where `path` is path to be used as a prefix
Where `path` is the path to be used as a prefix
"#
))),
["workspace"] => Err(josh_error(indoc!(
Expand All @@ -26,7 +27,18 @@ fn make_op(args: &[&str]) -> JoshResult<Op> {

:workspace=path

Where `path` is path to the directory where workspace.josh file is located
Where `path` is the path to the directory where workspace.josh file is located
"#
))),
["include"] => Err(josh_error(indoc!(
r#"
Filter ":include" requires an argument.

Note: use "=" to provide the argument value:

:include=path/to/include.josh

Where `path/to/include.josh` is the path to the include file
"#
))),
["SQUASH"] => Ok(Op::Squash),
Expand Down
Loading