Skip to content

Commit 0c7cd42

Browse files
Implement :lookup filter
1 parent 33fefa4 commit 0c7cd42

File tree

4 files changed

+230
-0
lines changed

4 files changed

+230
-0
lines changed

josh-core/src/filter/mod.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,14 @@ fn spec2(op: &Op) -> String {
519519
Op::Workspace(path) => {
520520
format!(":workspace={}", parse::quote_if(&path.to_string_lossy()))
521521
}
522+
#[cfg(feature = "incubating")]
523+
Op::Lookup(path) => {
524+
format!(":lookup={}", parse::quote_if(&path.to_string_lossy()))
525+
}
526+
#[cfg(feature = "incubating")]
527+
Op::Lookup2(oid) => {
528+
format!(":lookup2={}", oid.to_string())
529+
}
522530
Op::Stored(path) => {
523531
format!(":+{}", parse::quote_if(&path.to_string_lossy()))
524532
}
@@ -846,6 +854,71 @@ fn apply_to_commit2(
846854

847855
apply(transaction, nf, Rewrite::from_commit(commit)?)?
848856
}
857+
#[cfg(feature = "incubating")]
858+
Op::Lookup(lookup_path) => {
859+
let lookup_commit = if let Some(lookup_commit) =
860+
apply_to_commit2(&Op::Subdir(lookup_path.clone()), &commit, transaction)?
861+
{
862+
lookup_commit
863+
} else {
864+
return Ok(None);
865+
};
866+
867+
let op = Op::Lookup2(lookup_commit);
868+
869+
if let Some(start) = transaction.get(to_filter(op), commit.id()) {
870+
transaction.insert(filter, commit.id(), start, true);
871+
return Ok(Some(start));
872+
} else {
873+
return Ok(None);
874+
}
875+
}
876+
877+
#[cfg(feature = "incubating")]
878+
Op::Lookup2(lookup_commit_id) => {
879+
let lookup_commit = repo.find_commit(*lookup_commit_id)?;
880+
for parent in lookup_commit.parents() {
881+
let lookup_tree = lookup_commit.tree_id();
882+
let cw = get_filter(
883+
transaction,
884+
&repo.find_tree(lookup_tree)?,
885+
&std::path::PathBuf::new().join(commit.id().to_string()),
886+
);
887+
if cw != filter::empty() {
888+
if let Some(start) =
889+
apply_to_commit2(&Op::Lookup2(parent.id()), &commit, transaction)?
890+
{
891+
transaction.insert(filter, commit.id(), start, true);
892+
return Ok(Some(start));
893+
} else {
894+
return Ok(None);
895+
}
896+
}
897+
break;
898+
}
899+
let lookup_tree = lookup_commit.tree_id();
900+
let cw = get_filter(
901+
transaction,
902+
&repo.find_tree(lookup_tree)?,
903+
&std::path::PathBuf::new().join(commit.id().to_string()),
904+
);
905+
906+
if cw == filter::empty() {
907+
// FIXME empty filter or no entry in table?
908+
for parent in commit.parents() {
909+
if let Some(start) = apply_to_commit2(&op, &parent, transaction)? {
910+
transaction.insert(filter, commit.id(), start, true);
911+
return Ok(Some(start));
912+
} else {
913+
return Ok(None);
914+
}
915+
}
916+
return Ok(None);
917+
}
918+
919+
Apply::from_commit(commit)?
920+
.with_tree(apply(transaction, cw, Apply::from_commit(commit)?)?.into_tree())
921+
}
849922
Op::Squash(Some(ids)) => {
850923
if let Some(sq) = ids.get(&LazyRef::Resolved(commit.id())) {
851924
let oid = if let Some(oid) =
@@ -1660,6 +1733,8 @@ fn apply2<'a>(
16601733
}
16611734
}
16621735
Op::Pin(_) => Ok(x),
1736+
#[cfg(feature = "incubating")]
1737+
Op::Lookup(_) | Op::Lookup2(_) => Err(josh_error("not applicable to tree")),
16631738
}
16641739
}
16651740

josh-core/src/filter/parse.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ fn make_op(args: &[&str]) -> JoshResult<Op> {
1010
["author", author, email] => Ok(Op::Author(author.to_string(), email.to_string())),
1111
["committer", author, email] => Ok(Op::Committer(author.to_string(), email.to_string())),
1212
["workspace", arg] => Ok(Op::Workspace(Path::new(arg).to_owned())),
13+
#[cfg(feature = "incubating")]
14+
["lookup", arg] => Ok(Op::Lookup(Path::new(arg).to_owned())),
1315
["prefix"] => Err(josh_error(indoc!(
1416
r#"
1517
Filter ":prefix" requires an argument.

josh-core/src/filter/persist.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,16 @@ impl InMemoryBuilder {
325325
let params_tree = self.build_str_params(&[hook.as_ref()]);
326326
push_tree_entries(&mut entries, [("hook", params_tree)]);
327327
}
328+
#[cfg(feature = "incubating")]
329+
Op::Lookup(path) => {
330+
let params_tree = self.build_str_params(&[path.to_string_lossy().as_ref()]);
331+
push_tree_entries(&mut entries, [("lookup", params_tree)]);
332+
}
333+
#[cfg(feature = "incubating")]
334+
Op::Lookup2(oid) => {
335+
let params_tree = self.build_str_params(&[oid.to_string().as_ref()]);
336+
push_tree_entries(&mut entries, [("lookup2", params_tree)]);
337+
}
328338
}
329339

330340
let tree = gix_object::Tree { entries };
@@ -606,6 +616,32 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
606616
let path = std::str::from_utf8(path_blob.content())?;
607617
Ok(Op::Stored(std::path::PathBuf::from(path)))
608618
}
619+
#[cfg(feature = "incubating")]
620+
"lookup" => {
621+
let inner = repo.find_tree(entry.id())?;
622+
let path_blob = repo.find_blob(
623+
inner
624+
.get_name("0")
625+
.ok_or_else(|| josh_error("lookup: missing path"))?
626+
.id(),
627+
)?;
628+
let path = std::str::from_utf8(path_blob.content())?;
629+
Ok(Op::Lookup(std::path::PathBuf::from(path)))
630+
}
631+
#[cfg(feature = "incubating")]
632+
"lookup2" => {
633+
let inner = repo.find_tree(entry.id())?;
634+
let oid_blob = repo.find_blob(
635+
inner
636+
.get_name("0")
637+
.ok_or_else(|| josh_error("lookup2: missing oid"))?
638+
.id(),
639+
)?;
640+
let oid_str = std::str::from_utf8(oid_blob.content())?;
641+
let oid = git2::Oid::from_str(oid_str)
642+
.map_err(|e| josh_error(&format!("lookup2: invalid oid: {}", e)))?;
643+
Ok(Op::Lookup2(oid))
644+
}
609645
"compose" => {
610646
let compose_tree = repo.find_tree(entry.id())?;
611647
let mut filters = Vec::new();

tests/experimental/lookup.t

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
$ export TERM=dumb
2+
$ export RUST_LOG_STYLE=never
3+
4+
$ git init -q real_repo 1> /dev/null
5+
$ cd real_repo
6+
7+
$ mkdir sub1
8+
$ echo contents1 > sub1/file1
9+
$ git add sub1
10+
$ git commit -m "add file1" 1> /dev/null
11+
12+
$ mkdir sub1
13+
mkdir: cannot create directory 'sub1': File exists
14+
[1]
15+
$ echo contents2 > sub1/file2
16+
$ git add sub1
17+
$ git commit -m "add file2" 1> /dev/null
18+
19+
$ git log --graph --pretty=%H
20+
* 81b10fb4984d20142cd275b89c91c346e536876a
21+
* bb282e9cdc1b972fffd08fd21eead43bc0c83cb8
22+
23+
$ mkdir table
24+
$ echo ":prefix=x" > table/81b10fb4984d20142cd275b89c91c346e536876a
25+
$ echo ":prefix=y" > table/bb282e9cdc1b972fffd08fd21eead43bc0c83cb8
26+
$ git add table
27+
$ git commit -m "add lookup table" 1> /dev/null
28+
29+
30+
$ echo contents3 > sub1/file3
31+
$ git add sub1
32+
$ git commit -m "add file3" 1> /dev/null
33+
34+
$ git log --graph --pretty=%H
35+
* 26e4c43675b985689e280bc42264a9226af76943
36+
* 14c74c5eca73952b36d736034b388832748c49d6
37+
* 81b10fb4984d20142cd275b89c91c346e536876a
38+
* bb282e9cdc1b972fffd08fd21eead43bc0c83cb8
39+
40+
$ josh-filter -s ":lookup=table" --update refs/heads/filtered
41+
[1] :lookup=table
42+
[2] :/table
43+
[4] :lookup2=4880528e9d57aa5efc925e120a8077bfa37d778d
44+
45+
$ git log refs/heads/filtered --graph --pretty=%s
46+
* add file2
47+
* add file1
48+
$ git diff ${EMPTY_TREE}..refs/heads/filtered
49+
diff --git a/x/sub1/file1 b/x/sub1/file1
50+
new file mode 100644
51+
index 0000000..a024003
52+
--- /dev/null
53+
+++ b/x/sub1/file1
54+
@@ -0,0 +1 @@
55+
+contents1
56+
diff --git a/x/sub1/file2 b/x/sub1/file2
57+
new file mode 100644
58+
index 0000000..6b46faa
59+
--- /dev/null
60+
+++ b/x/sub1/file2
61+
@@ -0,0 +1 @@
62+
+contents2
63+
$ git diff ${EMPTY_TREE}..refs/heads/filtered~1
64+
diff --git a/y/sub1/file1 b/y/sub1/file1
65+
new file mode 100644
66+
index 0000000..a024003
67+
--- /dev/null
68+
+++ b/y/sub1/file1
69+
@@ -0,0 +1 @@
70+
+contents1
71+
72+
$ echo ":prefix=z" > table/14c74c5eca73952b36d736034b388832748c49d6
73+
$ echo ":prefix=z" > table/26e4c43675b985689e280bc42264a9226af76943
74+
$ git add table
75+
$ git commit -m "mod lookup table" 1> /dev/null
76+
$ tree table
77+
table
78+
|-- 14c74c5eca73952b36d736034b388832748c49d6
79+
|-- 26e4c43675b985689e280bc42264a9226af76943
80+
|-- 81b10fb4984d20142cd275b89c91c346e536876a
81+
`-- bb282e9cdc1b972fffd08fd21eead43bc0c83cb8
82+
83+
1 directory, 4 files
84+
85+
$ josh-filter -s ":lookup=table" --update refs/heads/filtered
86+
Warning: reference refs/heads/filtered wasn't updated
87+
[2] :lookup=table
88+
[3] :/table
89+
[4] :lookup2=4880528e9d57aa5efc925e120a8077bfa37d778d
90+
[5] :lookup2=ed934c124e28c83270d9cfbb011f3ceb46c0f69e
91+
$ git log refs/heads/filtered --graph --pretty=%s
92+
* add file2
93+
* add file1
94+
95+
$ git diff ${EMPTY_TREE}..refs/heads/filtered
96+
diff --git a/x/sub1/file1 b/x/sub1/file1
97+
new file mode 100644
98+
index 0000000..a024003
99+
--- /dev/null
100+
+++ b/x/sub1/file1
101+
@@ -0,0 +1 @@
102+
+contents1
103+
diff --git a/x/sub1/file2 b/x/sub1/file2
104+
new file mode 100644
105+
index 0000000..6b46faa
106+
--- /dev/null
107+
+++ b/x/sub1/file2
108+
@@ -0,0 +1 @@
109+
+contents2
110+
$ git diff ${EMPTY_TREE}..refs/heads/filtered~1
111+
diff --git a/y/sub1/file1 b/y/sub1/file1
112+
new file mode 100644
113+
index 0000000..a024003
114+
--- /dev/null
115+
+++ b/y/sub1/file1
116+
@@ -0,0 +1 @@
117+
+contents1

0 commit comments

Comments
 (0)