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
79 changes: 79 additions & 0 deletions josh-core/src/filter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,10 @@ enum Op {
Prefix(std::path::PathBuf),
Subdir(std::path::PathBuf),
Workspace(std::path::PathBuf),
#[cfg(feature = "incubating")]
Lookup(std::path::PathBuf),
#[cfg(feature = "incubating")]
Lookup2(git2::Oid),
Stored(std::path::PathBuf),

Pattern(String),
Expand Down Expand Up @@ -599,6 +603,14 @@ fn spec2(op: &Op) -> String {
Op::Workspace(path) => {
format!(":workspace={}", parse::quote_if(&path.to_string_lossy()))
}
#[cfg(feature = "incubating")]
Op::Lookup(path) => {
format!(":lookup={}", parse::quote_if(&path.to_string_lossy()))
}
#[cfg(feature = "incubating")]
Op::Lookup2(oid) => {
format!(":lookup2={}", oid.to_string())
}
Op::Stored(path) => {
format!(":+{}", parse::quote_if(&path.to_string_lossy()))
}
Expand Down Expand Up @@ -926,6 +938,71 @@ fn apply_to_commit2(

apply(transaction, nf, Apply::from_commit(commit)?)?
}
#[cfg(feature = "incubating")]
Op::Lookup(lookup_path) => {
let lookup_commit = if let Some(lookup_commit) =
apply_to_commit2(&Op::Subdir(lookup_path.clone()), &commit, transaction)?
{
lookup_commit
} else {
return Ok(None);
};

let op = Op::Lookup2(lookup_commit);

if let Some(start) = transaction.get(to_filter(op), commit.id()) {
transaction.insert(filter, commit.id(), start, true);
return Ok(Some(start));
} else {
return Ok(None);
}
}

#[cfg(feature = "incubating")]
Op::Lookup2(lookup_commit_id) => {
let lookup_commit = repo.find_commit(*lookup_commit_id)?;
for parent in lookup_commit.parents() {
let lookup_tree = lookup_commit.tree_id();
let cw = get_filter(
transaction,
&repo.find_tree(lookup_tree)?,
&std::path::PathBuf::new().join(commit.id().to_string()),
);
if cw != filter::empty() {
if let Some(start) =
apply_to_commit2(&Op::Lookup2(parent.id()), &commit, transaction)?
{
transaction.insert(filter, commit.id(), start, true);
return Ok(Some(start));
} else {
return Ok(None);
}
}
break;
}
let lookup_tree = lookup_commit.tree_id();
let cw = get_filter(
transaction,
&repo.find_tree(lookup_tree)?,
&std::path::PathBuf::new().join(commit.id().to_string()),
);

if cw == filter::empty() {
// FIXME empty filter or no entry in table?
for parent in commit.parents() {
if let Some(start) = apply_to_commit2(&op, &parent, transaction)? {
transaction.insert(filter, commit.id(), start, true);
return Ok(Some(start));
} else {
return Ok(None);
}
}
return Ok(None);
}

Apply::from_commit(commit)?
.with_tree(apply(transaction, cw, Apply::from_commit(commit)?)?.into_tree())
}
Op::Squash(Some(ids)) => {
if let Some(sq) = ids.get(&LazyRef::Resolved(commit.id())) {
let oid = if let Some(oid) =
Expand Down Expand Up @@ -1717,6 +1794,8 @@ fn apply2<'a>(transaction: &'a cache::Transaction, op: &Op, x: Apply<'a>) -> Jos
}
}
Op::Pin(_) => Ok(x),
#[cfg(feature = "incubating")]
Op::Lookup(_) | Op::Lookup2(_) => Err(josh_error("not applicable to tree")),
}
}

Expand Down
2 changes: 2 additions & 0 deletions josh-core/src/filter/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ fn make_op(args: &[&str]) -> JoshResult<Op> {
["author", author, email] => Ok(Op::Author(author.to_string(), email.to_string())),
["committer", author, email] => Ok(Op::Committer(author.to_string(), email.to_string())),
["workspace", arg] => Ok(Op::Workspace(Path::new(arg).to_owned())),
#[cfg(feature = "incubating")]
["lookup", arg] => Ok(Op::Lookup(Path::new(arg).to_owned())),
["prefix"] => Err(josh_error(indoc!(
r#"
Filter ":prefix" requires an argument.
Expand Down
36 changes: 36 additions & 0 deletions josh-core/src/filter/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,16 @@ impl InMemoryBuilder {
let params_tree = self.build_str_params(&[hook.as_ref()]);
push_tree_entries(&mut entries, [("hook", params_tree)]);
}
#[cfg(feature = "incubating")]
Op::Lookup(path) => {
let params_tree = self.build_str_params(&[path.to_string_lossy().as_ref()]);
push_tree_entries(&mut entries, [("lookup", params_tree)]);
}
#[cfg(feature = "incubating")]
Op::Lookup2(oid) => {
let params_tree = self.build_str_params(&[oid.to_string().as_ref()]);
push_tree_entries(&mut entries, [("lookup2", params_tree)]);
}
}

let tree = gix_object::Tree { entries };
Expand Down Expand Up @@ -606,6 +616,32 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
let path = std::str::from_utf8(path_blob.content())?;
Ok(Op::Stored(std::path::PathBuf::from(path)))
}
#[cfg(feature = "incubating")]
"lookup" => {
let inner = repo.find_tree(entry.id())?;
let path_blob = repo.find_blob(
inner
.get_name("0")
.ok_or_else(|| josh_error("lookup: missing path"))?
.id(),
)?;
let path = std::str::from_utf8(path_blob.content())?;
Ok(Op::Lookup(std::path::PathBuf::from(path)))
}
#[cfg(feature = "incubating")]
"lookup2" => {
let inner = repo.find_tree(entry.id())?;
let oid_blob = repo.find_blob(
inner
.get_name("0")
.ok_or_else(|| josh_error("lookup2: missing oid"))?
.id(),
)?;
let oid_str = std::str::from_utf8(oid_blob.content())?;
let oid = git2::Oid::from_str(oid_str)
.map_err(|e| josh_error(&format!("lookup2: invalid oid: {}", e)))?;
Ok(Op::Lookup2(oid))
}
"compose" => {
let compose_tree = repo.find_tree(entry.id())?;
let mut filters = Vec::new();
Expand Down
117 changes: 117 additions & 0 deletions tests/experimental/lookup.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
$ export TERM=dumb
$ export RUST_LOG_STYLE=never

$ git init -q real_repo 1> /dev/null
$ cd real_repo

$ mkdir sub1
$ echo contents1 > sub1/file1
$ git add sub1
$ git commit -m "add file1" 1> /dev/null

$ mkdir sub1
mkdir: cannot create directory 'sub1': File exists
[1]
$ echo contents2 > sub1/file2
$ git add sub1
$ git commit -m "add file2" 1> /dev/null

$ git log --graph --pretty=%H
* 81b10fb4984d20142cd275b89c91c346e536876a
* bb282e9cdc1b972fffd08fd21eead43bc0c83cb8

$ mkdir table
$ echo ":prefix=x" > table/81b10fb4984d20142cd275b89c91c346e536876a
$ echo ":prefix=y" > table/bb282e9cdc1b972fffd08fd21eead43bc0c83cb8
$ git add table
$ git commit -m "add lookup table" 1> /dev/null


$ echo contents3 > sub1/file3
$ git add sub1
$ git commit -m "add file3" 1> /dev/null

$ git log --graph --pretty=%H
* 26e4c43675b985689e280bc42264a9226af76943
* 14c74c5eca73952b36d736034b388832748c49d6
* 81b10fb4984d20142cd275b89c91c346e536876a
* bb282e9cdc1b972fffd08fd21eead43bc0c83cb8

$ josh-filter -s ":lookup=table" --update refs/heads/filtered
[1] :lookup=table
[2] :/table
[4] :lookup2=4880528e9d57aa5efc925e120a8077bfa37d778d

$ git log refs/heads/filtered --graph --pretty=%s
* add file2
* add file1
$ git diff ${EMPTY_TREE}..refs/heads/filtered
diff --git a/x/sub1/file1 b/x/sub1/file1
new file mode 100644
index 0000000..a024003
--- /dev/null
+++ b/x/sub1/file1
@@ -0,0 +1 @@
+contents1
diff --git a/x/sub1/file2 b/x/sub1/file2
new file mode 100644
index 0000000..6b46faa
--- /dev/null
+++ b/x/sub1/file2
@@ -0,0 +1 @@
+contents2
$ git diff ${EMPTY_TREE}..refs/heads/filtered~1
diff --git a/y/sub1/file1 b/y/sub1/file1
new file mode 100644
index 0000000..a024003
--- /dev/null
+++ b/y/sub1/file1
@@ -0,0 +1 @@
+contents1

$ echo ":prefix=z" > table/14c74c5eca73952b36d736034b388832748c49d6
$ echo ":prefix=z" > table/26e4c43675b985689e280bc42264a9226af76943
$ git add table
$ git commit -m "mod lookup table" 1> /dev/null
$ tree table
table
|-- 14c74c5eca73952b36d736034b388832748c49d6
|-- 26e4c43675b985689e280bc42264a9226af76943
|-- 81b10fb4984d20142cd275b89c91c346e536876a
`-- bb282e9cdc1b972fffd08fd21eead43bc0c83cb8

1 directory, 4 files

$ josh-filter -s ":lookup=table" --update refs/heads/filtered
Warning: reference refs/heads/filtered wasn't updated
[2] :lookup=table
[3] :/table
[4] :lookup2=4880528e9d57aa5efc925e120a8077bfa37d778d
[5] :lookup2=ed934c124e28c83270d9cfbb011f3ceb46c0f69e
$ git log refs/heads/filtered --graph --pretty=%s
* add file2
* add file1

$ git diff ${EMPTY_TREE}..refs/heads/filtered
diff --git a/x/sub1/file1 b/x/sub1/file1
new file mode 100644
index 0000000..a024003
--- /dev/null
+++ b/x/sub1/file1
@@ -0,0 +1 @@
+contents1
diff --git a/x/sub1/file2 b/x/sub1/file2
new file mode 100644
index 0000000..6b46faa
--- /dev/null
+++ b/x/sub1/file2
@@ -0,0 +1 @@
+contents2
$ git diff ${EMPTY_TREE}..refs/heads/filtered~1
diff --git a/y/sub1/file1 b/y/sub1/file1
new file mode 100644
index 0000000..a024003
--- /dev/null
+++ b/y/sub1/file1
@@ -0,0 +1 @@
+contents1