libpijul_compat/unrecord/
mod.rs1use 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 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 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 unused_in_other_branches {
57 info!("deleting patch");
58 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 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 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 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 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 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 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}