libpijul_compat/unrecord/
mod.rs

1use apply::find_alive::FindAlive;
2use backend::*;
3use patch::*;
4use rand;
5use std::collections::{HashMap, HashSet};
6use Result;
7mod context_repair;
8mod edges;
9mod nodes;
10
11#[derive(Debug)]
12struct Workspace {
13    file_moves: HashSet<Key<PatchId>>,
14    context_edges: HashMap<Key<PatchId>, Edge>,
15}
16
17impl<'env, T: rand::Rng> MutTxn<'env, T> {
18    pub fn unrecord(
19        &mut self,
20        branch: &mut Branch,
21        patch_id: PatchId,
22        patch: &Patch,
23    ) -> Result<bool> {
24        let timestamp = self.get_patch(&branch.patches, patch_id).unwrap();
25        let is_on_branch = self.del_patches(&mut branch.patches, patch_id)?;
26        self.del_revpatches(&mut branch.revpatches, timestamp, patch_id)?;
27
28        // Is the patch used in another branch?;
29        let unused_in_other_branches = {
30            let mut it = self.iter_branches(None).filter(|br| {
31                br.name != branch.name && self.get_patch(&br.patches, patch_id).is_some()
32            });
33            it.next().is_none()
34        };
35
36        if is_on_branch {
37            debug!("unrecord: {:?}", patch_id);
38
39            self.unapply(branch, patch_id, patch, unused_in_other_branches)?;
40
41            for dep in patch.dependencies().iter() {
42                let internal_dep = self.get_internal(dep.as_ref()).unwrap().to_owned();
43                // Test whether other branches have both this patch and `dep`.
44                let other_branches_have_dep = self.iter_branches(None).any(|br| {
45                    br.name != branch.name && self.get_patch(&br.patches, internal_dep).is_some()
46                        && self.get_patch(&br.patches, patch_id).is_some()
47                });
48
49                if !other_branches_have_dep {
50                    self.del_revdep(internal_dep, Some(patch_id))?;
51                }
52            }
53        }
54
55        // If no other branch uses this patch, delete from revdeps.
56        if unused_in_other_branches {
57            info!("deleting patch");
58            // Delete all references to patch_id in revdep.
59            while self.del_revdep(patch_id, None)? {}
60            let ext = self.get_external(patch_id).unwrap().to_owned();
61            self.del_external(patch_id)?;
62            self.del_internal(ext.as_ref())?;
63            Ok(false)
64        } else {
65            Ok(true)
66        }
67    }
68
69    /// Unrecord the patch, returning true if and only if another
70    /// branch still uses this patch.
71    pub fn unapply(
72        &mut self,
73        branch: &mut Branch,
74        patch_id: PatchId,
75        patch: &Patch,
76        unused_in_other_branch: bool,
77    ) -> Result<()> {
78        debug!("revdep: {:?}", self.get_revdep(patch_id, None));
79
80        // Check that the branch has no patch that depends on this one.
81        assert!(
82            self.iter_revdep(Some((patch_id, None)))
83                .take_while(|&(p, _)| p == patch_id)
84                .all(|(_, p)| self.get_patch(&branch.patches, p).is_none())
85        );
86
87        let mut workspace = Workspace {
88            file_moves: HashSet::new(),
89            context_edges: HashMap::new(),
90        };
91        let mut find_alive = FindAlive::new();
92
93        // Check applied, check dependencies.
94        for change in patch.changes().iter() {
95            match *change {
96                Change::NewEdges {
97                    ref edges,
98                    previous,
99                    flag,
100                    ..
101                } => self.unrecord_edges(
102                    &mut find_alive,
103                    branch,
104                    patch_id,
105                    patch.dependencies(),
106                    previous,
107                    flag,
108                    edges,
109                    &mut workspace,
110                    unused_in_other_branch,
111                )?,
112                Change::NewNodes {
113                    ref up_context,
114                    ref down_context,
115                    ref line_num,
116                    ref flag,
117                    ref nodes,
118                    ..
119                } => self.unrecord_nodes(
120                    branch,
121                    patch_id,
122                    patch.dependencies(),
123                    up_context,
124                    down_context,
125                    *line_num,
126                    *flag,
127                    nodes,
128                    &mut workspace,
129                    unused_in_other_branch,
130                )?,
131            }
132        }
133        Ok(())
134    }
135
136    fn reconnect_across_deleted_nodes(
137        &mut self,
138        patch_id: PatchId,
139        branch: &mut Branch,
140        dependencies: &HashSet<Hash>,
141        deleted_nodes: &[Key<PatchId>],
142    ) -> Result<()> {
143        debug!("reconnect_across_deleted_nodes");
144        let mut find_alive = FindAlive::new();
145        let mut alive_ancestors = Vec::new();
146        let mut files = Vec::new();
147
148        // find alive descendants of the deleted nodes.
149        let mut alive_descendants = Vec::new();
150        for &c in deleted_nodes {
151            debug!("down_context c = {:?}", c);
152            if !self.is_alive(branch, c) {
153                find_alive.clear();
154                find_alive.push(c);
155                self.find_alive_descendants(&mut find_alive, branch, &mut alive_descendants);
156            }
157        }
158
159        if !alive_descendants.is_empty() {
160            // find alive ancestors of the deleted nodes.
161            for &c in deleted_nodes {
162                debug!("down_context c = {:?}", c);
163                if !self.is_alive(branch, c) {
164                    find_alive.clear();
165                    find_alive.push(c);
166                    let mut first_file = None;
167                    self.find_alive_ancestors(
168                        &mut find_alive,
169                        branch,
170                        &mut alive_ancestors,
171                        &mut first_file,
172                        &mut files,
173                    );
174                }
175            }
176            debug!(
177                "ancestors = {:?}, descendants = {:?}",
178                alive_ancestors, alive_descendants
179            );
180            // file_edges should contain the now zombie files.
181            debug!("file: {:?}", files);
182            for (mut k, mut v) in files.drain(..) {
183                assert!(v.flag.contains(EdgeFlags::DELETED_EDGE));
184                assert!(v.introduced_by != patch_id);
185                let introduced_by = self.get_external(v.introduced_by).unwrap().to_owned();
186                if !dependencies.contains(&introduced_by) {
187                    v.flag = (v.flag | EdgeFlags::PSEUDO_EDGE) ^ EdgeFlags::DELETED_EDGE;
188                    debug!("zombie {:?} {:?}", k, v);
189                    self.put_nodes(branch, k, &v)?;
190                }
191            }
192
193            for ancestor in alive_ancestors.iter() {
194                for descendant in alive_descendants.iter() {
195                    let mut edge = Edge::zero(EdgeFlags::PSEUDO_EDGE);
196                    edge.dest = *descendant;
197                    debug!("adding {:?} -> {:?}", ancestor, edge);
198                    self.put_nodes_with_rev(branch, *ancestor, edge)?;
199                }
200            }
201        }
202        Ok(())
203    }
204
205    fn remove_file_from_inodes(&mut self, k: Key<PatchId>) -> Result<()> {
206        let inode = self.get_revinodes(k).map(|x| x.to_owned());
207        if let Some(inode) = inode {
208            self.del_revinodes(k, None)?;
209            self.del_inodes(inode, None)?;
210        }
211        Ok(())
212    }
213}