Skip to content

Commit 76f4488

Browse files
committed
make rev() detect all parents of the given commits
1 parent 007fd70 commit 76f4488

File tree

1 file changed

+46
-14
lines changed

1 file changed

+46
-14
lines changed

josh-core/src/filter/mod.rs

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ lazy_static! {
1414
std::sync::Mutex::new(std::collections::HashMap::new());
1515
static ref WORKSPACES: std::sync::Mutex<std::collections::HashMap<git2::Oid, Filter>> =
1616
std::sync::Mutex::new(std::collections::HashMap::new());
17+
static ref ANCESTORS: std::sync::Mutex<std::collections::HashMap<git2::Oid, std::collections::HashSet<git2::Oid>>> =
18+
std::sync::Mutex::new(std::collections::HashMap::new());
1719
}
1820

1921
/// Filters are represented as `git2::Oid`, however they are not ever stored
@@ -482,20 +484,23 @@ fn apply_to_commit2(
482484

483485
let id = commit.id();
484486

485-
if let Some(startfilter) = filters.get(&id) {
486-
let mut f2 = filters.clone();
487-
f2.remove(&id);
488-
f2.insert(git2::Oid::zero(), *startfilter);
489-
let op = if f2.len() == 1 {
490-
to_op(*startfilter)
491-
} else {
492-
Op::Rev(f2)
493-
};
494-
if let Some(start) = apply_to_commit2(&op, commit, transaction)? {
495-
transaction.insert(filter, id, start, true);
496-
return Ok(Some(start));
497-
} else {
498-
return Ok(None);
487+
for (&filter_tip, startfilter) in filters.iter() {
488+
if filter_tip != git2::Oid::zero() && is_ancestor_of(repo, id, filter_tip)? {
489+
// Remove this filter but preserve the others.
490+
let mut f2 = filters.clone();
491+
f2.remove(&filter_tip);
492+
f2.insert(git2::Oid::zero(), *startfilter);
493+
let op = if f2.len() == 1 {
494+
to_op(*startfilter)
495+
} else {
496+
Op::Rev(f2)
497+
};
498+
if let Some(start) = apply_to_commit2(&op, commit, transaction)? {
499+
transaction.insert(filter, id, start, true);
500+
return Ok(Some(start));
501+
} else {
502+
return Ok(None);
503+
}
499504
}
500505
}
501506

@@ -980,6 +985,33 @@ pub fn make_permissions_filter(filter: Filter, whitelist: Filter, blacklist: Fil
980985
opt::optimize(filter)
981986
}
982987

988+
/// Check if `commit` is an ancestor of `tip`.
989+
///
990+
/// Creates a cache for a given `tip` so repeated queries with the same `tip` are more efficient.
991+
fn is_ancestor_of(repo: &git2::Repository, commit: git2::Oid, tip: git2::Oid) -> JoshResult<bool> {
992+
let mut ancestor_cache = ANCESTORS.lock().unwrap();
993+
let ancestors = match ancestor_cache.entry(tip) {
994+
std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(),
995+
std::collections::hash_map::Entry::Vacant(entry) => {
996+
tracing::trace!("is_ancestor_of tip={tip}");
997+
// Recursively compute all ancestors of `tip`.
998+
// Invariant: Everything in `todo` is also in `ancestors`.
999+
let mut todo = vec![tip];
1000+
let mut ancestors = std::collections::HashSet::from_iter(todo.iter().copied());
1001+
while let Some(commit) = todo.pop() {
1002+
for parent in repo.find_commit(commit)?.parent_ids() {
1003+
if ancestors.insert(parent) {
1004+
// Newly inserted! Also handle its parents.
1005+
todo.push(parent);
1006+
}
1007+
}
1008+
}
1009+
entry.insert(ancestors)
1010+
}
1011+
};
1012+
Ok(ancestors.contains(&commit))
1013+
}
1014+
9831015
#[cfg(test)]
9841016
mod tests {
9851017
use super::*;

0 commit comments

Comments
 (0)