Skip to content

Commit c75861d

Browse files
Implement :lookup filter
1 parent 5eb99d2 commit c75861d

File tree

4 files changed

+218
-1
lines changed

4 files changed

+218
-1
lines changed

josh-core/src/filter/mod.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ enum Op {
296296
Prefix(std::path::PathBuf),
297297
Subdir(std::path::PathBuf),
298298
Workspace(std::path::PathBuf),
299+
Lookup(std::path::PathBuf),
300+
Lookup2(git2::Oid),
299301

300302
Pattern(String),
301303
Message(String),
@@ -616,6 +618,12 @@ fn spec2(op: &Op) -> String {
616618
Op::Workspace(path) => {
617619
format!(":workspace={}", parse::quote_if(&path.to_string_lossy()))
618620
}
621+
Op::Lookup(path) => {
622+
format!(":lookup={}", parse::quote_if(&path.to_string_lossy()))
623+
}
624+
Op::Lookup2(oid) => {
625+
format!(":lookup2={}", oid.to_string())
626+
}
619627
Op::RegexReplace(replacements) => {
620628
let v = replacements
621629
.iter()
@@ -919,6 +927,69 @@ fn apply_to_commit2(
919927

920928
apply(transaction, nf, Apply::from_commit(commit)?)?
921929
}
930+
Op::Lookup(lookup_path) => {
931+
let lookup_commit = if let Some(lookup_commit) =
932+
apply_to_commit2(&Op::Subdir(lookup_path.clone()), &commit, transaction)?
933+
{
934+
lookup_commit
935+
} else {
936+
return Ok(None);
937+
};
938+
939+
let op = Op::Lookup2(lookup_commit);
940+
941+
if let Some(start) = transaction.get(to_filter(op), commit.id()) {
942+
transaction.insert(filter, commit.id(), start, true);
943+
return Ok(Some(start));
944+
} else {
945+
return Ok(None);
946+
}
947+
}
948+
949+
Op::Lookup2(lookup_commit_id) => {
950+
let lookup_commit = repo.find_commit(*lookup_commit_id)?;
951+
for parent in lookup_commit.parents() {
952+
let lookup_tree = lookup_commit.tree_id();
953+
let cw = get_filter(
954+
repo,
955+
&repo.find_tree(lookup_tree)?,
956+
&std::path::PathBuf::new().join(commit.id().to_string()),
957+
);
958+
if cw != filter::empty() {
959+
if let Some(start) =
960+
apply_to_commit2(&Op::Lookup2(parent.id()), &commit, transaction)?
961+
{
962+
transaction.insert(filter, commit.id(), start, true);
963+
return Ok(Some(start));
964+
} else {
965+
return Ok(None);
966+
}
967+
}
968+
break;
969+
}
970+
let lookup_tree = lookup_commit.tree_id();
971+
let cw = get_filter(
972+
repo,
973+
&repo.find_tree(lookup_tree)?,
974+
&std::path::PathBuf::new().join(commit.id().to_string()),
975+
);
976+
977+
if cw == filter::empty() {
978+
// FIXME empty filter or no entry in table?
979+
for parent in commit.parents() {
980+
if let Some(start) = apply_to_commit2(&op, &parent, transaction)? {
981+
transaction.insert(filter, commit.id(), start, true);
982+
return Ok(Some(start));
983+
} else {
984+
return Ok(None);
985+
}
986+
}
987+
return Ok(None);
988+
}
989+
990+
Apply::from_commit(commit)?
991+
.with_tree(apply(transaction, cw, Apply::from_commit(commit)?)?.into_tree())
992+
}
922993
Op::Squash(Some(ids)) => {
923994
if let Some(sq) = ids.get(&LazyRef::Resolved(commit.id())) {
924995
let oid = if let Some(oid) =
@@ -1579,6 +1650,8 @@ fn apply2<'a>(transaction: &'a cache::Transaction, op: &Op, x: Apply<'a>) -> Jos
15791650
Ok(x.with_tree(result_tree))
15801651
}
15811652
Op::Rev(_) => Err(josh_error("not applicable to tree")),
1653+
Op::Lookup(_) => Err(josh_error("not applicable to tree")),
1654+
Op::Lookup2(_) => Err(josh_error("not applicable to tree")),
15821655
Op::Join(_) => Err(josh_error("not applicable to tree")),
15831656
Op::RegexReplace(replacements) => {
15841657
let mut t = x.tree().clone();

josh-core/src/filter/parse.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ 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+
["lookup", arg] => Ok(Op::Lookup(Path::new(arg).to_owned())),
1314
["prefix"] => Err(josh_error(indoc!(
1415
r#"
1516
Filter ":prefix" requires an argument.

josh-filter/src/bin/josh-filter.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,32 @@ impl josh::cache::FilterHook for GitNotesFilterHook {
159159
}
160160
}
161161

162+
struct ChangeIdFilterHook {
163+
repo: std::sync::Mutex<git2::Repository>,
164+
}
165+
166+
impl josh::cache::FilterHook for ChangeIdFilterHook {
167+
fn filter_for_commit(
168+
&self,
169+
commit_oid: git2::Oid,
170+
arg: &str,
171+
) -> josh::JoshResult<josh::filter::Filter> {
172+
let repo = self.repo.lock().unwrap();
173+
let commit = repo.find_commit(commit_oid)?;
174+
let data = format!(
175+
"{:?}:{:?}:{}:{}",
176+
commit.message(),
177+
commit.time(),
178+
commit.author(),
179+
commit.committer()
180+
);
181+
182+
let hash = git2::Oid::hash_object(git2::ObjectType::Blob, data.as_bytes())
183+
.expect("hash_object changeid");
184+
josh::filter::parse(&format!(":\"{}\"", hash))
185+
}
186+
}
187+
162188
fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
163189
let args = make_app().get_matches_from(args);
164190

@@ -200,7 +226,7 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
200226
git2::RepositoryOpenFlags::NO_SEARCH,
201227
&[] as &[&std::ffi::OsStr],
202228
)?;
203-
let hook = GitNotesFilterHook {
229+
let hook = ChangeIdFilterHook {
204230
repo: std::sync::Mutex::new(repo_for_hook),
205231
};
206232
transaction = transaction.with_filter_hook(std::sync::Arc::new(hook));

tests/filter/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)