libpijul_compat/apply/
mod.rs

1use backend::*;
2use patch::*;
3use rand;
4use record::{InodeUpdate, RecordState};
5use std::collections::HashSet;
6use std::path::Path;
7use Result;
8mod apply;
9pub mod find_alive;
10mod repair_deleted_context;
11use output;
12
13impl<U: Transaction, R> GenericTxn<U, R> {
14    /// Return the patch id corresponding to `e`, or `internal` if `e==None`.
15    pub fn internal_hash(&self, e: &Option<Hash>, internal: PatchId) -> PatchId {
16        match *e {
17            Some(Hash::None) => ROOT_PATCH_ID.clone(),
18            Some(ref h) => self.get_internal(h.as_ref()).unwrap().to_owned(),
19            None => internal.clone(),
20        }
21    }
22
23    /// Fetch the internal key for this external key (or `internal` if
24    /// `key.patch` is `None`).
25    pub fn internal_key(&self, key: &Key<Option<Hash>>, internal: PatchId) -> Key<PatchId> {
26        // debug!("internal_key: {:?} {:?}", key, internal);
27        Key {
28            patch: self.internal_hash(&key.patch, internal),
29            line: key.line.clone(),
30        }
31    }
32
33    pub fn internal_key_unwrap(&self, key: &Key<Option<Hash>>) -> Key<PatchId> {
34        Key {
35            patch: self.get_internal(key.patch.as_ref().unwrap().as_ref())
36                .unwrap()
37                .to_owned(),
38            line: key.line.clone(),
39        }
40    }
41}
42
43impl<'env, T: rand::Rng> MutTxn<'env, T> {
44    /// Assumes all patches have been downloaded. The third argument
45    /// `remote_patches` needs to contain at least all the patches we
46    /// want to apply, and the fourth one `local_patches` at least all
47    /// the patches the other repository doesn't have.
48    pub fn apply_patches<F, P: output::ToPrefixes>(
49        &mut self,
50        branch: &mut Branch,
51        r: &Path,
52        remote_patches: &[(Hash, Patch)],
53        partial_paths: P,
54        mut f: F,
55    ) -> Result<()>
56    where
57        F: FnMut(usize, &Hash),
58    {
59        let (pending, local_pending) = {
60            let mut record = RecordState::new();
61            self.record(&mut record, branch, &r, None)?;
62            let (changes, local) = record.finish();
63            let mut p = UnsignedPatch::empty();
64            p.changes = changes
65                .into_iter()
66                .flat_map(|x| x.into_iter())
67                .map(|x| self.globalize_change(x))
68                .collect();
69            p.dependencies = self.dependencies(&branch, p.changes.iter());
70            (p.leave_unsigned(), local)
71        };
72
73        let mut new_patches_count = 0;
74        for &(ref p, ref patch) in remote_patches.iter() {
75            debug!("apply_patches: {:?}", p);
76            self.apply_patches_rec(branch, remote_patches, p, patch, &mut new_patches_count)?;
77            f(new_patches_count, p);
78        }
79        debug!("{} patches applied", new_patches_count);
80
81        if new_patches_count > 0 {
82            let partial_paths = partial_paths.to_prefixes(self, &branch);
83            self.output_changes_file(&branch, r)?;
84            debug!("output_repository");
85            self.output_partials(branch.name.as_str(), &partial_paths)?;
86            self.output_repository(branch, &r, &partial_paths, &pending, &local_pending)?;
87            debug!("done outputting_repository");
88        }
89        debug!("finished apply_patches");
90        Ok(())
91    }
92
93    /// Lower-level applier. This function only applies patches as
94    /// found in `patches_dir`, following dependencies recursively. It
95    /// outputs neither the repository nor the "changes file" of the
96    /// branch, necessary to exchange patches locally or over HTTP.
97    pub fn apply_patches_rec(
98        &mut self,
99        branch: &mut Branch,
100        patches: &[(Hash, Patch)],
101        patch_hash: &Hash,
102        patch: &Patch,
103        new_patches_count: &mut usize,
104    ) -> Result<()> {
105        let internal = {
106            if let Some(internal) = self.get_internal(patch_hash.as_ref()) {
107                if self.get_patch(&branch.patches, internal).is_some() {
108                    debug!(
109                        "get_patch returned {:?}",
110                        self.get_patch(&branch.patches, internal)
111                    );
112                    None
113                } else {
114                    // Doesn't have patch, but the patch is known in
115                    // another branch
116                    Some(internal.to_owned())
117                }
118            } else {
119                // The patch is totally new to the repository.
120                let internal = self.new_internal(patch_hash.as_ref());
121                Some(internal)
122            }
123        };
124        if let Some(internal) = internal {
125            info!(
126                "Now applying patch {:?} {:?} to branch {:?}",
127                patch.name, patch_hash, branch
128            );
129            if patch.dependencies().is_empty() {
130                info!("Patch {:?} has no dependencies", patch_hash);
131            }
132            for dep in patch.dependencies().iter() {
133                info!("Applying dependency {:?}", dep);
134                info!("dep hash {:?}", dep.to_base58());
135                let is_applied = {
136                    if let Some(dep_internal) = self.get_internal(dep.as_ref()) {
137                        self.get_patch(&branch.patches, dep_internal).is_some()
138                    } else {
139                        false
140                    }
141                };
142                if !is_applied {
143                    info!("Not applied");
144                    // If `patches` is sorted in topological order,
145                    // this shouldn't happen, because the dependencies
146                    // have been applied before.
147                    if let Some(&(_, ref patch)) = patches.iter().find(|&&(ref a, _)| a == dep) {
148                        self.apply_patches_rec(branch, patches, &dep, patch, new_patches_count)?;
149                    } else {
150                        error!("Dependency not found");
151                    }
152                } else {
153                    info!("Already applied");
154                }
155                let dep_internal = self.get_internal(dep.as_ref()).unwrap().to_owned();
156                self.put_revdep(dep_internal, internal)?;
157                self.put_dep(internal, dep_internal)?;
158            }
159
160            // Sanakirja doesn't let us insert the same pair twice.
161            self.register_patch(internal, patch_hash, patch)?;
162
163            let now = branch.apply_counter;
164            branch.apply_counter += 1;
165            self.apply(branch, &patch, internal, now)?;
166
167            *new_patches_count += 1;
168
169            Ok(())
170        } else {
171            info!("Patch {:?} has already been applied", patch_hash);
172            Ok(())
173        }
174    }
175
176    /// Apply a patch from a local record: register it, give it a hash, and then apply.
177    pub fn apply_local_patch(
178        &mut self,
179        branch: &mut Branch,
180        working_copy: &Path,
181        hash: &Hash,
182        patch: &Patch,
183        inode_updates: &HashSet<InodeUpdate>,
184        is_pending: bool,
185    ) -> Result<PatchId> {
186        info!("registering a patch with {} changes", patch.changes().len());
187        info!("dependencies: {:?}", patch.dependencies());
188
189        // let child_patch = patch.clone();
190
191        let internal: PatchId = self.new_internal(hash.as_ref());
192
193        for dep in patch.dependencies().iter() {
194            let dep_internal = self.get_internal(dep.as_ref()).unwrap().to_owned();
195            self.put_revdep(dep_internal, internal)?;
196            self.put_dep(internal, dep_internal)?;
197        }
198        self.register_patch(internal, hash, patch)?;
199
200        info!("Applying local patch");
201        let now = branch.apply_counter;
202        self.apply(branch, &patch, internal, now)?;
203        debug!("synchronizing tree: {:?}", inode_updates);
204        for update in inode_updates.iter() {
205            self.update_inode(&branch, internal, update)?;
206        }
207        debug!("committing branch");
208        if !is_pending {
209            debug!("not pending, adding to changes");
210            branch.apply_counter += 1;
211            self.output_changes_file(&branch, working_copy)?;
212        }
213        trace!("done apply_local_patch");
214        Ok(internal)
215    }
216
217    fn register_patch(&mut self, internal: PatchId, hash: &Hash, patch: &Patch) -> Result<()> {
218        self.put_external(internal, hash.as_ref())?;
219        self.put_internal(hash.as_ref(), internal)?;
220        for hunk in patch.changes() {
221            let inode = match *hunk {
222                Change::NewNodes { ref inode, .. } => inode,
223                Change::NewEdges { ref inode, .. } => inode,
224            };
225            let inode = self.internal_key(inode, internal);
226            self.put_touched_file(inode, internal)?;
227        }
228        Ok(())
229    }
230
231    /// Update the inodes/revinodes, tree/revtrees databases with the
232    /// patch we just applied. This is because files don't really get
233    /// moved or deleted before we apply the patch, they are just
234    /// "marked as moved/deleted". This function does the actual
235    /// update.
236    fn update_inode(
237        &mut self,
238        branch: &Branch,
239        internal: PatchId,
240        update: &InodeUpdate,
241    ) -> Result<()> {
242        match *update {
243            InodeUpdate::Add {
244                ref line,
245                ref meta,
246                inode,
247            } => {
248                let key = FileHeader {
249                    metadata: *meta,
250                    status: FileStatus::Ok,
251                    key: Key {
252                        patch: internal.clone(),
253                        line: line.clone(),
254                    },
255                };
256                // If this file addition was actually recorded.
257                if self.get_nodes(&branch, key.key, None).is_some() {
258                    debug!("it's in here!: {:?} {:?}", key, inode);
259                    self.replace_inodes(inode, key)?;
260                    self.replace_revinodes(key.key, inode)?;
261                }
262            }
263            InodeUpdate::Deleted { inode } => {
264                // If this change was actually applied.
265                debug!("deleted: {:?}", inode);
266                let header = self.get_inodes(inode).unwrap().clone();
267                debug!("deleted header: {:?}", header);
268                let edge = Edge::zero(
269                    EdgeFlags::PARENT_EDGE | EdgeFlags::FOLDER_EDGE | EdgeFlags::DELETED_EDGE,
270                );
271                if self.iter_nodes(&branch, Some((header.key, Some(&edge))))
272                    .take_while(|&(k, v)| k == header.key && edge.flag == v.flag)
273                    .any(|(_, v)| v.introduced_by == internal)
274                {
275                    self.del_inodes(inode, Some(header))?;
276                    self.del_revinodes(header.key, Some(inode))?;
277
278                    // We might have killed the parent in the same
279                    // update.
280                    if let Some(parent) = self.get_revtree(inode).map(|x| x.to_owned()) {
281                        let parent = parent.as_file_id();
282                        self.del_tree(&parent, None)?;
283                        self.del_revtree(inode, None)?;
284                    }
285                }
286            }
287            InodeUpdate::Moved { inode } => {
288                // If this change was actually applied.
289                debug!("moved: {:?}", inode);
290                let mut header = self.get_inodes(inode).unwrap().clone();
291                debug!("moved header: {:?}", header);
292                let edge = Edge::zero(EdgeFlags::PARENT_EDGE | EdgeFlags::FOLDER_EDGE);
293                if self.iter_nodes(&branch, Some((header.key, Some(&edge))))
294                    .take_while(|&(k, v)| k == header.key && edge.flag == v.flag)
295                    .any(|(_, v)| v.introduced_by == internal)
296                {
297                    header.status = FileStatus::Ok;
298                    self.replace_inodes(inode, header)?;
299                    self.replace_revinodes(header.key, inode)?;
300                }
301            }
302        }
303        Ok(())
304    }
305}