78 lines
2.3 KiB
Rust
78 lines
2.3 KiB
Rust
//! Path-limited history follows a tree-same parent at merges, as Git's default history simplification does.
|
|
use crate::{Result, changes, repo::error};
|
|
use std::collections::BTreeSet;
|
|
|
|
pub(crate) fn paths(
|
|
repo: &gix::Repository,
|
|
start: gix::ObjectId,
|
|
path: &str,
|
|
) -> Result<Vec<gix::ObjectId>> {
|
|
let empty = repo
|
|
.write_object(&gix::objs::Tree { entries: vec![] })
|
|
.map_err(|e| error("log empty tree", e))?
|
|
.detach();
|
|
let time = |id| -> Result<i64> {
|
|
Ok(repo
|
|
.find_commit(id)
|
|
.map_err(|e| error("log commit", e))?
|
|
.committer()
|
|
.map_err(|e| error("log committer", e))?
|
|
.time()
|
|
.map_err(|e| error("log time", e))?
|
|
.seconds)
|
|
};
|
|
let mut todo = vec![(start, time(start)?)];
|
|
let mut visited = BTreeSet::new();
|
|
let mut visible = Vec::new();
|
|
while !todo.is_empty() {
|
|
let next = todo
|
|
.iter()
|
|
.enumerate()
|
|
.min_by_key(|(_, (_, time))| std::cmp::Reverse(*time))
|
|
.unwrap()
|
|
.0;
|
|
let (id, _) = todo.remove(next);
|
|
if !visited.insert(id) {
|
|
continue;
|
|
}
|
|
let commit = repo.find_commit(id).map_err(|e| error("log commit", e))?;
|
|
let parents = commit
|
|
.parent_ids()
|
|
.map(|id| id.detach())
|
|
.collect::<Vec<_>>();
|
|
let mut same = None;
|
|
for parent in &parents {
|
|
if diff(repo, *parent, id, path)?.is_empty() {
|
|
same = Some(*parent);
|
|
break;
|
|
}
|
|
}
|
|
if let Some(parent) = same {
|
|
todo.push((parent, time(parent)?));
|
|
} else {
|
|
if !parents.is_empty() || !diff(repo, empty, id, path)?.is_empty() {
|
|
visible.push(id);
|
|
}
|
|
for parent in parents {
|
|
todo.push((parent, time(parent)?));
|
|
}
|
|
}
|
|
}
|
|
Ok(visible)
|
|
}
|
|
fn diff(
|
|
repo: &gix::Repository,
|
|
from: gix::ObjectId,
|
|
to: gix::ObjectId,
|
|
path: &str,
|
|
) -> Result<Vec<changes::FileDiff>> {
|
|
changes::diff(changes::DiffParams {
|
|
repo: repo.workdir().unwrap().to_string_lossy().into_owned(),
|
|
from: Some(from.to_string()),
|
|
to: Some(to.to_string()),
|
|
staged: None,
|
|
path: Some(path.into()),
|
|
context: None,
|
|
})
|
|
}
|