use crate::changestore::*;
use crate::pristine::*;
use crate::small_string::*;
use std::collections::HashSet;
use std::iter::Iterator;
#[derive(Debug, Error)]
pub enum FsError<T: std::error::Error + 'static> {
#[error(transparent)]
NotFound(#[from] FsNotFound),
#[error("File already in repository: {0}")]
AlreadyInRepo(String),
#[error(transparent)]
Txn(T),
}
#[derive(Debug, Error)]
pub enum FsErrorC<C: std::error::Error + 'static, T: std::error::Error + 'static> {
#[error(transparent)]
Txn(T),
#[error(transparent)]
Changestore(C),
#[error(transparent)]
NotFound(#[from] FsNotFound),
}
#[derive(Debug, Error)]
#[error("Path not found: {0}")]
pub struct FsNotFound(String);
impl<T: std::error::Error + 'static> std::convert::From<TxnErr<T>> for FsError<T> {
fn from(e: TxnErr<T>) -> Self {
FsError::Txn(e.0)
}
}
impl<C: std::error::Error + 'static, T: std::error::Error + 'static> std::convert::From<TxnErr<T>>
for FsErrorC<C, T>
{
fn from(e: TxnErr<T>) -> Self {
FsErrorC::Txn(e.0)
}
}
pub(crate) fn create_new_inode<T: TreeMutTxnT>(txn: &mut T) -> Result<Inode, TxnErr<T::TreeError>> {
let mut already_taken = true;
let mut inode: Inode = Inode::ROOT;
while already_taken {
inode = Inode::random();
already_taken = txn.get_revtree(inode, None)?.is_some();
}
Ok(inode)
}
pub fn is_directory<T: TreeTxnT>(txn: &T, inode: Inode) -> Result<bool, TxnErr<T::TreeError>> {
if inode == Inode::ROOT {
return Ok(true);
}
let pathid = OwnedPathId {
parent_inode: inode,
basename: SmallString::new(),
};
for x in txn.iter_tree(pathid.clone(), None)? {
let (pid, _) = x?;
if pid < pathid {
continue;
} else if pid > pathid {
break;
}
return Ok(true);
}
Ok(false)
}
fn closest_in_repo_ancestor<'a, T: TreeTxnT>(
txn: &T,
path: &'a str,
) -> Result<(Inode, std::iter::Peekable<crate::path::Components<'a>>), TxnErr<T::TreeError>> {
let mut components = crate::path::components(path).peekable();
let mut fileid = OwnedPathId {
parent_inode: Inode::ROOT,
basename: SmallString::new(),
};
while let Some(c) = components.peek() {
trace!("component {:?}", c);
fileid.basename.clone_from_str(c);
trace!("{:?}", fileid);
let mut found = false;
for x in txn.iter_tree(fileid.clone(), None)? {
let (id, inode) = x?;
trace!(
"id = {:?}, inode = {:?}, cmp = {:?}",
id,
inode,
id > fileid
);
if id > fileid {
break;
} else if id < fileid {
continue;
}
found = true;
fileid.parent_inode = inode;
break;
}
if found {
components.next();
} else {
break;
}
}
Ok((fileid.parent_inode, components))
}
pub fn find_inode<T: TreeTxnT>(txn: &T, path: &str) -> Result<Inode, FsError<T::TreeError>> {
debug!("find_inode");
let (inode, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;
debug!("/find_inode");
if let Some(c) = remaining_path_components.next() {
debug!("c = {:?}", c);
Err(FsNotFound(path.to_string()).into())
} else {
Ok(inode)
}
}
pub fn is_tracked<T: TreeTxnT>(txn: &T, path: &str) -> Result<bool, TxnErr<T::TreeError>> {
debug!("is_tracked {:?}", path);
let (_, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;
debug!("/is_tracked {:?}", path);
Ok(remaining_path_components.next().is_none())
}
pub fn inode_filename<T: TreeTxnT>(
txn: &T,
inode: Inode,
) -> Result<Option<String>, TxnErr<T::TreeError>> {
let mut components = Vec::new();
let mut current = inode;
loop {
match txn.get_revtree(current, None)? {
Some(v) => {
components.push(v.basename);
current = v.parent_inode;
if current == Inode::ROOT {
break;
}
}
None => {
debug!("filename_of_inode: not in tree");
return Ok(None);
}
}
}
let mut path = String::new();
for c in components.iter().rev() {
if !path.is_empty() {
path.push('/')
}
path.push_str(c.as_str());
}
Ok(Some(path))
}
fn make_new_child<T: TreeMutTxnT>(
txn: &mut T,
parent_inode: Inode,
filename: &str,
is_dir: bool,
child_inode: Option<Inode>,
) -> Result<Inode, FsError<T::TreeError>> {
let parent_id = OwnedPathId {
parent_inode,
basename: SmallString::from_str(filename),
};
if let Some(inode) = txn.get_tree(parent_id.as_file_id(), None)? {
debug!("inode = {:?}", inode);
if let Some(child) = child_inode {
if child == inode {
Ok(inode)
} else {
del_tree_with_rev(txn, parent_id.as_file_id(), inode)?;
if let Some(vertex) = txn.get_inodes(inode, None)? {
del_inodes_with_rev(txn, inode, vertex)?;
}
put_tree_with_rev(txn, parent_id.as_file_id(), child)?;
Ok(child)
}
} else {
Err(FsError::AlreadyInRepo(filename.to_string()))
}
} else {
let child_inode = match child_inode {
None => create_new_inode(txn)?,
Some(i) => i,
};
debug!("make_new_child: {:?} {:?}", parent_id, child_inode);
put_tree_with_rev(txn, parent_id.as_file_id(), child_inode)?;
if is_dir {
let dir_id = OwnedPathId {
parent_inode: child_inode,
basename: SmallString::new(),
};
txn.put_tree(dir_id.as_file_id(), child_inode)?;
};
Ok(child_inode)
}
}
pub(crate) fn add_inode<T: TreeMutTxnT>(
txn: &mut T,
inode: Option<Inode>,
path: &str,
is_dir: bool,
) -> Result<(), FsError<T::TreeError>> {
debug!("add_inode");
if let Some(parent) = crate::path::parent(path) {
let (current_inode, unrecorded_path) = closest_in_repo_ancestor(txn, parent)?;
let mut current_inode = current_inode;
debug!("add_inode: closest = {:?}", current_inode);
for c in unrecorded_path {
debug!("unrecorded: {:?}", c);
current_inode = make_new_child(txn, current_inode, c, true, None)?;
}
let file_name = crate::path::file_name(path).unwrap();
debug!("add_inode: file_name = {:?}", file_name);
make_new_child(txn, current_inode, file_name, is_dir, inode)?;
}
Ok(())
}
pub fn move_file<T: TreeMutTxnT>(
txn: &mut T,
origin: &str,
destination: &str,
) -> Result<(), FsError<T::TreeError>> {
debug!("move_file: {},{}", origin, destination);
move_file_by_inode(txn, find_inode(txn, origin)?, destination)?;
Ok(())
}
pub fn move_file_by_inode<T: TreeMutTxnT>(
txn: &mut T,
inode: Inode,
destination: &str,
) -> Result<(), FsError<T::TreeError>> {
let fileref = txn.get_revtree(inode, None)?.unwrap().to_owned();
debug!("fileref = {:?}", fileref);
del_tree_with_rev(txn, fileref.as_file_id(), inode)?;
debug!("inode={:?} destination={}", inode, destination);
let is_dir = txn
.get_tree(
(OwnedPathId {
parent_inode: inode,
basename: SmallString::new(),
})
.as_file_id(),
None,
)?
.is_some();
add_inode(txn, Some(inode), destination, is_dir)?;
Ok(())
}
pub(crate) fn rec_delete<T: TreeMutTxnT>(
txn: &mut T,
parent: OwnedPathId,
inode: Inode,
delete_inodes: bool,
) -> Result<(), FsError<T::TreeError>> {
let file_id = OwnedPathId {
parent_inode: inode,
basename: SmallString::new(),
};
let mut children = Vec::new();
let mut is_dir = false;
for x in txn.iter_tree(file_id.clone(), None)? {
let (k, inode) = x?;
if k.parent_inode > file_id.parent_inode {
break;
} else if k.parent_inode < file_id.parent_inode {
continue;
}
debug!("iter_tree: {:?} {:?}", k, inode);
is_dir = true;
if !k.basename.is_empty() {
children.push((k, inode))
}
}
for (k, inode_) in children {
assert_ne!(inode, inode_);
rec_delete(txn, k, inode_, delete_inodes)?;
}
debug!(
"rec_delete: {:?}, {:?}, {:?}, {:?}",
parent, file_id, inode, is_dir
);
if is_dir {
assert!(inode.is_root() || txn.del_tree(file_id.as_file_id(), Some(inode))?);
}
if !inode.is_root() && del_tree_with_rev(txn, parent.as_file_id(), inode)? {
if delete_inodes {
if let Some(vertex) = txn.get_inodes(inode, None)? {
del_inodes_with_rev(txn, inode, vertex)?;
}
}
} else {
debug!(
"rec_delete: {:?} {:?} not present",
parent.as_file_id(),
inode
);
}
Ok(())
}
pub fn remove_file<T: TreeMutTxnT>(txn: &mut T, path: &str) -> Result<(), FsError<T::TreeError>> {
debug!("remove file {:?}", path);
let inode = find_inode(txn, path)?;
let parent = if inode.is_root() {
OwnedPathId {
parent_inode: Inode::ROOT,
basename: SmallString::new(),
}
} else {
txn.get_revtree(inode, None)?.unwrap().to_owned()
};
debug!("remove file {:?} {:?}", parent, inode);
rec_delete(txn, parent, inode, false)?;
Ok(())
}
pub struct WorkingCopyChildren<'txn, T: TreeTxnT> {
iter: crate::pristine::Cursor<T, &'txn T, T::TreeCursor, OwnedPathId, Inode>,
inode: Inode,
}
impl<'txn, T: TreeTxnT> Iterator for WorkingCopyChildren<'txn, T> {
type Item = Result<(SmallString, Inode), T::TreeError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.iter.next() {
Some(Ok((k, v))) => {
if k.parent_inode == self.inode {
if k.basename.len() > 0 {
return Some(Ok((k.basename, v)));
}
} else if k.parent_inode > self.inode {
return None;
}
}
None => return None,
Some(Err(e)) => return Some(Err(e.0)),
}
}
}
}
pub fn working_copy_children<T: TreeTxnT>(
txn: &T,
inode: Inode,
) -> Result<WorkingCopyChildren<T>, T::TreeError> {
Ok(WorkingCopyChildren {
iter: txn
.iter_tree(
OwnedPathId {
parent_inode: inode,
basename: SmallString::new(),
},
None,
)
.map_err(|e| e.0)?,
inode,
})
}
pub struct WorkingCopyIterator<'txn, T: TreeTxnT> {
stack: Vec<(Inode, String)>,
txn: &'txn T,
}
impl<'txn, T: TreeTxnT> Iterator for WorkingCopyIterator<'txn, T> {
type Item = Result<(Inode, String), T::TreeError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some((inode, name)) = self.stack.pop() {
let fileid = OwnedPathId {
parent_inode: inode,
basename: SmallString::from_str(""),
};
let len = self.stack.len();
let iter = match self.txn.iter_tree(fileid, None) {
Ok(iter) => iter,
Err(e) => return Some(Err(e.0)),
};
for x in iter {
let (k, v) = match x {
Ok(x) => x,
Err(e) => return Some(Err(e.0)),
};
if k.parent_inode < inode {
continue;
} else if k.parent_inode > inode {
break;
}
if !k.basename.is_empty() {
let mut name = name.clone();
crate::path::push(&mut name, k.basename.as_str());
self.stack.push((v, name))
}
}
(&mut self.stack[len..]).reverse();
if !name.is_empty() {
return Some(Ok((inode, name)));
}
} else {
return None;
}
}
}
}
pub fn iter_working_copy<T: TreeTxnT>(txn: &T, root: Inode) -> WorkingCopyIterator<T> {
WorkingCopyIterator {
stack: vec![(root, String::new())],
txn,
}
}
pub struct GraphDescendants<'txn, T: GraphTxnT> {
txn: &'txn T,
channel: &'txn T::Graph,
stack: Vec<AdjacentIterator<'txn, T>>,
visited: HashSet<Position<ChangeId>>,
}
impl<'txn, T: GraphTxnT> Iterator for GraphDescendants<'txn, T> {
type Item = Result<Position<ChangeId>, T::GraphError>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(mut adj) = self.stack.pop() {
match adj.next() {
Some(Ok(child)) => {
self.stack.push(adj);
let dest = match self.txn.find_block(self.channel, child.dest) {
Ok(dest) => dest,
Err(BlockError::Txn(t)) => return Some(Err(t)),
Err(e) => panic!("{}", e),
};
let grandchild = match iter_adjacent(
self.txn,
self.channel,
dest,
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
) {
Ok(mut x) => match x.next().unwrap() {
Ok(x) => x,
Err(e) => return Some(Err(e.0)),
},
Err(e) => return Some(Err(e.0)),
};
if self.visited.insert(grandchild.dest) {
match iter_adjacent(
self.txn,
self.channel,
grandchild.dest.inode_vertex(),
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
) {
Ok(adj) => self.stack.push(adj),
Err(e) => return Some(Err(e.0)),
}
}
return Some(Ok(grandchild.dest));
}
Some(Err(e)) => return Some(Err(e.0)),
None => {
}
}
}
None
}
}
pub fn iter_graph_descendants<'txn, T: GraphTxnT>(
txn: &'txn T,
channel: &'txn T::Graph,
key: Position<ChangeId>,
) -> Result<GraphDescendants<'txn, T>, T::GraphError> {
Ok(GraphDescendants {
stack: vec![iter_adjacent(
txn,
&channel,
key.inode_vertex(),
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)
.map_err(|e| e.0)?],
visited: HashSet::new(),
txn,
channel,
})
}
pub struct GraphChildren<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> {
txn: &'txn T,
channel: &'txn T::Graph,
adj: AdjacentIterator<'txn, T>,
changes: &'changes P,
buf: Vec<u8>,
}
impl<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> Iterator
for GraphChildren<'txn, 'changes, T, P>
{
type Item = Result<(Position<ChangeId>, ChangeId, InodeMetadata, String), T::GraphError>;
fn next(&mut self) -> Option<Self::Item> {
let child = match self.adj.next()? {
Ok(child) => child,
Err(e) => return Some(Err(e.0)),
};
let dest = self.txn.find_block(self.channel, child.dest).unwrap();
let mut buf = std::mem::replace(&mut self.buf, Vec::new());
self.changes
.get_contents(|p| self.txn.get_external(p).unwrap(), dest, &mut buf)
.unwrap();
self.buf = buf;
let (perms, basename) = self.buf.split_at(2);
let perms = InodeMetadata::from_basename(perms);
let basename = std::str::from_utf8(basename).unwrap();
let grandchild = match iter_adjacent(
self.txn,
self.channel,
dest,
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
) {
Ok(mut adj) => match adj.next() {
Some(Ok(n)) => n,
None => unreachable!(),
Some(Err(e)) => return Some(Err(e.0)),
},
Err(e) => return Some(Err(e.0)),
};
Some(Ok((
grandchild.dest,
grandchild.introduced_by,
perms,
basename.to_string(),
)))
}
}
pub fn iter_graph_children<'txn, 'changes, T, P>(
txn: &'txn T,
changes: &'changes P,
channel: &'txn T::Graph,
key: Position<ChangeId>,
) -> Result<GraphChildren<'txn, 'changes, T, P>, T::GraphError>
where
T: GraphTxnT,
P: ChangeStore,
{
Ok(GraphChildren {
buf: Vec::new(),
adj: iter_adjacent(
txn,
channel,
key.inode_vertex(),
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)
.map_err(|e| e.0)?,
txn,
channel,
changes,
})
}
pub struct GraphBasenames<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> {
txn: &'txn T,
channel: &'txn T::Graph,
adj: AdjacentIterator<'txn, T>,
changes: &'changes P,
buf: Vec<u8>,
}
impl<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> Iterator
for GraphBasenames<'txn, 'changes, T, P>
{
type Item = Result<(Position<ChangeId>, InodeMetadata, String), T::GraphError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let parent = match self.adj.next() {
Some(Ok(n)) => n,
Some(Err(e)) => return Some(Err(e.0)),
None => return None,
};
let dest = self.txn.find_block_end(&self.channel, parent.dest).unwrap();
let mut buf = std::mem::replace(&mut self.buf, Vec::new());
self.changes
.get_contents(|p| self.txn.get_external(p).unwrap(), dest, &mut buf)
.unwrap();
self.buf = buf;
let (perms, basename) = self.buf.split_at(2);
let perms = InodeMetadata::from_basename(perms);
let basename = std::str::from_utf8(basename).unwrap().to_string();
match iter_adjacent(
self.txn,
&self.channel,
dest,
EdgeFlags::FOLDER | EdgeFlags::PARENT,
EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
) {
Ok(mut adj) => match adj.next() {
Some(Ok(grandparent)) => return Some(Ok((grandparent.dest, perms, basename))),
Some(Err(e)) => return Some(Err(e.0)),
None => {}
},
Err(e) => return Some(Err(e.0)),
}
}
}
}
pub fn iter_basenames<'txn, 'changes, T, P>(
txn: &'txn T,
changes: &'changes P,
channel: &'txn T::Graph,
pos: Position<ChangeId>,
) -> Result<GraphBasenames<'txn, 'changes, T, P>, T::GraphError>
where
T: GraphTxnT,
P: ChangeStore,
{
Ok(GraphBasenames {
buf: Vec::new(),
adj: iter_adjacent(
txn,
channel,
pos.inode_vertex(),
EdgeFlags::FOLDER | EdgeFlags::PARENT,
EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO,
)
.map_err(|e| e.0)?,
txn,
channel,
changes,
})
}
pub fn iter_paths<T: GraphTxnT, F: FnMut(&mut dyn Iterator<Item = Position<ChangeId>>) -> bool>(
txn: &T,
graph: &T::Graph,
key: Position<ChangeId>,
mut f: F,
) -> Result<(), TxnErr<T::GraphError>> {
let mut stack: Vec<(Position<ChangeId>, bool)> = vec![(key, true)];
while let Some((cur_key, on_stack)) = stack.pop() {
if cur_key.is_root() {
if !f(&mut stack
.iter()
.filter_map(|(key, on_path)| if *on_path { Some(*key) } else { None }))
{
break;
}
} else if !on_stack {
stack.push((cur_key, true));
let len = stack.len();
let f = EdgeFlags::parent_folder();
for parent in iter_adjacent(
txn,
&graph,
cur_key.inode_vertex(),
f,
f | EdgeFlags::PSEUDO,
)? {
let parent_dest = txn.find_block_end(&graph, parent?.dest).unwrap();
for grandparent in
iter_adjacent(txn, &graph, parent_dest, f, f | EdgeFlags::PSEUDO)?
{
stack.push((grandparent?.dest, false))
}
}
if stack.len() == len {
stack.pop();
}
}
}
Ok(())
}
pub(crate) fn follow_oldest_path<T: ChannelTxnT, C: ChangeStore>(
changes: &C,
txn: &T,
channel: &T::Channel,
path: &str,
) -> Result<(Position<ChangeId>, bool), FsErrorC<C::Error, T::GraphError>> {
use crate::pristine::*;
debug!("follow_oldest_path = {:?}", path);
let mut current = Position::ROOT;
let flag0 = EdgeFlags::FOLDER;
let flag1 = flag0 | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
let mut name_buf = Vec::new();
let mut ambiguous = false;
for c in crate::path::components(path) {
let mut next = None;
for name in iter_adjacent(txn, T::graph(channel), current.inode_vertex(), flag0, flag1)? {
let name = name?;
let name_dest = txn.find_block(T::graph(channel), name.dest).unwrap();
name_buf.clear();
debug!("getting contents {:?}", name);
changes
.get_contents(|h| txn.get_external(h).unwrap(), name_dest, &mut name_buf)
.map_err(FsErrorC::Changestore)?;
if std::str::from_utf8(&name_buf[2..]) == Ok(c) {
let age = txn
.get_changeset(T::changes(&channel), name.dest.change)
.unwrap();
if let Some((ref mut next, ref mut next_age)) = next {
ambiguous = true;
if age < *next_age {
*next = name_dest;
*next_age = age;
}
} else {
next = Some((name_dest, age));
}
}
}
if let Some((next, _)) = next {
current = iter_adjacent(txn, T::graph(channel), next, flag0, flag1)?
.next()
.unwrap()?
.dest
} else {
return Err(FsErrorC::NotFound(FsNotFound(path.to_string())));
}
}
Ok((current, ambiguous))
}
pub fn find_path<T: ChannelTxnT, C: ChangeStore>(
changes: &C,
txn: &T,
channel: &T::Channel,
youngest: bool,
mut v: Position<ChangeId>,
) -> Result<(String, bool), crate::output::FileError<C::Error, T::GraphError>> {
debug!("oldest_path = {:?}", v);
let mut path = Vec::new();
let mut name_buf = Vec::new();
let flag0 = EdgeFlags::FOLDER | EdgeFlags::PARENT;
let flag1 = EdgeFlags::all();
let mut all_alive = true;
while !v.change.is_root() {
let mut next_v = None;
let mut alive = false;
let inode_vertex = txn.find_block_end(T::graph(channel), v).unwrap();
assert_eq!(inode_vertex, v.inode_vertex());
for name in iter_adjacent(txn, T::graph(channel), v.inode_vertex(), flag0, flag1)? {
let name = name?;
if !name.flag.contains(EdgeFlags::PARENT) {
continue;
}
debug!("oldest_path, name = {:?}", name);
let age = txn
.get_changeset(T::changes(&channel), name.dest.change)?
.unwrap();
let name_dest = txn.find_block_end(T::graph(channel), name.dest).unwrap();
debug!("name_dest = {:?}", name_dest);
let mut next = None;
for e in iter_adjacent(txn, T::graph(channel), name_dest, flag0, flag1)? {
let e = e?;
if e.flag.contains(EdgeFlags::PARENT | EdgeFlags::FOLDER) {
next = Some(e);
break;
}
}
if let Some(next) = next {
debug!("oldest_path, next = {:?}", next);
if !next.flag.contains(EdgeFlags::DELETED) {
alive = true;
} else if alive {
break;
} else {
all_alive = false
}
if let Some((_, p_age, _)) = next_v {
if (age > p_age) ^ youngest {
continue;
}
}
next_v = Some((name_dest, age, next.dest));
}
}
let (name, _, next) = next_v.unwrap();
if alive {
name_buf.clear();
debug!("getting contents {:?}", name);
changes
.get_contents(|h| txn.get_external(h).unwrap(), name, &mut name_buf)
.map_err(crate::output::FileError::Changestore)?;
path.push(std::str::from_utf8(&name_buf[2..]).unwrap().to_string());
}
debug!("next = {:?}", next);
v = next;
}
path.reverse();
Ok((path.join("/"), all_alive))
}
pub fn get_latest_touch<'a, T: ChannelTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>>(
txn: &T,
channel: &T::Channel,
pos: Position<ChangeId>,
) -> Result<(u64, ChangeId), TxnErr<T::GraphError>> {
let mut latest_change = 0;
let mut id = ChangeId::ROOT;
let mut touch_iter = Some(txn.iter_touched(pos)?);
let mut log_iter = changeid_rev_log(txn, &channel, None)?;
let mut continue_ = true;
while continue_ {
continue_ = false;
if let Some(ref mut it) = touch_iter {
if let Some(c) = it.next() {
debug!("inode, change = {:?}", c);
let (inode, change) = c?;
if inode > pos {
touch_iter = None;
} else if inode == pos {
if let Some(t) = txn.get_changeset(T::changes(&channel), change)? {
if t >= latest_change {
latest_change = t;
id = change;
}
}
continue_ = true;
}
}
}
if let Some(l) = log_iter.next() {
debug!("int = {:?}", l);
let (n, (int, _)) = l?;
if txn.get_touched_files(pos, Some(int))?.is_some() {
id = int;
latest_change = n;
break;
}
continue_ = true;
}
}
Ok((latest_change, id))
}