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