libpijul 0.12.0

A patch-based distributed version control system, easy to use and fast.
use backend::*;
use std::collections::HashSet;

#[derive(Debug)]
pub struct FindAlive {
    stack: Vec<Key<PatchId>>,
    visited: HashSet<Key<PatchId>>,
}

impl FindAlive {
    pub fn new() -> Self {
        FindAlive {
            stack: Vec::new(),
            visited: HashSet::new(),
        }
    }
    pub fn clear(&mut self) {
        self.stack.clear();
        self.visited.clear();
    }
    pub fn push(&mut self, k: Key<PatchId>) {
        self.stack.push(k)
    }
    pub fn pop(&mut self) -> Option<Key<PatchId>> {
        while let Some(p) = self.stack.pop() {
            if !self.visited.contains(&p) {
                self.visited.insert(p.clone());
                return Some(p);
            }
        }
        None
    }
}

impl<U: Transaction, R> GenericTxn<U, R> {
    /// Recursively find all ancestors by doing a DFS, and collect all
    /// edges until finding an alive ancestor.
    ///
    /// Returns whether or not at least one traversed vertex was dead
    /// (or otherwise said, returns `false` if and only if there all
    /// vertices in `find_alive` are alive).
    pub fn find_alive_ancestors(
        &self,
        find_alive: &mut FindAlive,
        branch: &Branch,
        alive: &mut Vec<Key<PatchId>>,
        file_ancestor: &mut Option<Key<PatchId>>,
        files: &mut Vec<(Key<PatchId>, Edge)>,
    ) -> bool {
        let mut first_is_alive = false;
        let mut file = None;
        while let Some(a) = find_alive.pop() {
            if self.is_alive(branch, a) {
                // This node is alive.
                alive.push(a);
            } else {
                first_is_alive = true;
                for v in self.iter_adjacent(
                    &branch,
                    a,
                    EdgeFlags::PARENT_EDGE | EdgeFlags::DELETED_EDGE,
                    EdgeFlags::all(),
                ) {
                    debug!("find_alive_ancestors: {:?}", v);
                    if v.flag.contains(EdgeFlags::FOLDER_EDGE) {
                        // deleted file.
                        file = Some(a);
                        *file_ancestor = Some(a)
                    } else {
                        find_alive.push(v.dest)
                    }
                }
            }
        }
        debug!("file {:?}", file);
        if let Some(file) = file {
            find_alive.clear();
            find_alive.push(file);
            while let Some(a) = find_alive.pop() {
                debug!("file {:?}", a);
                if !self.is_alive(branch, a) {
                    debug!("not alive");
                    first_is_alive = true;
                    let flag =
                        EdgeFlags::PARENT_EDGE | EdgeFlags::DELETED_EDGE | EdgeFlags::FOLDER_EDGE;
                    for v in self.iter_adjacent(&branch, a, flag, EdgeFlags::all()) {
                        debug!("file find_alive_ancestors: {:?}", v);
                        // deleted file, collect.
                        files.push((a, v));
                        find_alive.push(v.dest)
                    }
                }
            }
        }

        first_is_alive
    }

    /// Recursively find all descendants by doing a DFS on deleted
    /// edges (including folder edges), and collect all edges until
    /// finding an alive or zombie descendant.
    ///
    /// Returns whether or not at least one traversed vertex was dead
    /// (or otherwise said, returns `false` if and only if there all
    /// vertices in `find_alive` are alive).
    pub fn find_alive_descendants(
        &self,
        find_alive: &mut FindAlive,
        branch: &Branch,
        alive: &mut Vec<Key<PatchId>>,
    ) -> bool {
        let mut first_is_alive = false;
        debug!("begin find_alive_descendants");
        while let Some(a) = find_alive.pop() {
            debug!("find_alive_descendants, a = {:?}", a);
            if self.is_alive(branch, a) {
                debug!("alive: {:?}", a);
                alive.push(a);
            } else {
                // Else, we need to explore its deleted descendants.
                first_is_alive = true;
                for v in self
                    .iter_adjacent(&branch, a, EdgeFlags::empty(), EdgeFlags::all())
                    .filter(|v| !v.flag.contains(EdgeFlags::PARENT_EDGE))
                {
                    debug!("v = {:?}", v);
                    if v.flag.contains(EdgeFlags::DELETED_EDGE) {
                        debug!("find_alive_descendants: {:?}", v);
                        find_alive.push(v.dest)
                    } else {
                        debug!("alive in for: {:?}", v.dest);
                        alive.push(v.dest)
                    }
                }
            }
        }
        debug!("end find_alive_descendants");
        first_is_alive
    }

    fn find_alive(
        &self,
        branch: &Branch,
        find_alive: &mut FindAlive,
        alive: &mut HashSet<Key<PatchId>>,
        file: &mut Option<Key<PatchId>>,
        current: Key<PatchId>,
        flag: EdgeFlags,
    ) {
        find_alive.clear();
        debug!("find_alive: {:?}", current);
        find_alive.push(current);
        while let Some(current) = find_alive.pop() {
            debug!("find_alive, current = {:?}", current);
            if self.is_alive(branch, current) {
                alive.insert(current.clone());
            } else {
                let flagmax = flag | EdgeFlags::FOLDER_EDGE | EdgeFlags::PSEUDO_EDGE;
                for e in self
                    .iter_adjacent(branch, current, flag, flagmax)
                    .take_while(|e| {
                        e.flag | EdgeFlags::FOLDER_EDGE | EdgeFlags::PSEUDO_EDGE == flagmax
                    })
                {
                    debug!("e = {:?}", e);
                    // e might be FOLDER_EDGE here.
                    if e.flag.contains(EdgeFlags::FOLDER_EDGE) && file.is_none() {
                        *file = Some(current.clone())
                    } else {
                        find_alive.push(e.dest.clone())
                    }
                }
            }
        }
    }

    /// Find the alive descendants of `current`. `cache` is here to
    /// avoid cycles, and `alive` is an accumulator of the
    /// result. Since this search stops at files, if the file
    /// containing these lines is ever hit, it will be put in `file`.
    pub fn find_alive_nonfolder_descendants(
        &self,
        branch: &Branch,
        find_alive: &mut FindAlive,
        alive: &mut HashSet<Key<PatchId>>,
        file: &mut Option<Key<PatchId>>,
        current: Key<PatchId>,
    ) {
        self.find_alive(
            branch,
            find_alive,
            alive,
            file,
            current,
            EdgeFlags::DELETED_EDGE,
        )
    }

    /// Find the alive ancestors of `current`. `cache` is here to
    /// avoid cycles, and `alive` is an accumulator of the
    /// result. Since this search stops at files, if the file
    /// containing these lines is ever hit, it will be put in `file`.
    pub fn find_alive_nonfolder_ancestors(
        &self,
        branch: &Branch,
        find_alive: &mut FindAlive,
        alive: &mut HashSet<Key<PatchId>>,
        file: &mut Option<Key<PatchId>>,
        current: Key<PatchId>,
    ) {
        self.find_alive(
            branch,
            find_alive,
            alive,
            file,
            current,
            EdgeFlags::DELETED_EDGE | EdgeFlags::PARENT_EDGE,
        )
    }
}