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
use super::Workspace;
use backend::*;
use rand;
use std::collections::HashSet;
use std::mem::swap;
use Result;
impl<'env, T: rand::Rng> MutTxn<'env, T> {
pub(in unrecord) fn unrecord_nodes(
&mut self,
branch: &mut Branch,
patch_id: PatchId,
dependencies: &HashSet<Hash>,
up_context: &[Key<Option<Hash>>],
down_context: &[Key<Option<Hash>>],
line_num: LineId,
flag: EdgeFlags,
nodes: &[Vec<u8>],
w: &mut Workspace,
unused_in_other_branch: bool,
) -> Result<()> {
debug!(
"unrecord_nodes: {:?} {:?} {:?}",
patch_id,
line_num,
nodes.len()
);
// Delete the new nodes.
// Start by deleting all the "missing context repair" we've
// added when applying this patch, i.e. all the extra
// pseudo-edges that were inserted to connect the alive set of
// vertices.
// We make the assumption that no pseudo-edge is a shortcut
// for this NewNodes. This is because `nodes` is nonempty:
// indeed, any such pseudo-edge would stop at one of the nodes
// introduced by this NewNodes.
assert!(nodes.len() != 0);
// Remove the zombie edges introduced to repair the context,
// if it was missing when we applied this NewNodes.
for c in up_context.iter() {
let c = self.internal_key(c, patch_id);
self.remove_up_context_repair(branch, c, patch_id, &mut w.context_edges)?;
}
for c in down_context.iter() {
let c = self.internal_key(c, patch_id);
self.remove_down_context_repair(branch, c, patch_id, &mut w.context_edges)?;
}
// Delete the nodes and all their adjacent edges.
let mut k = Key {
patch: patch_id.clone(),
line: line_num.clone(),
};
for i in 0..nodes.len() {
debug!("starting k: {:?}", k);
if unused_in_other_branch {
// If this patch is unknown to any other branch,
// delete the contents of this node.
info!("deleting contents for key {:?}", k);
self.del_contents(k, None)?;
}
// Delete all edges adjacent to this node, which will also
// delete the node (we're only storing edges).
loop {
// Find the next edge from this key, or break if we're done.
let mut edge = if let Some(edge) = self.get_nodes(branch, k, None) {
edge.to_owned()
} else {
break;
};
debug!("{:?} {:?}", k, edge);
// Kill that edge in both directions.
self.del_edge_one_dir(branch, k, edge)?;
edge.flag.toggle(EdgeFlags::PARENT_EDGE);
swap(&mut edge.dest, &mut k);
self.del_edge_one_dir(branch, k, edge)?;
swap(&mut edge.dest, &mut k);
}
// If this is a file addition, delete it from inodes/revinodes.
if flag.contains(EdgeFlags::FOLDER_EDGE) {
self.undo_file_addition(patch_id, k, i, nodes, down_context)?
}
// Increment the line id (its type, LineId, implements
// little-endian additions with usize. See the `backend`
// module).
k.line += 1
}
// From all nodes in the down context, climb deleted paths up
// until finding alive ancestors, and add pseudo-edges from
// these ancestors to the down context.
let internal_down_context: Vec<_> = down_context
.iter()
.map(|c| self.internal_key(c, patch_id))
.collect();
if flag.contains(EdgeFlags::FOLDER_EDGE) {
// unimplemented!()
} else {
self.reconnect_across_deleted_nodes(
patch_id,
branch,
dependencies,
&internal_down_context,
)?
}
Ok(())
}
fn undo_file_addition(
&mut self,
patch_id: PatchId,
k: Key<PatchId>,
i: usize,
nodes: &[Vec<u8>],
down_context: &[Key<Option<Hash>>],
) -> Result<()> {
debug!(
"remove_file_from_inodes: {:?} {:?} {:?}",
k,
i,
nodes.len() - 1
);
// Is this a move?
if i == nodes.len() - 1 && !down_context.is_empty() {
// Yes, this is a move.
assert_eq!(down_context.len(), 1);
let internal = self.internal_key(&down_context[0], patch_id).to_owned();
// Mark the file as moved.
let inode = self.get_revinodes(internal).unwrap().to_owned();
let mut file = self.get_inodes(inode).unwrap().to_owned();
file.status = FileStatus::Moved;
self.replace_inodes(inode, file)?;
} else {
// No, this is actually an addition. We have to leave the
// inodes in place in trees and revtrees, and remove them
// from inodes.
self.remove_file_from_inodes(k)?;
}
Ok(())
}
}