Skip to content

Commit

Permalink
Feature: return oldest revsets
Browse files Browse the repository at this point in the history
  • Loading branch information
AjithPanneerselvam committed Jan 30, 2025
1 parent d620ef9 commit e9f866b
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 1 deletion.
178 changes: 178 additions & 0 deletions lib/src/default_index/revset_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,10 @@ impl EvaluationContext<'_> {
let candidate_set = self.evaluate(candidates)?;
Ok(Box::new(self.take_latest_revset(&*candidate_set, *count)?))
}
ResolvedExpression::Oldest { candidates, count } => {
let candidate_set = self.evaluate(candidates)?;
Ok(Box::new(self.take_oldest_revset(&*candidate_set, *count)?))
}
ResolvedExpression::Coalesce(expression1, expression2) => {
let set1 = self.evaluate(expression1)?;
if set1.positions().attach(index).next().is_some() {
Expand Down Expand Up @@ -1057,6 +1061,180 @@ impl EvaluationContext<'_> {
Ok(EagerRevset { positions })
}

fn take_oldest_revset(
&self,
candidate_set: &dyn InternalRevset,
count: usize,
) -> Result<EagerRevset, RevsetEvaluationError> {
if count == 0 {
return Ok(EagerRevset::empty());
}

#[derive(Clone, Eq, PartialEq)]
struct Item {
timestamp: MillisSinceEpoch,
pos: IndexPosition,
}

let make_rev_item = |pos| -> Result<_, RevsetEvaluationError> {
let entry = self.index.entry_by_pos(pos?);
let commit = self.store.get_commit(&entry.commit_id())?;
Ok(Item {
timestamp: commit.committer().timestamp.timestamp,
pos: entry.position(),
})
};

let mut items: Vec<_> = candidate_set
.positions()
.attach(self.index)
.map(make_rev_item)
.try_collect()?;

// Sort by timestamp (oldest first), then by position
items.sort_by(|a, b| {
a.timestamp
.cmp(&b.timestamp)
.then_with(|| a.pos.cmp(&b.pos))
});

let _positions = items
.into_iter()
.take(count)
.map(|item| item.pos)
.collect_vec();

Ok(EagerRevset::empty())
// Ok(EagerRevset { vec![]})
}

// fn take_oldest_revset(
// &self,
// candidate_set: &dyn InternalRevset,
// count: usize,
// ) -> Result<EagerRevset, RevsetEvaluationError> {
// if count == 0 {
// return Ok(EagerRevset::empty());
// }

// #[derive(Clone, Eq, PartialEq)]
// struct Item {
// timestamp: MillisSinceEpoch,
// pos: IndexPosition,
// }

// // Implement Ord and PartialOrd to create a min-heap
// impl Ord for Item {
// fn cmp(&self, other: &Self) -> std::cmp::Ordering {
// other
// .timestamp
// .cmp(&self.timestamp)
// .then_with(|| other.pos.cmp(&self.pos))
// }
// }

// impl PartialOrd for Item {
// fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
// Some(self.cmp(other))
// }
// }

// let make_rev_item = |pos| -> Result<_, RevsetEvaluationError> {
// let entry = self.index.entry_by_pos(pos?);
// let commit = self.store.get_commit(&entry.commit_id())?;
// Ok(Item {
// timestamp: commit.committer().timestamp.timestamp,
// pos: entry.position(),
// })
// };

// let mut candidate_iter = candidate_set
// .positions()
// .attach(self.index)
// .map(make_rev_item)
// .fuse();
// let mut oldest_items: BinaryHeap<_> = candidate_iter.by_ref().take(count).try_collect()?;
// for item in candidate_iter {
// let item = item?;
// if oldest_items.len() < count {
// oldest_items.push(item);
// } else if item < *oldest_items.peek().unwrap() {
// oldest_items.pop();
// oldest_items.push(item);
// }
// }

// assert!(oldest_items.len() <= count);
// let mut positions = oldest_items.into_iter().map(|item| item.pos).collect_vec();
// positions.sort_unstable();
// Ok(EagerRevset { positions })
// }

// fn take_oldest_revset(
// &self,
// candidate_set: &dyn InternalRevset,
// count: usize,
// ) -> Result<EagerRevset, RevsetEvaluationError> {
// if count == 0 {
// return Ok(EagerRevset::empty());
// }

// #[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
// struct Item {
// timestamp: MillisSinceEpoch,
// pos: IndexPosition, // tie-breaker
// }

// let make_rev_item = |pos| -> Result<_, RevsetEvaluationError> {
// let entry = self.index.entry_by_pos(pos?);
// let commit = self.store.get_commit(&entry.commit_id())?;
// Ok(Reverse(Item {
// timestamp: commit.committer().timestamp.timestamp,
// pos: entry.position(),
// }))
// // Ok(Item {
// // timestamp: commit.committer().timestamp.timestamp,
// // pos: entry.position(),
// // })
// };

// // Maintain min-heap containing the oldest (smallest) count items. For small
// // count and large candidate set, this is probably cheaper than building vec
// // and applying selection algorithm.
// let mut candidate_iter = candidate_set
// .positions()
// .attach(self.index)
// .map(make_rev_item)
// .fuse();
// let mut oldest_items: BinaryHeap<_> = candidate_iter.by_ref().take(count).try_collect()?;
// for item in candidate_iter {
// let item = item?;
// let mut latest = oldest_items.peek_mut().unwrap();
// if item.0 < latest.0 {
// *latest = item;
// }
// // if latest.pos.0 < item.pos.0 {
// // *latest = item;
// // }
// }

// assert!(oldest_items.len() <= count);
// // let mut positions = oldest_items
// // .into_iter()
// // .map(|item| item.pos.0)
// // .collect_vec();
// // positions.sort_unstable();
// // Ok(EagerRevset { positions })

// let mut positions = oldest_items
// .into_iter()
// .map(|item| item.0.pos)
// .collect_vec();
// // positions.sort_unstable();
// positions.sort_unstable_by_key(|&pos| pos);
// Ok(EagerRevset { positions })
// }

fn take_latest_revset(
&self,
candidate_set: &dyn InternalRevset,
Expand Down
42 changes: 41 additions & 1 deletion lib/src/revset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ pub enum RevsetExpression<St: ExpressionState> {
candidates: Rc<Self>,
count: usize,
},
Oldest {
candidates: Rc<Self>,
count: usize,
},
Filter(RevsetFilterPredicate),
/// Marker for subtree that should be intersected as filter.
AsFilter(Rc<Self>),
Expand Down Expand Up @@ -376,6 +380,13 @@ impl<St: ExpressionState> RevsetExpression<St> {
})
}

pub fn oldest(self: &Rc<Self>, count: usize) -> Rc<Self> {
Rc::new(Self::Oldest {
candidates: self.clone(),
count,
})
}

/// Commits in `self` that don't have descendants in `self`.
pub fn heads(self: &Rc<Self>) -> Rc<Self> {
Rc::new(Self::Heads(self.clone()))
Expand Down Expand Up @@ -632,6 +643,10 @@ pub enum ResolvedExpression {
candidates: Box<Self>,
count: usize,
},
Oldest {
candidates: Box<Self>,
count: usize,
},
Coalesce(Box<Self>, Box<Self>),
Union(Box<Self>, Box<Self>),
/// Intersects `candidates` with `predicate` by filtering.
Expand Down Expand Up @@ -818,6 +833,16 @@ static BUILTIN_FUNCTION_MAP: Lazy<HashMap<&'static str, RevsetFunction>> = Lazy:
};
Ok(candidates.latest(count))
});
map.insert("oldest", |diagnostics, function, context| {
let ([candidates_arg], [count_opt_arg]) = function.expect_arguments()?;
let candidates = lower_expression(diagnostics, candidates_arg, context)?;
let count = if let Some(count_arg) = count_opt_arg {
expect_literal(diagnostics, "integer", count_arg)?
} else {
1
};
Ok(candidates.latest(count))
});
map.insert("fork_point", |diagnostics, function, context| {
let [expression_arg] = function.expect_exact_arguments()?;
let expression = lower_expression(diagnostics, expression_arg, context)?;
Expand Down Expand Up @@ -1310,6 +1335,11 @@ fn try_transform_expression<St: ExpressionState, E>(
candidates,
count: *count,
}),
RevsetExpression::Oldest { candidates, count } => transform_rec(candidates, pre, post)?
.map(|candidates| RevsetExpression::Latest {
candidates,
count: *count,
}),
RevsetExpression::Filter(_) => None,
RevsetExpression::AsFilter(candidates) => {
transform_rec(candidates, pre, post)?.map(RevsetExpression::AsFilter)
Expand Down Expand Up @@ -1504,6 +1534,11 @@ where
let count = *count;
RevsetExpression::Latest { candidates, count }.into()
}
RevsetExpression::Oldest { candidates, count } => {
let candidates = folder.fold_expression(candidates)?;
let count = *count;
RevsetExpression::Oldest { candidates, count }.into()
}
RevsetExpression::Filter(predicate) => RevsetExpression::Filter(predicate.clone()).into(),
RevsetExpression::AsFilter(candidates) => {
let candidates = folder.fold_expression(candidates)?;
Expand Down Expand Up @@ -2388,6 +2423,10 @@ impl VisibilityResolutionContext<'_> {
candidates: self.resolve(candidates).into(),
count: *count,
},
RevsetExpression::Oldest { candidates, count } => ResolvedExpression::Oldest {
candidates: self.resolve(candidates).into(),
count: *count,
},
RevsetExpression::Filter(_) | RevsetExpression::AsFilter(_) => {
// Top-level filter without intersection: e.g. "~author(_)" is represented as
// `AsFilter(NotIn(Filter(Author(_))))`.
Expand Down Expand Up @@ -2489,7 +2528,8 @@ impl VisibilityResolutionContext<'_> {
| RevsetExpression::Heads(_)
| RevsetExpression::Roots(_)
| RevsetExpression::ForkPoint(_)
| RevsetExpression::Latest { .. } => {
| RevsetExpression::Latest { .. }
| RevsetExpression::Oldest { .. } => {
ResolvedPredicateExpression::Set(self.resolve(expression).into())
}
RevsetExpression::Filter(predicate) => {
Expand Down

0 comments on commit e9f866b

Please sign in to comment.