##// END OF EJS Templates
copies-rust: pre-introduce a PathToken type and use it where applicable...
marmoute -
r46745:818502d2 default
parent child Browse files
Show More
@@ -1,663 +1,665 b''
1 use crate::utils::hg_path::HgPath;
1 use crate::utils::hg_path::HgPath;
2 use crate::utils::hg_path::HgPathBuf;
2 use crate::utils::hg_path::HgPathBuf;
3 use crate::Revision;
3 use crate::Revision;
4
4
5 use im_rc::ordmap::DiffItem;
5 use im_rc::ordmap::DiffItem;
6 use im_rc::ordmap::OrdMap;
6 use im_rc::ordmap::OrdMap;
7
7
8 use std::cmp::Ordering;
8 use std::cmp::Ordering;
9 use std::collections::HashMap;
9 use std::collections::HashMap;
10 use std::convert::TryInto;
10 use std::convert::TryInto;
11
11
12 pub type PathCopies = HashMap<HgPathBuf, HgPathBuf>;
12 pub type PathCopies = HashMap<HgPathBuf, HgPathBuf>;
13
13
14 type PathToken = HgPathBuf;
15
14 #[derive(Clone, Debug, PartialEq)]
16 #[derive(Clone, Debug, PartialEq)]
15 struct TimeStampedPathCopy {
17 struct TimeStampedPathCopy {
16 /// revision at which the copy information was added
18 /// revision at which the copy information was added
17 rev: Revision,
19 rev: Revision,
18 /// the copy source, (Set to None in case of deletion of the associated
20 /// the copy source, (Set to None in case of deletion of the associated
19 /// key)
21 /// key)
20 path: Option<HgPathBuf>,
22 path: Option<PathToken>,
21 }
23 }
22
24
23 /// maps CopyDestination to Copy Source (+ a "timestamp" for the operation)
25 /// maps CopyDestination to Copy Source (+ a "timestamp" for the operation)
24 type TimeStampedPathCopies = OrdMap<HgPathBuf, TimeStampedPathCopy>;
26 type TimeStampedPathCopies = OrdMap<PathToken, TimeStampedPathCopy>;
25
27
26 /// hold parent 1, parent 2 and relevant files actions.
28 /// hold parent 1, parent 2 and relevant files actions.
27 pub type RevInfo<'a> = (Revision, Revision, ChangedFiles<'a>);
29 pub type RevInfo<'a> = (Revision, Revision, ChangedFiles<'a>);
28
30
29 /// represent the files affected by a changesets
31 /// represent the files affected by a changesets
30 ///
32 ///
31 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
33 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
32 /// all the data categories tracked by it.
34 /// all the data categories tracked by it.
33 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
35 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
34 /// all the data categories tracked by it.
36 /// all the data categories tracked by it.
35 pub struct ChangedFiles<'a> {
37 pub struct ChangedFiles<'a> {
36 nb_items: u32,
38 nb_items: u32,
37 index: &'a [u8],
39 index: &'a [u8],
38 data: &'a [u8],
40 data: &'a [u8],
39 }
41 }
40
42
41 /// Represent active changes that affect the copy tracing.
43 /// Represent active changes that affect the copy tracing.
42 enum Action<'a> {
44 enum Action<'a> {
43 /// The parent ? children edge is removing a file
45 /// The parent ? children edge is removing a file
44 ///
46 ///
45 /// (actually, this could be the edge from the other parent, but it does
47 /// (actually, this could be the edge from the other parent, but it does
46 /// not matters)
48 /// not matters)
47 Removed(&'a HgPath),
49 Removed(&'a HgPath),
48 /// The parent ? children edge introduce copy information between (dest,
50 /// The parent ? children edge introduce copy information between (dest,
49 /// source)
51 /// source)
50 Copied(&'a HgPath, &'a HgPath),
52 Copied(&'a HgPath, &'a HgPath),
51 }
53 }
52
54
53 /// This express the possible "special" case we can get in a merge
55 /// This express the possible "special" case we can get in a merge
54 ///
56 ///
55 /// See mercurial/metadata.py for details on these values.
57 /// See mercurial/metadata.py for details on these values.
56 #[derive(PartialEq)]
58 #[derive(PartialEq)]
57 enum MergeCase {
59 enum MergeCase {
58 /// Merged: file had history on both side that needed to be merged
60 /// Merged: file had history on both side that needed to be merged
59 Merged,
61 Merged,
60 /// Salvaged: file was candidate for deletion, but survived the merge
62 /// Salvaged: file was candidate for deletion, but survived the merge
61 Salvaged,
63 Salvaged,
62 /// Normal: Not one of the two cases above
64 /// Normal: Not one of the two cases above
63 Normal,
65 Normal,
64 }
66 }
65
67
66 type FileChange<'a> = (u8, &'a HgPath, &'a HgPath);
68 type FileChange<'a> = (u8, &'a HgPath, &'a HgPath);
67
69
68 const EMPTY: &[u8] = b"";
70 const EMPTY: &[u8] = b"";
69 const COPY_MASK: u8 = 3;
71 const COPY_MASK: u8 = 3;
70 const P1_COPY: u8 = 2;
72 const P1_COPY: u8 = 2;
71 const P2_COPY: u8 = 3;
73 const P2_COPY: u8 = 3;
72 const ACTION_MASK: u8 = 28;
74 const ACTION_MASK: u8 = 28;
73 const REMOVED: u8 = 12;
75 const REMOVED: u8 = 12;
74 const MERGED: u8 = 8;
76 const MERGED: u8 = 8;
75 const SALVAGED: u8 = 16;
77 const SALVAGED: u8 = 16;
76
78
77 impl<'a> ChangedFiles<'a> {
79 impl<'a> ChangedFiles<'a> {
78 const INDEX_START: usize = 4;
80 const INDEX_START: usize = 4;
79 const ENTRY_SIZE: u32 = 9;
81 const ENTRY_SIZE: u32 = 9;
80 const FILENAME_START: u32 = 1;
82 const FILENAME_START: u32 = 1;
81 const COPY_SOURCE_START: u32 = 5;
83 const COPY_SOURCE_START: u32 = 5;
82
84
83 pub fn new(data: &'a [u8]) -> Self {
85 pub fn new(data: &'a [u8]) -> Self {
84 assert!(
86 assert!(
85 data.len() >= 4,
87 data.len() >= 4,
86 "data size ({}) is too small to contain the header (4)",
88 "data size ({}) is too small to contain the header (4)",
87 data.len()
89 data.len()
88 );
90 );
89 let nb_items_raw: [u8; 4] = (&data[0..=3])
91 let nb_items_raw: [u8; 4] = (&data[0..=3])
90 .try_into()
92 .try_into()
91 .expect("failed to turn 4 bytes into 4 bytes");
93 .expect("failed to turn 4 bytes into 4 bytes");
92 let nb_items = u32::from_be_bytes(nb_items_raw);
94 let nb_items = u32::from_be_bytes(nb_items_raw);
93
95
94 let index_size = (nb_items * Self::ENTRY_SIZE) as usize;
96 let index_size = (nb_items * Self::ENTRY_SIZE) as usize;
95 let index_end = Self::INDEX_START + index_size;
97 let index_end = Self::INDEX_START + index_size;
96
98
97 assert!(
99 assert!(
98 data.len() >= index_end,
100 data.len() >= index_end,
99 "data size ({}) is too small to fit the index_data ({})",
101 "data size ({}) is too small to fit the index_data ({})",
100 data.len(),
102 data.len(),
101 index_end
103 index_end
102 );
104 );
103
105
104 let ret = ChangedFiles {
106 let ret = ChangedFiles {
105 nb_items,
107 nb_items,
106 index: &data[Self::INDEX_START..index_end],
108 index: &data[Self::INDEX_START..index_end],
107 data: &data[index_end..],
109 data: &data[index_end..],
108 };
110 };
109 let max_data = ret.filename_end(nb_items - 1) as usize;
111 let max_data = ret.filename_end(nb_items - 1) as usize;
110 assert!(
112 assert!(
111 ret.data.len() >= max_data,
113 ret.data.len() >= max_data,
112 "data size ({}) is too small to fit all data ({})",
114 "data size ({}) is too small to fit all data ({})",
113 data.len(),
115 data.len(),
114 index_end + max_data
116 index_end + max_data
115 );
117 );
116 ret
118 ret
117 }
119 }
118
120
119 pub fn new_empty() -> Self {
121 pub fn new_empty() -> Self {
120 ChangedFiles {
122 ChangedFiles {
121 nb_items: 0,
123 nb_items: 0,
122 index: EMPTY,
124 index: EMPTY,
123 data: EMPTY,
125 data: EMPTY,
124 }
126 }
125 }
127 }
126
128
127 /// internal function to return an individual entry at a given index
129 /// internal function to return an individual entry at a given index
128 fn entry(&'a self, idx: u32) -> FileChange<'a> {
130 fn entry(&'a self, idx: u32) -> FileChange<'a> {
129 if idx >= self.nb_items {
131 if idx >= self.nb_items {
130 panic!(
132 panic!(
131 "index for entry is higher that the number of file {} >= {}",
133 "index for entry is higher that the number of file {} >= {}",
132 idx, self.nb_items
134 idx, self.nb_items
133 )
135 )
134 }
136 }
135 let flags = self.flags(idx);
137 let flags = self.flags(idx);
136 let filename = self.filename(idx);
138 let filename = self.filename(idx);
137 let copy_idx = self.copy_idx(idx);
139 let copy_idx = self.copy_idx(idx);
138 let copy_source = self.filename(copy_idx);
140 let copy_source = self.filename(copy_idx);
139 (flags, filename, copy_source)
141 (flags, filename, copy_source)
140 }
142 }
141
143
142 /// internal function to return the filename of the entry at a given index
144 /// internal function to return the filename of the entry at a given index
143 fn filename(&self, idx: u32) -> &HgPath {
145 fn filename(&self, idx: u32) -> &HgPath {
144 let filename_start;
146 let filename_start;
145 if idx == 0 {
147 if idx == 0 {
146 filename_start = 0;
148 filename_start = 0;
147 } else {
149 } else {
148 filename_start = self.filename_end(idx - 1)
150 filename_start = self.filename_end(idx - 1)
149 }
151 }
150 let filename_end = self.filename_end(idx);
152 let filename_end = self.filename_end(idx);
151 let filename_start = filename_start as usize;
153 let filename_start = filename_start as usize;
152 let filename_end = filename_end as usize;
154 let filename_end = filename_end as usize;
153 HgPath::new(&self.data[filename_start..filename_end])
155 HgPath::new(&self.data[filename_start..filename_end])
154 }
156 }
155
157
156 /// internal function to return the flag field of the entry at a given
158 /// internal function to return the flag field of the entry at a given
157 /// index
159 /// index
158 fn flags(&self, idx: u32) -> u8 {
160 fn flags(&self, idx: u32) -> u8 {
159 let idx = idx as usize;
161 let idx = idx as usize;
160 self.index[idx * (Self::ENTRY_SIZE as usize)]
162 self.index[idx * (Self::ENTRY_SIZE as usize)]
161 }
163 }
162
164
163 /// internal function to return the end of a filename part at a given index
165 /// internal function to return the end of a filename part at a given index
164 fn filename_end(&self, idx: u32) -> u32 {
166 fn filename_end(&self, idx: u32) -> u32 {
165 let start = (idx * Self::ENTRY_SIZE) + Self::FILENAME_START;
167 let start = (idx * Self::ENTRY_SIZE) + Self::FILENAME_START;
166 let end = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
168 let end = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
167 let start = start as usize;
169 let start = start as usize;
168 let end = end as usize;
170 let end = end as usize;
169 let raw = (&self.index[start..end])
171 let raw = (&self.index[start..end])
170 .try_into()
172 .try_into()
171 .expect("failed to turn 4 bytes into 4 bytes");
173 .expect("failed to turn 4 bytes into 4 bytes");
172 u32::from_be_bytes(raw)
174 u32::from_be_bytes(raw)
173 }
175 }
174
176
175 /// internal function to return index of the copy source of the entry at a
177 /// internal function to return index of the copy source of the entry at a
176 /// given index
178 /// given index
177 fn copy_idx(&self, idx: u32) -> u32 {
179 fn copy_idx(&self, idx: u32) -> u32 {
178 let start = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
180 let start = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
179 let end = (idx + 1) * Self::ENTRY_SIZE;
181 let end = (idx + 1) * Self::ENTRY_SIZE;
180 let start = start as usize;
182 let start = start as usize;
181 let end = end as usize;
183 let end = end as usize;
182 let raw = (&self.index[start..end])
184 let raw = (&self.index[start..end])
183 .try_into()
185 .try_into()
184 .expect("failed to turn 4 bytes into 4 bytes");
186 .expect("failed to turn 4 bytes into 4 bytes");
185 u32::from_be_bytes(raw)
187 u32::from_be_bytes(raw)
186 }
188 }
187
189
188 /// Return an iterator over all the `Action` in this instance.
190 /// Return an iterator over all the `Action` in this instance.
189 fn iter_actions(&self, parent: Parent) -> ActionsIterator {
191 fn iter_actions(&self, parent: Parent) -> ActionsIterator {
190 ActionsIterator {
192 ActionsIterator {
191 changes: &self,
193 changes: &self,
192 parent: parent,
194 parent: parent,
193 current: 0,
195 current: 0,
194 }
196 }
195 }
197 }
196
198
197 /// return the MergeCase value associated with a filename
199 /// return the MergeCase value associated with a filename
198 fn get_merge_case(&self, path: &HgPath) -> MergeCase {
200 fn get_merge_case(&self, path: &HgPath) -> MergeCase {
199 if self.nb_items == 0 {
201 if self.nb_items == 0 {
200 return MergeCase::Normal;
202 return MergeCase::Normal;
201 }
203 }
202 let mut low_part = 0;
204 let mut low_part = 0;
203 let mut high_part = self.nb_items;
205 let mut high_part = self.nb_items;
204
206
205 while low_part < high_part {
207 while low_part < high_part {
206 let cursor = (low_part + high_part - 1) / 2;
208 let cursor = (low_part + high_part - 1) / 2;
207 let (flags, filename, _source) = self.entry(cursor);
209 let (flags, filename, _source) = self.entry(cursor);
208 match path.cmp(filename) {
210 match path.cmp(filename) {
209 Ordering::Less => low_part = cursor + 1,
211 Ordering::Less => low_part = cursor + 1,
210 Ordering::Greater => high_part = cursor,
212 Ordering::Greater => high_part = cursor,
211 Ordering::Equal => {
213 Ordering::Equal => {
212 return match flags & ACTION_MASK {
214 return match flags & ACTION_MASK {
213 MERGED => MergeCase::Merged,
215 MERGED => MergeCase::Merged,
214 SALVAGED => MergeCase::Salvaged,
216 SALVAGED => MergeCase::Salvaged,
215 _ => MergeCase::Normal,
217 _ => MergeCase::Normal,
216 };
218 };
217 }
219 }
218 }
220 }
219 }
221 }
220 MergeCase::Normal
222 MergeCase::Normal
221 }
223 }
222 }
224 }
223
225
224 /// A struct responsible for answering "is X ancestors of Y" quickly
226 /// A struct responsible for answering "is X ancestors of Y" quickly
225 ///
227 ///
226 /// The structure will delegate ancestors call to a callback, and cache the
228 /// The structure will delegate ancestors call to a callback, and cache the
227 /// result.
229 /// result.
228 #[derive(Debug)]
230 #[derive(Debug)]
229 struct AncestorOracle<'a, A: Fn(Revision, Revision) -> bool> {
231 struct AncestorOracle<'a, A: Fn(Revision, Revision) -> bool> {
230 inner: &'a A,
232 inner: &'a A,
231 pairs: HashMap<(Revision, Revision), bool>,
233 pairs: HashMap<(Revision, Revision), bool>,
232 }
234 }
233
235
234 impl<'a, A: Fn(Revision, Revision) -> bool> AncestorOracle<'a, A> {
236 impl<'a, A: Fn(Revision, Revision) -> bool> AncestorOracle<'a, A> {
235 fn new(func: &'a A) -> Self {
237 fn new(func: &'a A) -> Self {
236 Self {
238 Self {
237 inner: func,
239 inner: func,
238 pairs: HashMap::default(),
240 pairs: HashMap::default(),
239 }
241 }
240 }
242 }
241
243
242 /// returns `true` if `anc` is an ancestors of `desc`, `false` otherwise
244 /// returns `true` if `anc` is an ancestors of `desc`, `false` otherwise
243 fn is_ancestor(&mut self, anc: Revision, desc: Revision) -> bool {
245 fn is_ancestor(&mut self, anc: Revision, desc: Revision) -> bool {
244 if anc > desc {
246 if anc > desc {
245 false
247 false
246 } else if anc == desc {
248 } else if anc == desc {
247 true
249 true
248 } else {
250 } else {
249 if let Some(b) = self.pairs.get(&(anc, desc)) {
251 if let Some(b) = self.pairs.get(&(anc, desc)) {
250 *b
252 *b
251 } else {
253 } else {
252 let b = (self.inner)(anc, desc);
254 let b = (self.inner)(anc, desc);
253 self.pairs.insert((anc, desc), b);
255 self.pairs.insert((anc, desc), b);
254 b
256 b
255 }
257 }
256 }
258 }
257 }
259 }
258 }
260 }
259
261
260 struct ActionsIterator<'a> {
262 struct ActionsIterator<'a> {
261 changes: &'a ChangedFiles<'a>,
263 changes: &'a ChangedFiles<'a>,
262 parent: Parent,
264 parent: Parent,
263 current: u32,
265 current: u32,
264 }
266 }
265
267
266 impl<'a> Iterator for ActionsIterator<'a> {
268 impl<'a> Iterator for ActionsIterator<'a> {
267 type Item = Action<'a>;
269 type Item = Action<'a>;
268
270
269 fn next(&mut self) -> Option<Action<'a>> {
271 fn next(&mut self) -> Option<Action<'a>> {
270 let copy_flag = match self.parent {
272 let copy_flag = match self.parent {
271 Parent::FirstParent => P1_COPY,
273 Parent::FirstParent => P1_COPY,
272 Parent::SecondParent => P2_COPY,
274 Parent::SecondParent => P2_COPY,
273 };
275 };
274 while self.current < self.changes.nb_items {
276 while self.current < self.changes.nb_items {
275 let (flags, file, source) = self.changes.entry(self.current);
277 let (flags, file, source) = self.changes.entry(self.current);
276 self.current += 1;
278 self.current += 1;
277 if (flags & ACTION_MASK) == REMOVED {
279 if (flags & ACTION_MASK) == REMOVED {
278 return Some(Action::Removed(file));
280 return Some(Action::Removed(file));
279 }
281 }
280 let copy = flags & COPY_MASK;
282 let copy = flags & COPY_MASK;
281 if copy == copy_flag {
283 if copy == copy_flag {
282 return Some(Action::Copied(file, source));
284 return Some(Action::Copied(file, source));
283 }
285 }
284 }
286 }
285 return None;
287 return None;
286 }
288 }
287 }
289 }
288
290
289 /// A small struct whose purpose is to ensure lifetime of bytes referenced in
291 /// A small struct whose purpose is to ensure lifetime of bytes referenced in
290 /// ChangedFiles
292 /// ChangedFiles
291 ///
293 ///
292 /// It is passed to the RevInfoMaker callback who can assign any necessary
294 /// It is passed to the RevInfoMaker callback who can assign any necessary
293 /// content to the `data` attribute. The copy tracing code is responsible for
295 /// content to the `data` attribute. The copy tracing code is responsible for
294 /// keeping the DataHolder alive at least as long as the ChangedFiles object.
296 /// keeping the DataHolder alive at least as long as the ChangedFiles object.
295 pub struct DataHolder<D> {
297 pub struct DataHolder<D> {
296 /// RevInfoMaker callback should assign data referenced by the
298 /// RevInfoMaker callback should assign data referenced by the
297 /// ChangedFiles struct it return to this attribute. The DataHolder
299 /// ChangedFiles struct it return to this attribute. The DataHolder
298 /// lifetime will be at least as long as the ChangedFiles one.
300 /// lifetime will be at least as long as the ChangedFiles one.
299 pub data: Option<D>,
301 pub data: Option<D>,
300 }
302 }
301
303
302 pub type RevInfoMaker<'a, D> =
304 pub type RevInfoMaker<'a, D> =
303 Box<dyn for<'r> Fn(Revision, &'r mut DataHolder<D>) -> RevInfo<'r> + 'a>;
305 Box<dyn for<'r> Fn(Revision, &'r mut DataHolder<D>) -> RevInfo<'r> + 'a>;
304
306
305 /// enum used to carry information about the parent β†’ child currently processed
307 /// enum used to carry information about the parent β†’ child currently processed
306 #[derive(Copy, Clone, Debug)]
308 #[derive(Copy, Clone, Debug)]
307 enum Parent {
309 enum Parent {
308 /// The `p1(x) β†’ x` edge
310 /// The `p1(x) β†’ x` edge
309 FirstParent,
311 FirstParent,
310 /// The `p2(x) β†’ x` edge
312 /// The `p2(x) β†’ x` edge
311 SecondParent,
313 SecondParent,
312 }
314 }
313
315
314 /// Same as mercurial.copies._combine_changeset_copies, but in Rust.
316 /// Same as mercurial.copies._combine_changeset_copies, but in Rust.
315 ///
317 ///
316 /// Arguments are:
318 /// Arguments are:
317 ///
319 ///
318 /// revs: all revisions to be considered
320 /// revs: all revisions to be considered
319 /// children: a {parent ? [childrens]} mapping
321 /// children: a {parent ? [childrens]} mapping
320 /// target_rev: the final revision we are combining copies to
322 /// target_rev: the final revision we are combining copies to
321 /// rev_info(rev): callback to get revision information:
323 /// rev_info(rev): callback to get revision information:
322 /// * first parent
324 /// * first parent
323 /// * second parent
325 /// * second parent
324 /// * ChangedFiles
326 /// * ChangedFiles
325 /// isancestors(low_rev, high_rev): callback to check if a revision is an
327 /// isancestors(low_rev, high_rev): callback to check if a revision is an
326 /// ancestor of another
328 /// ancestor of another
327 pub fn combine_changeset_copies<A: Fn(Revision, Revision) -> bool, D>(
329 pub fn combine_changeset_copies<A: Fn(Revision, Revision) -> bool, D>(
328 revs: Vec<Revision>,
330 revs: Vec<Revision>,
329 children: HashMap<Revision, Vec<Revision>>,
331 children: HashMap<Revision, Vec<Revision>>,
330 target_rev: Revision,
332 target_rev: Revision,
331 rev_info: RevInfoMaker<D>,
333 rev_info: RevInfoMaker<D>,
332 is_ancestor: &A,
334 is_ancestor: &A,
333 ) -> PathCopies {
335 ) -> PathCopies {
334 let mut all_copies = HashMap::new();
336 let mut all_copies = HashMap::new();
335 let mut oracle = AncestorOracle::new(is_ancestor);
337 let mut oracle = AncestorOracle::new(is_ancestor);
336
338
337 for rev in revs {
339 for rev in revs {
338 // Retrieve data computed in a previous iteration
340 // Retrieve data computed in a previous iteration
339 let copies = all_copies.remove(&rev);
341 let copies = all_copies.remove(&rev);
340 let copies = match copies {
342 let copies = match copies {
341 Some(c) => c,
343 Some(c) => c,
342 None => TimeStampedPathCopies::default(), // root of the walked set
344 None => TimeStampedPathCopies::default(), // root of the walked set
343 };
345 };
344
346
345 let current_children = match children.get(&rev) {
347 let current_children = match children.get(&rev) {
346 Some(c) => c,
348 Some(c) => c,
347 None => panic!("inconsistent `revs` and `children`"),
349 None => panic!("inconsistent `revs` and `children`"),
348 };
350 };
349
351
350 for child in current_children {
352 for child in current_children {
351 // We will chain the copies information accumulated for `rev` with
353 // We will chain the copies information accumulated for `rev` with
352 // the individual copies information for each of its children.
354 // the individual copies information for each of its children.
353 // Creating a new PathCopies for each `rev` β†’ `children` vertex.
355 // Creating a new PathCopies for each `rev` β†’ `children` vertex.
354 let mut d: DataHolder<D> = DataHolder { data: None };
356 let mut d: DataHolder<D> = DataHolder { data: None };
355 let (p1, p2, changes) = rev_info(*child, &mut d);
357 let (p1, p2, changes) = rev_info(*child, &mut d);
356
358
357 let parent = if rev == p1 {
359 let parent = if rev == p1 {
358 Parent::FirstParent
360 Parent::FirstParent
359 } else {
361 } else {
360 assert_eq!(rev, p2);
362 assert_eq!(rev, p2);
361 Parent::SecondParent
363 Parent::SecondParent
362 };
364 };
363 let new_copies =
365 let new_copies =
364 add_from_changes(&copies, &changes, parent, *child);
366 add_from_changes(&copies, &changes, parent, *child);
365
367
366 // Merge has two parents needs to combines their copy information.
368 // Merge has two parents needs to combines their copy information.
367 //
369 //
368 // If the vertex from the other parent was already processed, we
370 // If the vertex from the other parent was already processed, we
369 // will have a value for the child ready to be used. We need to
371 // will have a value for the child ready to be used. We need to
370 // grab it and combine it with the one we already
372 // grab it and combine it with the one we already
371 // computed. If not we can simply store the newly
373 // computed. If not we can simply store the newly
372 // computed data. The processing happening at
374 // computed data. The processing happening at
373 // the time of the second parent will take care of combining the
375 // the time of the second parent will take care of combining the
374 // two TimeStampedPathCopies instance.
376 // two TimeStampedPathCopies instance.
375 match all_copies.remove(child) {
377 match all_copies.remove(child) {
376 None => {
378 None => {
377 all_copies.insert(child, new_copies);
379 all_copies.insert(child, new_copies);
378 }
380 }
379 Some(other_copies) => {
381 Some(other_copies) => {
380 let (minor, major) = match parent {
382 let (minor, major) = match parent {
381 Parent::FirstParent => (other_copies, new_copies),
383 Parent::FirstParent => (other_copies, new_copies),
382 Parent::SecondParent => (new_copies, other_copies),
384 Parent::SecondParent => (new_copies, other_copies),
383 };
385 };
384 let merged_copies =
386 let merged_copies =
385 merge_copies_dict(minor, major, &changes, &mut oracle);
387 merge_copies_dict(minor, major, &changes, &mut oracle);
386 all_copies.insert(child, merged_copies);
388 all_copies.insert(child, merged_copies);
387 }
389 }
388 };
390 };
389 }
391 }
390 }
392 }
391
393
392 // Drop internal information (like the timestamp) and return the final
394 // Drop internal information (like the timestamp) and return the final
393 // mapping.
395 // mapping.
394 let tt_result = all_copies
396 let tt_result = all_copies
395 .remove(&target_rev)
397 .remove(&target_rev)
396 .expect("target revision was not processed");
398 .expect("target revision was not processed");
397 let mut result = PathCopies::default();
399 let mut result = PathCopies::default();
398 for (dest, tt_source) in tt_result {
400 for (dest, tt_source) in tt_result {
399 if let Some(path) = tt_source.path {
401 if let Some(path) = tt_source.path {
400 result.insert(dest, path);
402 result.insert(dest, path);
401 }
403 }
402 }
404 }
403 result
405 result
404 }
406 }
405
407
406 /// Combine ChangedFiles with some existing PathCopies information and return
408 /// Combine ChangedFiles with some existing PathCopies information and return
407 /// the result
409 /// the result
408 fn add_from_changes(
410 fn add_from_changes(
409 base_copies: &TimeStampedPathCopies,
411 base_copies: &TimeStampedPathCopies,
410 changes: &ChangedFiles,
412 changes: &ChangedFiles,
411 parent: Parent,
413 parent: Parent,
412 current_rev: Revision,
414 current_rev: Revision,
413 ) -> TimeStampedPathCopies {
415 ) -> TimeStampedPathCopies {
414 let mut copies = base_copies.clone();
416 let mut copies = base_copies.clone();
415 for action in changes.iter_actions(parent) {
417 for action in changes.iter_actions(parent) {
416 match action {
418 match action {
417 Action::Copied(dest, source) => {
419 Action::Copied(dest, source) => {
418 let entry;
420 let entry;
419 if let Some(v) = base_copies.get(source) {
421 if let Some(v) = base_copies.get(source) {
420 entry = match &v.path {
422 entry = match &v.path {
421 Some(path) => Some((*(path)).to_owned()),
423 Some(path) => Some((*(path)).to_owned()),
422 None => Some(source.to_owned()),
424 None => Some(source.to_owned()),
423 }
425 }
424 } else {
426 } else {
425 entry = Some(source.to_owned());
427 entry = Some(source.to_owned());
426 }
428 }
427 // Each new entry is introduced by the children, we
429 // Each new entry is introduced by the children, we
428 // record this information as we will need it to take
430 // record this information as we will need it to take
429 // the right decision when merging conflicting copy
431 // the right decision when merging conflicting copy
430 // information. See merge_copies_dict for details.
432 // information. See merge_copies_dict for details.
431 let ttpc = TimeStampedPathCopy {
433 let ttpc = TimeStampedPathCopy {
432 rev: current_rev,
434 rev: current_rev,
433 path: entry,
435 path: entry,
434 };
436 };
435 copies.insert(dest.to_owned(), ttpc);
437 copies.insert(dest.to_owned(), ttpc);
436 }
438 }
437 Action::Removed(f) => {
439 Action::Removed(f) => {
438 // We must drop copy information for removed file.
440 // We must drop copy information for removed file.
439 //
441 //
440 // We need to explicitly record them as dropped to
442 // We need to explicitly record them as dropped to
441 // propagate this information when merging two
443 // propagate this information when merging two
442 // TimeStampedPathCopies object.
444 // TimeStampedPathCopies object.
443 if copies.contains_key(f.as_ref()) {
445 if copies.contains_key(f.as_ref()) {
444 let ttpc = TimeStampedPathCopy {
446 let ttpc = TimeStampedPathCopy {
445 rev: current_rev,
447 rev: current_rev,
446 path: None,
448 path: None,
447 };
449 };
448 copies.insert(f.to_owned(), ttpc);
450 copies.insert(f.to_owned(), ttpc);
449 }
451 }
450 }
452 }
451 }
453 }
452 }
454 }
453 copies
455 copies
454 }
456 }
455
457
456 /// merge two copies-mapping together, minor and major
458 /// merge two copies-mapping together, minor and major
457 ///
459 ///
458 /// In case of conflict, value from "major" will be picked, unless in some
460 /// In case of conflict, value from "major" will be picked, unless in some
459 /// cases. See inline documentation for details.
461 /// cases. See inline documentation for details.
460 fn merge_copies_dict<A: Fn(Revision, Revision) -> bool>(
462 fn merge_copies_dict<A: Fn(Revision, Revision) -> bool>(
461 mut minor: TimeStampedPathCopies,
463 mut minor: TimeStampedPathCopies,
462 mut major: TimeStampedPathCopies,
464 mut major: TimeStampedPathCopies,
463 changes: &ChangedFiles,
465 changes: &ChangedFiles,
464 oracle: &mut AncestorOracle<A>,
466 oracle: &mut AncestorOracle<A>,
465 ) -> TimeStampedPathCopies {
467 ) -> TimeStampedPathCopies {
466 // This closure exist as temporary help while multiple developper are
468 // This closure exist as temporary help while multiple developper are
467 // actively working on this code. Feel free to re-inline it once this
469 // actively working on this code. Feel free to re-inline it once this
468 // code is more settled.
470 // code is more settled.
469 let mut cmp_value =
471 let mut cmp_value =
470 |dest: &HgPathBuf,
472 |dest: &PathToken,
471 src_minor: &TimeStampedPathCopy,
473 src_minor: &TimeStampedPathCopy,
472 src_major: &TimeStampedPathCopy| {
474 src_major: &TimeStampedPathCopy| {
473 compare_value(changes, oracle, dest, src_minor, src_major)
475 compare_value(changes, oracle, dest, src_minor, src_major)
474 };
476 };
475 if minor.is_empty() {
477 if minor.is_empty() {
476 major
478 major
477 } else if major.is_empty() {
479 } else if major.is_empty() {
478 minor
480 minor
479 } else if minor.len() * 2 < major.len() {
481 } else if minor.len() * 2 < major.len() {
480 // Lets says we are merging two TimeStampedPathCopies instance A and B.
482 // Lets says we are merging two TimeStampedPathCopies instance A and B.
481 //
483 //
482 // If A contains N items, the merge result will never contains more
484 // If A contains N items, the merge result will never contains more
483 // than N values differents than the one in A
485 // than N values differents than the one in A
484 //
486 //
485 // If B contains M items, with M > N, the merge result will always
487 // If B contains M items, with M > N, the merge result will always
486 // result in a minimum of M - N value differents than the on in
488 // result in a minimum of M - N value differents than the on in
487 // A
489 // A
488 //
490 //
489 // As a result, if N < (M-N), we know that simply iterating over A will
491 // As a result, if N < (M-N), we know that simply iterating over A will
490 // yield less difference than iterating over the difference
492 // yield less difference than iterating over the difference
491 // between A and B.
493 // between A and B.
492 //
494 //
493 // This help performance a lot in case were a tiny
495 // This help performance a lot in case were a tiny
494 // TimeStampedPathCopies is merged with a much larger one.
496 // TimeStampedPathCopies is merged with a much larger one.
495 for (dest, src_minor) in minor {
497 for (dest, src_minor) in minor {
496 let src_major = major.get(&dest);
498 let src_major = major.get(&dest);
497 match src_major {
499 match src_major {
498 None => major.insert(dest, src_minor),
500 None => major.insert(dest, src_minor),
499 Some(src_major) => {
501 Some(src_major) => {
500 match cmp_value(&dest, &src_minor, src_major) {
502 match cmp_value(&dest, &src_minor, src_major) {
501 MergePick::Any | MergePick::Major => None,
503 MergePick::Any | MergePick::Major => None,
502 MergePick::Minor => major.insert(dest, src_minor),
504 MergePick::Minor => major.insert(dest, src_minor),
503 }
505 }
504 }
506 }
505 };
507 };
506 }
508 }
507 major
509 major
508 } else if major.len() * 2 < minor.len() {
510 } else if major.len() * 2 < minor.len() {
509 // This use the same rational than the previous block.
511 // This use the same rational than the previous block.
510 // (Check previous block documentation for details.)
512 // (Check previous block documentation for details.)
511 for (dest, src_major) in major {
513 for (dest, src_major) in major {
512 let src_minor = minor.get(&dest);
514 let src_minor = minor.get(&dest);
513 match src_minor {
515 match src_minor {
514 None => minor.insert(dest, src_major),
516 None => minor.insert(dest, src_major),
515 Some(src_minor) => {
517 Some(src_minor) => {
516 match cmp_value(&dest, src_minor, &src_major) {
518 match cmp_value(&dest, src_minor, &src_major) {
517 MergePick::Any | MergePick::Minor => None,
519 MergePick::Any | MergePick::Minor => None,
518 MergePick::Major => minor.insert(dest, src_major),
520 MergePick::Major => minor.insert(dest, src_major),
519 }
521 }
520 }
522 }
521 };
523 };
522 }
524 }
523 minor
525 minor
524 } else {
526 } else {
525 let mut override_minor = Vec::new();
527 let mut override_minor = Vec::new();
526 let mut override_major = Vec::new();
528 let mut override_major = Vec::new();
527
529
528 let mut to_major = |k: &HgPathBuf, v: &TimeStampedPathCopy| {
530 let mut to_major = |k: &PathToken, v: &TimeStampedPathCopy| {
529 override_major.push((k.clone(), v.clone()))
531 override_major.push((k.clone(), v.clone()))
530 };
532 };
531 let mut to_minor = |k: &HgPathBuf, v: &TimeStampedPathCopy| {
533 let mut to_minor = |k: &PathToken, v: &TimeStampedPathCopy| {
532 override_minor.push((k.clone(), v.clone()))
534 override_minor.push((k.clone(), v.clone()))
533 };
535 };
534
536
535 // The diff function leverage detection of the identical subpart if
537 // The diff function leverage detection of the identical subpart if
536 // minor and major has some common ancestors. This make it very
538 // minor and major has some common ancestors. This make it very
537 // fast is most case.
539 // fast is most case.
538 //
540 //
539 // In case where the two map are vastly different in size, the current
541 // In case where the two map are vastly different in size, the current
540 // approach is still slowish because the iteration will iterate over
542 // approach is still slowish because the iteration will iterate over
541 // all the "exclusive" content of the larger on. This situation can be
543 // all the "exclusive" content of the larger on. This situation can be
542 // frequent when the subgraph of revision we are processing has a lot
544 // frequent when the subgraph of revision we are processing has a lot
543 // of roots. Each roots adding they own fully new map to the mix (and
545 // of roots. Each roots adding they own fully new map to the mix (and
544 // likely a small map, if the path from the root to the "main path" is
546 // likely a small map, if the path from the root to the "main path" is
545 // small.
547 // small.
546 //
548 //
547 // We could do better by detecting such situation and processing them
549 // We could do better by detecting such situation and processing them
548 // differently.
550 // differently.
549 for d in minor.diff(&major) {
551 for d in minor.diff(&major) {
550 match d {
552 match d {
551 DiffItem::Add(k, v) => to_minor(k, v),
553 DiffItem::Add(k, v) => to_minor(k, v),
552 DiffItem::Remove(k, v) => to_major(k, v),
554 DiffItem::Remove(k, v) => to_major(k, v),
553 DiffItem::Update { old, new } => {
555 DiffItem::Update { old, new } => {
554 let (dest, src_major) = new;
556 let (dest, src_major) = new;
555 let (_, src_minor) = old;
557 let (_, src_minor) = old;
556 match cmp_value(dest, src_minor, src_major) {
558 match cmp_value(dest, src_minor, src_major) {
557 MergePick::Major => to_minor(dest, src_major),
559 MergePick::Major => to_minor(dest, src_major),
558 MergePick::Minor => to_major(dest, src_minor),
560 MergePick::Minor => to_major(dest, src_minor),
559 // If the two entry are identical, no need to do
561 // If the two entry are identical, no need to do
560 // anything (but diff should not have yield them)
562 // anything (but diff should not have yield them)
561 MergePick::Any => unreachable!(),
563 MergePick::Any => unreachable!(),
562 }
564 }
563 }
565 }
564 };
566 };
565 }
567 }
566
568
567 let updates;
569 let updates;
568 let mut result;
570 let mut result;
569 if override_major.is_empty() {
571 if override_major.is_empty() {
570 result = major
572 result = major
571 } else if override_minor.is_empty() {
573 } else if override_minor.is_empty() {
572 result = minor
574 result = minor
573 } else {
575 } else {
574 if override_minor.len() < override_major.len() {
576 if override_minor.len() < override_major.len() {
575 updates = override_minor;
577 updates = override_minor;
576 result = minor;
578 result = minor;
577 } else {
579 } else {
578 updates = override_major;
580 updates = override_major;
579 result = major;
581 result = major;
580 }
582 }
581 for (k, v) in updates {
583 for (k, v) in updates {
582 result.insert(k, v);
584 result.insert(k, v);
583 }
585 }
584 }
586 }
585 result
587 result
586 }
588 }
587 }
589 }
588
590
589 /// represent the side that should prevail when merging two
591 /// represent the side that should prevail when merging two
590 /// TimeStampedPathCopies
592 /// TimeStampedPathCopies
591 enum MergePick {
593 enum MergePick {
592 /// The "major" (p1) side prevails
594 /// The "major" (p1) side prevails
593 Major,
595 Major,
594 /// The "minor" (p2) side prevails
596 /// The "minor" (p2) side prevails
595 Minor,
597 Minor,
596 /// Any side could be used (because they are the same)
598 /// Any side could be used (because they are the same)
597 Any,
599 Any,
598 }
600 }
599
601
600 /// decide which side prevails in case of conflicting values
602 /// decide which side prevails in case of conflicting values
601 #[allow(clippy::if_same_then_else)]
603 #[allow(clippy::if_same_then_else)]
602 fn compare_value<A: Fn(Revision, Revision) -> bool>(
604 fn compare_value<A: Fn(Revision, Revision) -> bool>(
603 changes: &ChangedFiles,
605 changes: &ChangedFiles,
604 oracle: &mut AncestorOracle<A>,
606 oracle: &mut AncestorOracle<A>,
605 dest: &HgPathBuf,
607 dest: &PathToken,
606 src_minor: &TimeStampedPathCopy,
608 src_minor: &TimeStampedPathCopy,
607 src_major: &TimeStampedPathCopy,
609 src_major: &TimeStampedPathCopy,
608 ) -> MergePick {
610 ) -> MergePick {
609 if src_major.path == src_minor.path {
611 if src_major.path == src_minor.path {
610 // we have the same value, but from other source;
612 // we have the same value, but from other source;
611 if src_major.rev == src_minor.rev {
613 if src_major.rev == src_minor.rev {
612 // If the two entry are identical, they are both valid
614 // If the two entry are identical, they are both valid
613 MergePick::Any
615 MergePick::Any
614 } else if oracle.is_ancestor(src_major.rev, src_minor.rev) {
616 } else if oracle.is_ancestor(src_major.rev, src_minor.rev) {
615 MergePick::Minor
617 MergePick::Minor
616 } else {
618 } else {
617 MergePick::Major
619 MergePick::Major
618 }
620 }
619 } else if src_major.rev == src_minor.rev {
621 } else if src_major.rev == src_minor.rev {
620 // We cannot get copy information for both p1 and p2 in the
622 // We cannot get copy information for both p1 and p2 in the
621 // same rev. So this is the same value.
623 // same rev. So this is the same value.
622 unreachable!(
624 unreachable!(
623 "conflict information from p1 and p2 in the same revision"
625 "conflict information from p1 and p2 in the same revision"
624 );
626 );
625 } else {
627 } else {
626 let action = changes.get_merge_case(&dest);
628 let action = changes.get_merge_case(&dest);
627 if src_major.path.is_none() && action == MergeCase::Salvaged {
629 if src_major.path.is_none() && action == MergeCase::Salvaged {
628 // If the file is "deleted" in the major side but was
630 // If the file is "deleted" in the major side but was
629 // salvaged by the merge, we keep the minor side alive
631 // salvaged by the merge, we keep the minor side alive
630 MergePick::Minor
632 MergePick::Minor
631 } else if src_minor.path.is_none() && action == MergeCase::Salvaged {
633 } else if src_minor.path.is_none() && action == MergeCase::Salvaged {
632 // If the file is "deleted" in the minor side but was
634 // If the file is "deleted" in the minor side but was
633 // salvaged by the merge, unconditionnaly preserve the
635 // salvaged by the merge, unconditionnaly preserve the
634 // major side.
636 // major side.
635 MergePick::Major
637 MergePick::Major
636 } else if action == MergeCase::Merged {
638 } else if action == MergeCase::Merged {
637 // If the file was actively merged, copy information
639 // If the file was actively merged, copy information
638 // from each side might conflict. The major side will
640 // from each side might conflict. The major side will
639 // win such conflict.
641 // win such conflict.
640 MergePick::Major
642 MergePick::Major
641 } else if oracle.is_ancestor(src_major.rev, src_minor.rev) {
643 } else if oracle.is_ancestor(src_major.rev, src_minor.rev) {
642 // If the minor side is strictly newer than the major
644 // If the minor side is strictly newer than the major
643 // side, it should be kept.
645 // side, it should be kept.
644 MergePick::Minor
646 MergePick::Minor
645 } else if src_major.path.is_some() {
647 } else if src_major.path.is_some() {
646 // without any special case, the "major" value win
648 // without any special case, the "major" value win
647 // other the "minor" one.
649 // other the "minor" one.
648 MergePick::Major
650 MergePick::Major
649 } else if oracle.is_ancestor(src_minor.rev, src_major.rev) {
651 } else if oracle.is_ancestor(src_minor.rev, src_major.rev) {
650 // the "major" rev is a direct ancestors of "minor",
652 // the "major" rev is a direct ancestors of "minor",
651 // any different value should
653 // any different value should
652 // overwrite
654 // overwrite
653 MergePick::Major
655 MergePick::Major
654 } else {
656 } else {
655 // major version is None (so the file was deleted on
657 // major version is None (so the file was deleted on
656 // that branch) and that branch is independant (neither
658 // that branch) and that branch is independant (neither
657 // minor nor major is an ancestors of the other one.)
659 // minor nor major is an ancestors of the other one.)
658 // We preserve the new
660 // We preserve the new
659 // information about the new file.
661 // information about the new file.
660 MergePick::Minor
662 MergePick::Minor
661 }
663 }
662 }
664 }
663 }
665 }
General Comments 0
You need to be logged in to leave comments. Login now