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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
use backend::*;
use patch::*;
use rand;
use record::{InodeUpdate, RecordState};
use std::collections::HashSet;
use std::path::Path;
use Result;
mod apply;
pub mod find_alive;
mod repair_deleted_context;
use output;

impl<U: Transaction, R> GenericTxn<U, R> {
    /// Return the patch id corresponding to `e`, or `internal` if `e==None`.
    pub fn internal_hash(&self, e: &Option<Hash>, internal: PatchId) -> PatchId {
        match *e {
            Some(Hash::None) => ROOT_PATCH_ID.clone(),
            Some(ref h) => self.get_internal(h.as_ref()).unwrap().to_owned(),
            None => internal.clone(),
        }
    }

    /// Fetch the internal key for this external key (or `internal` if
    /// `key.patch` is `None`).
    pub fn internal_key(&self, key: &Key<Option<Hash>>, internal: PatchId) -> Key<PatchId> {
        // debug!("internal_key: {:?} {:?}", key, internal);
        Key {
            patch: self.internal_hash(&key.patch, internal),
            line: key.line.clone(),
        }
    }

    pub fn internal_key_unwrap(&self, key: &Key<Option<Hash>>) -> Key<PatchId> {
        Key {
            patch: self.get_internal(key.patch.as_ref().unwrap().as_ref())
                .unwrap()
                .to_owned(),
            line: key.line.clone(),
        }
    }
}

impl<'env, T: rand::Rng> MutTxn<'env, T> {
    /// Assumes all patches have been downloaded. The third argument
    /// `remote_patches` needs to contain at least all the patches we
    /// want to apply, and the fourth one `local_patches` at least all
    /// the patches the other repository doesn't have.
    pub fn apply_patches<F, P: output::ToPrefixes>(
        &mut self,
        branch: &mut Branch,
        r: &Path,
        remote_patches: &[(Hash, Patch)],
        partial_paths: P,
        mut f: F,
    ) -> Result<()>
    where
        F: FnMut(usize, &Hash),
    {
        let (pending, local_pending) = {
            let mut record = RecordState::new();
            self.record(&mut record, branch, &r, None)?;
            let (changes, local) = record.finish();
            let mut p = UnsignedPatch::empty();
            p.changes = changes
                .into_iter()
                .flat_map(|x| x.into_iter())
                .map(|x| self.globalize_change(x))
                .collect();
            p.dependencies = self.dependencies(&branch, p.changes.iter());
            (p.leave_unsigned(), local)
        };

        let mut new_patches_count = 0;
        for &(ref p, ref patch) in remote_patches.iter() {
            debug!("apply_patches: {:?}", p);
            self.apply_patches_rec(branch, remote_patches, p, patch, &mut new_patches_count)?;
            f(new_patches_count, p);
        }
        debug!("{} patches applied", new_patches_count);

        if new_patches_count > 0 {
            let partial_paths = partial_paths.to_prefixes(self, &branch);
            self.output_changes_file(&branch, r)?;
            debug!("output_repository");
            self.output_partials(branch.name.as_str(), &partial_paths)?;
            self.output_repository(branch, &r, &partial_paths, &pending, &local_pending)?;
            debug!("done outputting_repository");
        }
        debug!("finished apply_patches");
        Ok(())
    }

    /// Lower-level applier. This function only applies patches as
    /// found in `patches_dir`, following dependencies recursively. It
    /// outputs neither the repository nor the "changes file" of the
    /// branch, necessary to exchange patches locally or over HTTP.
    pub fn apply_patches_rec(
        &mut self,
        branch: &mut Branch,
        patches: &[(Hash, Patch)],
        patch_hash: &Hash,
        patch: &Patch,
        new_patches_count: &mut usize,
    ) -> Result<()> {
        let internal = {
            if let Some(internal) = self.get_internal(patch_hash.as_ref()) {
                if self.get_patch(&branch.patches, internal).is_some() {
                    debug!(
                        "get_patch returned {:?}",
                        self.get_patch(&branch.patches, internal)
                    );
                    None
                } else {
                    // Doesn't have patch, but the patch is known in
                    // another branch
                    Some(internal.to_owned())
                }
            } else {
                // The patch is totally new to the repository.
                let internal = self.new_internal(patch_hash.as_ref());
                Some(internal)
            }
        };
        if let Some(internal) = internal {
            info!(
                "Now applying patch {:?} {:?} to branch {:?}",
                patch.name, patch_hash, branch
            );
            if patch.dependencies().is_empty() {
                info!("Patch {:?} has no dependencies", patch_hash);
            }
            for dep in patch.dependencies().iter() {
                info!("Applying dependency {:?}", dep);
                info!("dep hash {:?}", dep.to_base58());
                let is_applied = {
                    if let Some(dep_internal) = self.get_internal(dep.as_ref()) {
                        self.get_patch(&branch.patches, dep_internal).is_some()
                    } else {
                        false
                    }
                };
                if !is_applied {
                    info!("Not applied");
                    // If `patches` is sorted in topological order,
                    // this shouldn't happen, because the dependencies
                    // have been applied before.
                    if let Some(&(_, ref patch)) = patches.iter().find(|&&(ref a, _)| a == dep) {
                        self.apply_patches_rec(branch, patches, &dep, patch, new_patches_count)?;
                    } else {
                        error!("Dependency not found");
                    }
                } else {
                    info!("Already applied");
                }
                let dep_internal = self.get_internal(dep.as_ref()).unwrap().to_owned();
                self.put_revdep(dep_internal, internal)?;
                self.put_dep(internal, dep_internal)?;
            }

            // Sanakirja doesn't let us insert the same pair twice.
            self.register_patch(internal, patch_hash, patch)?;

            let now = branch.apply_counter;
            branch.apply_counter += 1;
            self.apply(branch, &patch, internal, now)?;

            *new_patches_count += 1;

            Ok(())
        } else {
            info!("Patch {:?} has already been applied", patch_hash);
            Ok(())
        }
    }

    /// Apply a patch from a local record: register it, give it a hash, and then apply.
    pub fn apply_local_patch(
        &mut self,
        branch: &mut Branch,
        working_copy: &Path,
        hash: &Hash,
        patch: &Patch,
        inode_updates: &HashSet<InodeUpdate>,
        is_pending: bool,
    ) -> Result<PatchId> {
        info!("registering a patch with {} changes", patch.changes().len());
        info!("dependencies: {:?}", patch.dependencies());

        // let child_patch = patch.clone();

        let internal: PatchId = self.new_internal(hash.as_ref());

        for dep in patch.dependencies().iter() {
            let dep_internal = self.get_internal(dep.as_ref()).unwrap().to_owned();
            self.put_revdep(dep_internal, internal)?;
            self.put_dep(internal, dep_internal)?;
        }
        self.register_patch(internal, hash, patch)?;

        info!("Applying local patch");
        let now = branch.apply_counter;
        self.apply(branch, &patch, internal, now)?;
        debug!("synchronizing tree: {:?}", inode_updates);
        for update in inode_updates.iter() {
            self.update_inode(&branch, internal, update)?;
        }
        debug!("committing branch");
        if !is_pending {
            debug!("not pending, adding to changes");
            branch.apply_counter += 1;
            self.output_changes_file(&branch, working_copy)?;
        }
        trace!("done apply_local_patch");
        Ok(internal)
    }

    fn register_patch(&mut self, internal: PatchId, hash: &Hash, patch: &Patch) -> Result<()> {
        self.put_external(internal, hash.as_ref())?;
        self.put_internal(hash.as_ref(), internal)?;
        for hunk in patch.changes() {
            let inode = match *hunk {
                Change::NewNodes { ref inode, .. } => inode,
                Change::NewEdges { ref inode, .. } => inode,
            };
            let inode = self.internal_key(inode, internal);
            self.put_touched_file(inode, internal)?;
        }
        Ok(())
    }

    /// Update the inodes/revinodes, tree/revtrees databases with the
    /// patch we just applied. This is because files don't really get
    /// moved or deleted before we apply the patch, they are just
    /// "marked as moved/deleted". This function does the actual
    /// update.
    fn update_inode(
        &mut self,
        branch: &Branch,
        internal: PatchId,
        update: &InodeUpdate,
    ) -> Result<()> {
        match *update {
            InodeUpdate::Add {
                ref line,
                ref meta,
                inode,
            } => {
                let key = FileHeader {
                    metadata: *meta,
                    status: FileStatus::Ok,
                    key: Key {
                        patch: internal.clone(),
                        line: line.clone(),
                    },
                };
                // If this file addition was actually recorded.
                if self.get_nodes(&branch, key.key, None).is_some() {
                    debug!("it's in here!: {:?} {:?}", key, inode);
                    self.replace_inodes(inode, key)?;
                    self.replace_revinodes(key.key, inode)?;
                }
            }
            InodeUpdate::Deleted { inode } => {
                // If this change was actually applied.
                debug!("deleted: {:?}", inode);
                let header = self.get_inodes(inode).unwrap().clone();
                debug!("deleted header: {:?}", header);
                let edge = Edge::zero(
                    EdgeFlags::PARENT_EDGE | EdgeFlags::FOLDER_EDGE | EdgeFlags::DELETED_EDGE,
                );
                if self.iter_nodes(&branch, Some((header.key, Some(&edge))))
                    .take_while(|&(k, v)| k == header.key && edge.flag == v.flag)
                    .any(|(_, v)| v.introduced_by == internal)
                {
                    self.del_inodes(inode, Some(header))?;
                    self.del_revinodes(header.key, Some(inode))?;

                    // We might have killed the parent in the same
                    // update.
                    if let Some(parent) = self.get_revtree(inode).map(|x| x.to_owned()) {
                        let parent = parent.as_file_id();
                        self.del_tree(&parent, None)?;
                        self.del_revtree(inode, None)?;
                    }
                }
            }
            InodeUpdate::Moved { inode } => {
                // If this change was actually applied.
                debug!("moved: {:?}", inode);
                let mut header = self.get_inodes(inode).unwrap().clone();
                debug!("moved header: {:?}", header);
                let edge = Edge::zero(EdgeFlags::PARENT_EDGE | EdgeFlags::FOLDER_EDGE);
                if self.iter_nodes(&branch, Some((header.key, Some(&edge))))
                    .take_while(|&(k, v)| k == header.key && edge.flag == v.flag)
                    .any(|(_, v)| v.introduced_by == internal)
                {
                    header.status = FileStatus::Ok;
                    self.replace_inodes(inode, header)?;
                    self.replace_revinodes(header.key, inode)?;
                }
            }
        }
        Ok(())
    }
}