32 removed: HashSet<HgPathBuf>, |
33 removed: HashSet<HgPathBuf>, |
33 merged: HashSet<HgPathBuf>, |
34 merged: HashSet<HgPathBuf>, |
34 salvaged: HashSet<HgPathBuf>, |
35 salvaged: HashSet<HgPathBuf>, |
35 copied_from_p1: PathCopies, |
36 copied_from_p1: PathCopies, |
36 copied_from_p2: PathCopies, |
37 copied_from_p2: PathCopies, |
|
38 } |
|
39 |
|
40 /// Represent active changes that affect the copy tracing. |
|
41 enum Action<'a> { |
|
42 /// The parent ? children edge is removing a file |
|
43 /// |
|
44 /// (actually, this could be the edge from the other parent, but it does |
|
45 /// not matters) |
|
46 Removed(&'a HgPath), |
|
47 /// The parent ? children edge introduce copy information between (dest, |
|
48 /// source) |
|
49 Copied(&'a HgPath, &'a HgPath), |
37 } |
50 } |
38 |
51 |
39 impl ChangedFiles { |
52 impl ChangedFiles { |
40 pub fn new( |
53 pub fn new( |
41 removed: HashSet<HgPathBuf>, |
54 removed: HashSet<HgPathBuf>, |
59 merged: HashSet::new(), |
72 merged: HashSet::new(), |
60 salvaged: HashSet::new(), |
73 salvaged: HashSet::new(), |
61 copied_from_p1: PathCopies::new(), |
74 copied_from_p1: PathCopies::new(), |
62 copied_from_p2: PathCopies::new(), |
75 copied_from_p2: PathCopies::new(), |
63 } |
76 } |
|
77 } |
|
78 |
|
79 /// Return an iterator over all the `Action` in this instance. |
|
80 fn iter_actions(&self, parent: usize) -> impl Iterator<Item = Action> { |
|
81 let copies_iter = match parent { |
|
82 1 => self.copied_from_p1.iter(), |
|
83 2 => self.copied_from_p2.iter(), |
|
84 _ => unreachable!(), |
|
85 }; |
|
86 let remove_iter = self.removed.iter(); |
|
87 let copies_iter = copies_iter.map(|(x, y)| Action::Copied(x, y)); |
|
88 let remove_iter = remove_iter.map(|x| Action::Removed(x)); |
|
89 copies_iter.chain(remove_iter) |
64 } |
90 } |
65 } |
91 } |
66 |
92 |
67 /// A struct responsible for answering "is X ancestors of Y" quickly |
93 /// A struct responsible for answering "is X ancestors of Y" quickly |
68 /// |
94 /// |
140 // We will chain the copies information accumulated for `rev` with |
166 // We will chain the copies information accumulated for `rev` with |
141 // the individual copies information for each of its children. |
167 // the individual copies information for each of its children. |
142 // Creating a new PathCopies for each `rev` ? `children` vertex. |
168 // Creating a new PathCopies for each `rev` ? `children` vertex. |
143 let (p1, p2, changes) = rev_info(*child); |
169 let (p1, p2, changes) = rev_info(*child); |
144 |
170 |
145 let (parent, child_copies) = if rev == p1 { |
171 let parent = if rev == p1 { |
146 (1, &changes.copied_from_p1) |
172 1 |
147 } else { |
173 } else { |
148 assert_eq!(rev, p2); |
174 assert_eq!(rev, p2); |
149 (2, &changes.copied_from_p2) |
175 2 |
150 }; |
176 }; |
151 let mut new_copies = copies.clone(); |
177 let mut new_copies = copies.clone(); |
152 |
178 |
153 for (dest, source) in child_copies { |
179 for action in changes.iter_actions(parent) { |
154 let entry; |
180 match action { |
155 if let Some(v) = copies.get(source) { |
181 Action::Copied(dest, source) => { |
156 entry = match &v.path { |
182 let entry; |
157 Some(path) => Some((*(path)).to_owned()), |
183 if let Some(v) = copies.get(source) { |
158 None => Some(source.to_owned()), |
184 entry = match &v.path { |
|
185 Some(path) => Some((*(path)).to_owned()), |
|
186 None => Some(source.to_owned()), |
|
187 } |
|
188 } else { |
|
189 entry = Some(source.to_owned()); |
|
190 } |
|
191 // Each new entry is introduced by the children, we |
|
192 // record this information as we will need it to take |
|
193 // the right decision when merging conflicting copy |
|
194 // information. See merge_copies_dict for details. |
|
195 let ttpc = TimeStampedPathCopy { |
|
196 rev: *child, |
|
197 path: entry, |
|
198 }; |
|
199 new_copies.insert(dest.to_owned(), ttpc); |
159 } |
200 } |
160 } else { |
201 Action::Removed(f) => { |
161 entry = Some(source.to_owned()); |
202 // We must drop copy information for removed file. |
162 } |
203 // |
163 // Each new entry is introduced by the children, we record this |
204 // We need to explicitly record them as dropped to |
164 // information as we will need it to take the right decision |
205 // propagate this information when merging two |
165 // when merging conflicting copy information. See |
206 // TimeStampedPathCopies object. |
166 // merge_copies_dict for details. |
207 if new_copies.contains_key(f.as_ref()) { |
167 let ttpc = TimeStampedPathCopy { |
208 let ttpc = TimeStampedPathCopy { |
168 rev: *child, |
209 rev: *child, |
169 path: entry, |
210 path: None, |
170 }; |
211 }; |
171 new_copies.insert(dest.to_owned(), ttpc); |
212 new_copies.insert(f.to_owned(), ttpc); |
172 } |
213 } |
173 |
214 } |
174 // We must drop copy information for removed file. |
|
175 // |
|
176 // We need to explicitly record them as dropped to propagate this |
|
177 // information when merging two TimeStampedPathCopies object. |
|
178 for f in changes.removed.iter() { |
|
179 if new_copies.contains_key(f.as_ref()) { |
|
180 let ttpc = TimeStampedPathCopy { |
|
181 rev: *child, |
|
182 path: None, |
|
183 }; |
|
184 new_copies.insert(f.to_owned(), ttpc); |
|
185 } |
215 } |
186 } |
216 } |
187 |
217 |
188 // Merge has two parents needs to combines their copy information. |
218 // Merge has two parents needs to combines their copy information. |
189 // |
219 // |