Skip to content

Commit e7e6eb2

Browse files
Implement :lookup filter
1 parent 9eb226b commit e7e6eb2

File tree

5 files changed

+234
-0
lines changed

5 files changed

+234
-0
lines changed

josh-core/src/filter/mod.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,14 @@ fn spec2(op: &Op) -> String {
496496
Op::Workspace(path) => {
497497
format!(":workspace={}", parse::quote_if(&path.to_string_lossy()))
498498
}
499+
#[cfg(feature = "incubating")]
500+
Op::Lookup(path) => {
501+
format!(":lookup={}", parse::quote_if(&path.to_string_lossy()))
502+
}
503+
#[cfg(feature = "incubating")]
504+
Op::Lookup2(oid) => {
505+
format!(":lookup2={}", oid.to_string())
506+
}
499507
Op::Stored(path) => {
500508
format!(":+{}", parse::quote_if(&path.to_string_lossy()))
501509
}
@@ -823,6 +831,71 @@ fn apply_to_commit2(
823831

824832
apply(transaction, nf, Rewrite::from_commit(commit)?)?
825833
}
834+
#[cfg(feature = "incubating")]
835+
Op::Lookup(lookup_path) => {
836+
let lookup_commit = if let Some(lookup_commit) =
837+
apply_to_commit2(&Op::Subdir(lookup_path.clone()), &commit, transaction)?
838+
{
839+
lookup_commit
840+
} else {
841+
return Ok(None);
842+
};
843+
844+
let op = Op::Lookup2(lookup_commit);
845+
846+
if let Some(start) = transaction.get(to_filter(op), commit.id()) {
847+
transaction.insert(filter, commit.id(), start, true);
848+
return Ok(Some(start));
849+
} else {
850+
return Ok(None);
851+
}
852+
}
853+
854+
#[cfg(feature = "incubating")]
855+
Op::Lookup2(lookup_commit_id) => {
856+
let lookup_commit = repo.find_commit(*lookup_commit_id)?;
857+
for parent in lookup_commit.parents() {
858+
let lookup_tree = lookup_commit.tree_id();
859+
let cw = get_filter(
860+
transaction,
861+
&repo.find_tree(lookup_tree)?,
862+
&std::path::PathBuf::new().join(commit.id().to_string()),
863+
);
864+
if cw != filter::empty() {
865+
if let Some(start) =
866+
apply_to_commit2(&Op::Lookup2(parent.id()), &commit, transaction)?
867+
{
868+
transaction.insert(filter, commit.id(), start, true);
869+
return Ok(Some(start));
870+
} else {
871+
return Ok(None);
872+
}
873+
}
874+
break;
875+
}
876+
let lookup_tree = lookup_commit.tree_id();
877+
let cw = get_filter(
878+
transaction,
879+
&repo.find_tree(lookup_tree)?,
880+
&std::path::PathBuf::new().join(commit.id().to_string()),
881+
);
882+
883+
if cw == filter::empty() {
884+
// FIXME empty filter or no entry in table?
885+
for parent in commit.parents() {
886+
if let Some(start) = apply_to_commit2(&op, &parent, transaction)? {
887+
transaction.insert(filter, commit.id(), start, true);
888+
return Ok(Some(start));
889+
} else {
890+
return Ok(None);
891+
}
892+
}
893+
return Ok(None);
894+
}
895+
896+
Apply::from_commit(commit)?
897+
.with_tree(apply(transaction, cw, Apply::from_commit(commit)?)?.into_tree())
898+
}
826899
Op::Squash(Some(ids)) => {
827900
if let Some(sq) = ids.get(&LazyRef::Resolved(commit.id())) {
828901
let oid = if let Some(oid) =
@@ -1637,6 +1710,8 @@ fn apply2<'a>(
16371710
}
16381711
}
16391712
Op::Pin(_) => Ok(x),
1713+
#[cfg(feature = "incubating")]
1714+
Op::Lookup(_) | Op::Lookup2(_) => Err(josh_error("not applicable to tree")),
16401715
}
16411716
}
16421717

josh-core/src/filter/op.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ pub enum Op {
6969
Prefix(std::path::PathBuf),
7070
Subdir(std::path::PathBuf),
7171
Workspace(std::path::PathBuf),
72+
#[cfg(feature = "incubating")]
73+
Lookup(std::path::PathBuf),
74+
#[cfg(feature = "incubating")]
75+
Lookup2(git2::Oid),
7276
Stored(std::path::PathBuf),
7377

7478
Pattern(String),

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
@@ -336,6 +336,16 @@ impl InMemoryBuilder {
336336
let params_tree = self.build_str_params(&[hook.as_ref()]);
337337
push_tree_entries(&mut entries, [("hook", params_tree)]);
338338
}
339+
#[cfg(feature = "incubating")]
340+
Op::Lookup(path) => {
341+
let params_tree = self.build_str_params(&[path.to_string_lossy().as_ref()]);
342+
push_tree_entries(&mut entries, [("lookup", params_tree)]);
343+
}
344+
#[cfg(feature = "incubating")]
345+
Op::Lookup2(oid) => {
346+
let params_tree = self.build_str_params(&[oid.to_string().as_ref()]);
347+
push_tree_entries(&mut entries, [("lookup2", params_tree)]);
348+
}
339349
}
340350

341351
let tree = gix_object::Tree { entries };
@@ -640,6 +650,32 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
640650
let path = std::str::from_utf8(path_blob.content())?;
641651
Ok(Op::Stored(std::path::PathBuf::from(path)))
642652
}
653+
#[cfg(feature = "incubating")]
654+
"lookup" => {
655+
let inner = repo.find_tree(entry.id())?;
656+
let path_blob = repo.find_blob(
657+
inner
658+
.get_name("0")
659+
.ok_or_else(|| josh_error("lookup: missing path"))?
660+
.id(),
661+
)?;
662+
let path = std::str::from_utf8(path_blob.content())?;
663+
Ok(Op::Lookup(std::path::PathBuf::from(path)))
664+
}
665+
#[cfg(feature = "incubating")]
666+
"lookup2" => {
667+
let inner = repo.find_tree(entry.id())?;
668+
let oid_blob = repo.find_blob(
669+
inner
670+
.get_name("0")
671+
.ok_or_else(|| josh_error("lookup2: missing oid"))?
672+
.id(),
673+
)?;
674+
let oid_str = std::str::from_utf8(oid_blob.content())?;
675+
let oid = git2::Oid::from_str(oid_str)
676+
.map_err(|e| josh_error(&format!("lookup2: invalid oid: {}", e)))?;
677+
Ok(Op::Lookup2(oid))
678+
}
643679
"compose" => {
644680
let compose_tree = repo.find_tree(entry.id())?;
645681
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)