##// END OF EJS Templates
rust: fix broken debug assertions...
Raphaël Gomès -
r50811:321e2b7b default
parent child Browse files
Show More
@@ -1,680 +1,680 b''
1 #[cfg(test)]
1 #[cfg(test)]
2 #[macro_use]
2 #[macro_use]
3 mod tests_support;
3 mod tests_support;
4
4
5 #[cfg(test)]
5 #[cfg(test)]
6 mod tests;
6 mod tests;
7
7
8 use crate::utils::hg_path::HgPath;
8 use crate::utils::hg_path::HgPath;
9 use crate::utils::hg_path::HgPathBuf;
9 use crate::utils::hg_path::HgPathBuf;
10 use crate::Revision;
10 use crate::Revision;
11 use crate::NULL_REVISION;
11 use crate::NULL_REVISION;
12
12
13 use bytes_cast::{unaligned, BytesCast};
13 use bytes_cast::{unaligned, BytesCast};
14 use im_rc::ordmap::Entry;
14 use im_rc::ordmap::Entry;
15 use im_rc::ordmap::OrdMap;
15 use im_rc::ordmap::OrdMap;
16 use im_rc::OrdSet;
16 use im_rc::OrdSet;
17
17
18 use std::cmp::Ordering;
18 use std::cmp::Ordering;
19 use std::collections::HashMap;
19 use std::collections::HashMap;
20
20
21 pub type PathCopies = HashMap<HgPathBuf, HgPathBuf>;
21 pub type PathCopies = HashMap<HgPathBuf, HgPathBuf>;
22
22
23 type PathToken = usize;
23 type PathToken = usize;
24
24
25 #[derive(Clone, Debug)]
25 #[derive(Clone, Debug)]
26 struct CopySource {
26 struct CopySource {
27 /// revision at which the copy information was added
27 /// revision at which the copy information was added
28 rev: Revision,
28 rev: Revision,
29 /// the copy source, (Set to None in case of deletion of the associated
29 /// the copy source, (Set to None in case of deletion of the associated
30 /// key)
30 /// key)
31 path: Option<PathToken>,
31 path: Option<PathToken>,
32 /// a set of previous `CopySource.rev` value directly or indirectly
32 /// a set of previous `CopySource.rev` value directly or indirectly
33 /// overwritten by this one.
33 /// overwritten by this one.
34 overwritten: OrdSet<Revision>,
34 overwritten: OrdSet<Revision>,
35 }
35 }
36
36
37 impl CopySource {
37 impl CopySource {
38 /// create a new CopySource
38 /// create a new CopySource
39 ///
39 ///
40 /// Use this when no previous copy source existed.
40 /// Use this when no previous copy source existed.
41 fn new(rev: Revision, path: Option<PathToken>) -> Self {
41 fn new(rev: Revision, path: Option<PathToken>) -> Self {
42 Self {
42 Self {
43 rev,
43 rev,
44 path,
44 path,
45 overwritten: OrdSet::new(),
45 overwritten: OrdSet::new(),
46 }
46 }
47 }
47 }
48
48
49 /// create a new CopySource from merging two others
49 /// create a new CopySource from merging two others
50 ///
50 ///
51 /// Use this when merging two InternalPathCopies requires active merging of
51 /// Use this when merging two InternalPathCopies requires active merging of
52 /// some entries.
52 /// some entries.
53 fn new_from_merge(rev: Revision, winner: &Self, loser: &Self) -> Self {
53 fn new_from_merge(rev: Revision, winner: &Self, loser: &Self) -> Self {
54 let mut overwritten = OrdSet::new();
54 let mut overwritten = OrdSet::new();
55 overwritten.extend(winner.overwritten.iter().copied());
55 overwritten.extend(winner.overwritten.iter().copied());
56 overwritten.extend(loser.overwritten.iter().copied());
56 overwritten.extend(loser.overwritten.iter().copied());
57 overwritten.insert(winner.rev);
57 overwritten.insert(winner.rev);
58 overwritten.insert(loser.rev);
58 overwritten.insert(loser.rev);
59 Self {
59 Self {
60 rev,
60 rev,
61 path: winner.path,
61 path: winner.path,
62 overwritten: overwritten,
62 overwritten: overwritten,
63 }
63 }
64 }
64 }
65
65
66 /// Update the value of a pre-existing CopySource
66 /// Update the value of a pre-existing CopySource
67 ///
67 ///
68 /// Use this when recording copy information from parent → child edges
68 /// Use this when recording copy information from parent → child edges
69 fn overwrite(&mut self, rev: Revision, path: Option<PathToken>) {
69 fn overwrite(&mut self, rev: Revision, path: Option<PathToken>) {
70 self.overwritten.insert(self.rev);
70 self.overwritten.insert(self.rev);
71 self.rev = rev;
71 self.rev = rev;
72 self.path = path;
72 self.path = path;
73 }
73 }
74
74
75 /// Mark pre-existing copy information as "dropped" by a file deletion
75 /// Mark pre-existing copy information as "dropped" by a file deletion
76 ///
76 ///
77 /// Use this when recording copy information from parent → child edges
77 /// Use this when recording copy information from parent → child edges
78 fn mark_delete(&mut self, rev: Revision) {
78 fn mark_delete(&mut self, rev: Revision) {
79 self.overwritten.insert(self.rev);
79 self.overwritten.insert(self.rev);
80 self.rev = rev;
80 self.rev = rev;
81 self.path = None;
81 self.path = None;
82 }
82 }
83
83
84 /// Mark pre-existing copy information as "dropped" by a file deletion
84 /// Mark pre-existing copy information as "dropped" by a file deletion
85 ///
85 ///
86 /// Use this when recording copy information from parent → child edges
86 /// Use this when recording copy information from parent → child edges
87 fn mark_delete_with_pair(&mut self, rev: Revision, other: &Self) {
87 fn mark_delete_with_pair(&mut self, rev: Revision, other: &Self) {
88 self.overwritten.insert(self.rev);
88 self.overwritten.insert(self.rev);
89 if other.rev != rev {
89 if other.rev != rev {
90 self.overwritten.insert(other.rev);
90 self.overwritten.insert(other.rev);
91 }
91 }
92 self.overwritten.extend(other.overwritten.iter().copied());
92 self.overwritten.extend(other.overwritten.iter().copied());
93 self.rev = rev;
93 self.rev = rev;
94 self.path = None;
94 self.path = None;
95 }
95 }
96
96
97 fn is_overwritten_by(&self, other: &Self) -> bool {
97 fn is_overwritten_by(&self, other: &Self) -> bool {
98 other.overwritten.contains(&self.rev)
98 other.overwritten.contains(&self.rev)
99 }
99 }
100 }
100 }
101
101
102 // For the same "dest", content generated for a given revision will always be
102 // For the same "dest", content generated for a given revision will always be
103 // the same.
103 // the same.
104 impl PartialEq for CopySource {
104 impl PartialEq for CopySource {
105 fn eq(&self, other: &Self) -> bool {
105 fn eq(&self, other: &Self) -> bool {
106 #[cfg(debug_assertions)]
106 #[cfg(debug_assertions)]
107 {
107 {
108 if self.rev == other.rev {
108 if self.rev == other.rev {
109 debug_assert!(self.path == other.path);
109 debug_assert!(self.path == other.path);
110 debug_assert!(self.overwritten == other.overwritten);
110 debug_assert!(self.overwritten == other.overwritten);
111 }
111 }
112 }
112 }
113 self.rev == other.rev
113 self.rev == other.rev
114 }
114 }
115 }
115 }
116
116
117 /// maps CopyDestination to Copy Source (+ a "timestamp" for the operation)
117 /// maps CopyDestination to Copy Source (+ a "timestamp" for the operation)
118 type InternalPathCopies = OrdMap<PathToken, CopySource>;
118 type InternalPathCopies = OrdMap<PathToken, CopySource>;
119
119
120 /// Represent active changes that affect the copy tracing.
120 /// Represent active changes that affect the copy tracing.
121 enum Action<'a> {
121 enum Action<'a> {
122 /// The parent ? children edge is removing a file
122 /// The parent ? children edge is removing a file
123 ///
123 ///
124 /// (actually, this could be the edge from the other parent, but it does
124 /// (actually, this could be the edge from the other parent, but it does
125 /// not matters)
125 /// not matters)
126 Removed(&'a HgPath),
126 Removed(&'a HgPath),
127 /// The parent ? children edge introduce copy information between (dest,
127 /// The parent ? children edge introduce copy information between (dest,
128 /// source)
128 /// source)
129 CopiedFromP1(&'a HgPath, &'a HgPath),
129 CopiedFromP1(&'a HgPath, &'a HgPath),
130 CopiedFromP2(&'a HgPath, &'a HgPath),
130 CopiedFromP2(&'a HgPath, &'a HgPath),
131 }
131 }
132
132
133 /// This express the possible "special" case we can get in a merge
133 /// This express the possible "special" case we can get in a merge
134 ///
134 ///
135 /// See mercurial/metadata.py for details on these values.
135 /// See mercurial/metadata.py for details on these values.
136 #[derive(PartialEq)]
136 #[derive(PartialEq)]
137 enum MergeCase {
137 enum MergeCase {
138 /// Merged: file had history on both side that needed to be merged
138 /// Merged: file had history on both side that needed to be merged
139 Merged,
139 Merged,
140 /// Salvaged: file was candidate for deletion, but survived the merge
140 /// Salvaged: file was candidate for deletion, but survived the merge
141 Salvaged,
141 Salvaged,
142 /// Normal: Not one of the two cases above
142 /// Normal: Not one of the two cases above
143 Normal,
143 Normal,
144 }
144 }
145
145
146 const COPY_MASK: u8 = 3;
146 const COPY_MASK: u8 = 3;
147 const P1_COPY: u8 = 2;
147 const P1_COPY: u8 = 2;
148 const P2_COPY: u8 = 3;
148 const P2_COPY: u8 = 3;
149 const ACTION_MASK: u8 = 28;
149 const ACTION_MASK: u8 = 28;
150 const REMOVED: u8 = 12;
150 const REMOVED: u8 = 12;
151 const MERGED: u8 = 8;
151 const MERGED: u8 = 8;
152 const SALVAGED: u8 = 16;
152 const SALVAGED: u8 = 16;
153
153
154 #[derive(BytesCast)]
154 #[derive(BytesCast)]
155 #[repr(C)]
155 #[repr(C)]
156 struct ChangedFilesIndexEntry {
156 struct ChangedFilesIndexEntry {
157 flags: u8,
157 flags: u8,
158
158
159 /// Only the end position is stored. The start is at the end of the
159 /// Only the end position is stored. The start is at the end of the
160 /// previous entry.
160 /// previous entry.
161 destination_path_end_position: unaligned::U32Be,
161 destination_path_end_position: unaligned::U32Be,
162
162
163 source_index_entry_position: unaligned::U32Be,
163 source_index_entry_position: unaligned::U32Be,
164 }
164 }
165
165
166 fn _static_assert_size_of() {
166 fn _static_assert_size_of() {
167 let _ = std::mem::transmute::<ChangedFilesIndexEntry, [u8; 9]>;
167 let _ = std::mem::transmute::<ChangedFilesIndexEntry, [u8; 9]>;
168 }
168 }
169
169
170 /// Represents the files affected by a changeset.
170 /// Represents the files affected by a changeset.
171 ///
171 ///
172 /// This holds a subset of `mercurial.metadata.ChangingFiles` as we do not need
172 /// This holds a subset of `mercurial.metadata.ChangingFiles` as we do not need
173 /// all the data categories tracked by it.
173 /// all the data categories tracked by it.
174 pub struct ChangedFiles<'a> {
174 pub struct ChangedFiles<'a> {
175 index: &'a [ChangedFilesIndexEntry],
175 index: &'a [ChangedFilesIndexEntry],
176 paths: &'a [u8],
176 paths: &'a [u8],
177 }
177 }
178
178
179 impl<'a> ChangedFiles<'a> {
179 impl<'a> ChangedFiles<'a> {
180 pub fn new(data: &'a [u8]) -> Self {
180 pub fn new(data: &'a [u8]) -> Self {
181 let (header, rest) = unaligned::U32Be::from_bytes(data).unwrap();
181 let (header, rest) = unaligned::U32Be::from_bytes(data).unwrap();
182 let nb_index_entries = header.get() as usize;
182 let nb_index_entries = header.get() as usize;
183 let (index, paths) =
183 let (index, paths) =
184 ChangedFilesIndexEntry::slice_from_bytes(rest, nb_index_entries)
184 ChangedFilesIndexEntry::slice_from_bytes(rest, nb_index_entries)
185 .unwrap();
185 .unwrap();
186 Self { index, paths }
186 Self { index, paths }
187 }
187 }
188
188
189 pub fn new_empty() -> Self {
189 pub fn new_empty() -> Self {
190 ChangedFiles {
190 ChangedFiles {
191 index: &[],
191 index: &[],
192 paths: &[],
192 paths: &[],
193 }
193 }
194 }
194 }
195
195
196 /// Internal function to return the filename of the entry at a given index
196 /// Internal function to return the filename of the entry at a given index
197 fn path(&self, idx: usize) -> &HgPath {
197 fn path(&self, idx: usize) -> &HgPath {
198 let start = if idx == 0 {
198 let start = if idx == 0 {
199 0
199 0
200 } else {
200 } else {
201 self.index[idx - 1].destination_path_end_position.get() as usize
201 self.index[idx - 1].destination_path_end_position.get() as usize
202 };
202 };
203 let end = self.index[idx].destination_path_end_position.get() as usize;
203 let end = self.index[idx].destination_path_end_position.get() as usize;
204 HgPath::new(&self.paths[start..end])
204 HgPath::new(&self.paths[start..end])
205 }
205 }
206
206
207 /// Return an iterator over all the `Action` in this instance.
207 /// Return an iterator over all the `Action` in this instance.
208 fn iter_actions(&self) -> impl Iterator<Item = Action> {
208 fn iter_actions(&self) -> impl Iterator<Item = Action> {
209 self.index.iter().enumerate().flat_map(move |(idx, entry)| {
209 self.index.iter().enumerate().flat_map(move |(idx, entry)| {
210 let path = self.path(idx);
210 let path = self.path(idx);
211 if (entry.flags & ACTION_MASK) == REMOVED {
211 if (entry.flags & ACTION_MASK) == REMOVED {
212 Some(Action::Removed(path))
212 Some(Action::Removed(path))
213 } else if (entry.flags & COPY_MASK) == P1_COPY {
213 } else if (entry.flags & COPY_MASK) == P1_COPY {
214 let source_idx =
214 let source_idx =
215 entry.source_index_entry_position.get() as usize;
215 entry.source_index_entry_position.get() as usize;
216 Some(Action::CopiedFromP1(path, self.path(source_idx)))
216 Some(Action::CopiedFromP1(path, self.path(source_idx)))
217 } else if (entry.flags & COPY_MASK) == P2_COPY {
217 } else if (entry.flags & COPY_MASK) == P2_COPY {
218 let source_idx =
218 let source_idx =
219 entry.source_index_entry_position.get() as usize;
219 entry.source_index_entry_position.get() as usize;
220 Some(Action::CopiedFromP2(path, self.path(source_idx)))
220 Some(Action::CopiedFromP2(path, self.path(source_idx)))
221 } else {
221 } else {
222 None
222 None
223 }
223 }
224 })
224 })
225 }
225 }
226
226
227 /// return the MergeCase value associated with a filename
227 /// return the MergeCase value associated with a filename
228 fn get_merge_case(&self, path: &HgPath) -> MergeCase {
228 fn get_merge_case(&self, path: &HgPath) -> MergeCase {
229 if self.index.is_empty() {
229 if self.index.is_empty() {
230 return MergeCase::Normal;
230 return MergeCase::Normal;
231 }
231 }
232 let mut low_part = 0;
232 let mut low_part = 0;
233 let mut high_part = self.index.len();
233 let mut high_part = self.index.len();
234
234
235 while low_part < high_part {
235 while low_part < high_part {
236 let cursor = (low_part + high_part - 1) / 2;
236 let cursor = (low_part + high_part - 1) / 2;
237 match path.cmp(self.path(cursor)) {
237 match path.cmp(self.path(cursor)) {
238 Ordering::Less => low_part = cursor + 1,
238 Ordering::Less => low_part = cursor + 1,
239 Ordering::Greater => high_part = cursor,
239 Ordering::Greater => high_part = cursor,
240 Ordering::Equal => {
240 Ordering::Equal => {
241 return match self.index[cursor].flags & ACTION_MASK {
241 return match self.index[cursor].flags & ACTION_MASK {
242 MERGED => MergeCase::Merged,
242 MERGED => MergeCase::Merged,
243 SALVAGED => MergeCase::Salvaged,
243 SALVAGED => MergeCase::Salvaged,
244 _ => MergeCase::Normal,
244 _ => MergeCase::Normal,
245 };
245 };
246 }
246 }
247 }
247 }
248 }
248 }
249 MergeCase::Normal
249 MergeCase::Normal
250 }
250 }
251 }
251 }
252
252
253 /// A small "tokenizer" responsible of turning full HgPath into lighter
253 /// A small "tokenizer" responsible of turning full HgPath into lighter
254 /// PathToken
254 /// PathToken
255 ///
255 ///
256 /// Dealing with small object, like integer is much faster, so HgPath input are
256 /// Dealing with small object, like integer is much faster, so HgPath input are
257 /// turned into integer "PathToken" and converted back in the end.
257 /// turned into integer "PathToken" and converted back in the end.
258 #[derive(Clone, Debug, Default)]
258 #[derive(Clone, Debug, Default)]
259 struct TwoWayPathMap {
259 struct TwoWayPathMap {
260 token: HashMap<HgPathBuf, PathToken>,
260 token: HashMap<HgPathBuf, PathToken>,
261 path: Vec<HgPathBuf>,
261 path: Vec<HgPathBuf>,
262 }
262 }
263
263
264 impl TwoWayPathMap {
264 impl TwoWayPathMap {
265 fn tokenize(&mut self, path: &HgPath) -> PathToken {
265 fn tokenize(&mut self, path: &HgPath) -> PathToken {
266 match self.token.get(path) {
266 match self.token.get(path) {
267 Some(a) => *a,
267 Some(a) => *a,
268 None => {
268 None => {
269 let a = self.token.len();
269 let a = self.token.len();
270 let buf = path.to_owned();
270 let buf = path.to_owned();
271 self.path.push(buf.clone());
271 self.path.push(buf.clone());
272 self.token.insert(buf, a);
272 self.token.insert(buf, a);
273 a
273 a
274 }
274 }
275 }
275 }
276 }
276 }
277
277
278 fn untokenize(&self, token: PathToken) -> &HgPathBuf {
278 fn untokenize(&self, token: PathToken) -> &HgPathBuf {
279 assert!(token < self.path.len(), "Unknown token: {}", token);
279 assert!(token < self.path.len(), "Unknown token: {}", token);
280 &self.path[token]
280 &self.path[token]
281 }
281 }
282 }
282 }
283
283
284 /// Same as mercurial.copies._combine_changeset_copies, but in Rust.
284 /// Same as mercurial.copies._combine_changeset_copies, but in Rust.
285 pub struct CombineChangesetCopies {
285 pub struct CombineChangesetCopies {
286 all_copies: HashMap<Revision, InternalPathCopies>,
286 all_copies: HashMap<Revision, InternalPathCopies>,
287 path_map: TwoWayPathMap,
287 path_map: TwoWayPathMap,
288 children_count: HashMap<Revision, usize>,
288 children_count: HashMap<Revision, usize>,
289 }
289 }
290
290
291 impl CombineChangesetCopies {
291 impl CombineChangesetCopies {
292 pub fn new(children_count: HashMap<Revision, usize>) -> Self {
292 pub fn new(children_count: HashMap<Revision, usize>) -> Self {
293 Self {
293 Self {
294 all_copies: HashMap::new(),
294 all_copies: HashMap::new(),
295 path_map: TwoWayPathMap::default(),
295 path_map: TwoWayPathMap::default(),
296 children_count,
296 children_count,
297 }
297 }
298 }
298 }
299
299
300 /// Combined the given `changes` data specific to `rev` with the data
300 /// Combined the given `changes` data specific to `rev` with the data
301 /// previously given for its parents (and transitively, its ancestors).
301 /// previously given for its parents (and transitively, its ancestors).
302 pub fn add_revision(
302 pub fn add_revision(
303 &mut self,
303 &mut self,
304 rev: Revision,
304 rev: Revision,
305 p1: Revision,
305 p1: Revision,
306 p2: Revision,
306 p2: Revision,
307 changes: ChangedFiles<'_>,
307 changes: ChangedFiles<'_>,
308 ) {
308 ) {
309 self.add_revision_inner(rev, p1, p2, changes.iter_actions(), |path| {
309 self.add_revision_inner(rev, p1, p2, changes.iter_actions(), |path| {
310 changes.get_merge_case(path)
310 changes.get_merge_case(path)
311 })
311 })
312 }
312 }
313
313
314 /// Separated out from `add_revsion` so that unit tests can call this
314 /// Separated out from `add_revsion` so that unit tests can call this
315 /// without synthetizing a `ChangedFiles` in binary format.
315 /// without synthetizing a `ChangedFiles` in binary format.
316 fn add_revision_inner<'a>(
316 fn add_revision_inner<'a>(
317 &mut self,
317 &mut self,
318 rev: Revision,
318 rev: Revision,
319 p1: Revision,
319 p1: Revision,
320 p2: Revision,
320 p2: Revision,
321 copy_actions: impl Iterator<Item = Action<'a>>,
321 copy_actions: impl Iterator<Item = Action<'a>>,
322 get_merge_case: impl Fn(&HgPath) -> MergeCase + Copy,
322 get_merge_case: impl Fn(&HgPath) -> MergeCase + Copy,
323 ) {
323 ) {
324 // Retrieve data computed in a previous iteration
324 // Retrieve data computed in a previous iteration
325 let p1_copies = match p1 {
325 let p1_copies = match p1 {
326 NULL_REVISION => None,
326 NULL_REVISION => None,
327 _ => get_and_clean_parent_copies(
327 _ => get_and_clean_parent_copies(
328 &mut self.all_copies,
328 &mut self.all_copies,
329 &mut self.children_count,
329 &mut self.children_count,
330 p1,
330 p1,
331 ), // will be None if the vertex is not to be traversed
331 ), // will be None if the vertex is not to be traversed
332 };
332 };
333 let p2_copies = match p2 {
333 let p2_copies = match p2 {
334 NULL_REVISION => None,
334 NULL_REVISION => None,
335 _ => get_and_clean_parent_copies(
335 _ => get_and_clean_parent_copies(
336 &mut self.all_copies,
336 &mut self.all_copies,
337 &mut self.children_count,
337 &mut self.children_count,
338 p2,
338 p2,
339 ), // will be None if the vertex is not to be traversed
339 ), // will be None if the vertex is not to be traversed
340 };
340 };
341 // combine it with data for that revision
341 // combine it with data for that revision
342 let (p1_copies, p2_copies) = chain_changes(
342 let (p1_copies, p2_copies) = chain_changes(
343 &mut self.path_map,
343 &mut self.path_map,
344 p1_copies,
344 p1_copies,
345 p2_copies,
345 p2_copies,
346 copy_actions,
346 copy_actions,
347 rev,
347 rev,
348 );
348 );
349 let copies = match (p1_copies, p2_copies) {
349 let copies = match (p1_copies, p2_copies) {
350 (None, None) => None,
350 (None, None) => None,
351 (c, None) => c,
351 (c, None) => c,
352 (None, c) => c,
352 (None, c) => c,
353 (Some(p1_copies), Some(p2_copies)) => Some(merge_copies_dict(
353 (Some(p1_copies), Some(p2_copies)) => Some(merge_copies_dict(
354 &self.path_map,
354 &self.path_map,
355 rev,
355 rev,
356 p2_copies,
356 p2_copies,
357 p1_copies,
357 p1_copies,
358 get_merge_case,
358 get_merge_case,
359 )),
359 )),
360 };
360 };
361 if let Some(c) = copies {
361 if let Some(c) = copies {
362 self.all_copies.insert(rev, c);
362 self.all_copies.insert(rev, c);
363 }
363 }
364 }
364 }
365
365
366 /// Drop intermediate data (such as which revision a copy was from) and
366 /// Drop intermediate data (such as which revision a copy was from) and
367 /// return the final mapping.
367 /// return the final mapping.
368 pub fn finish(mut self, target_rev: Revision) -> PathCopies {
368 pub fn finish(mut self, target_rev: Revision) -> PathCopies {
369 let tt_result = self
369 let tt_result = self
370 .all_copies
370 .all_copies
371 .remove(&target_rev)
371 .remove(&target_rev)
372 .expect("target revision was not processed");
372 .expect("target revision was not processed");
373 let mut result = PathCopies::default();
373 let mut result = PathCopies::default();
374 for (dest, tt_source) in tt_result {
374 for (dest, tt_source) in tt_result {
375 if let Some(path) = tt_source.path {
375 if let Some(path) = tt_source.path {
376 let path_dest = self.path_map.untokenize(dest).to_owned();
376 let path_dest = self.path_map.untokenize(dest).to_owned();
377 let path_path = self.path_map.untokenize(path).to_owned();
377 let path_path = self.path_map.untokenize(path).to_owned();
378 result.insert(path_dest, path_path);
378 result.insert(path_dest, path_path);
379 }
379 }
380 }
380 }
381 result
381 result
382 }
382 }
383 }
383 }
384
384
385 /// fetch previous computed information
385 /// fetch previous computed information
386 ///
386 ///
387 /// If no other children are expected to need this information, we drop it from
387 /// If no other children are expected to need this information, we drop it from
388 /// the cache.
388 /// the cache.
389 ///
389 ///
390 /// If parent is not part of the set we are expected to walk, return None.
390 /// If parent is not part of the set we are expected to walk, return None.
391 fn get_and_clean_parent_copies(
391 fn get_and_clean_parent_copies(
392 all_copies: &mut HashMap<Revision, InternalPathCopies>,
392 all_copies: &mut HashMap<Revision, InternalPathCopies>,
393 children_count: &mut HashMap<Revision, usize>,
393 children_count: &mut HashMap<Revision, usize>,
394 parent_rev: Revision,
394 parent_rev: Revision,
395 ) -> Option<InternalPathCopies> {
395 ) -> Option<InternalPathCopies> {
396 let count = children_count.get_mut(&parent_rev)?;
396 let count = children_count.get_mut(&parent_rev)?;
397 *count -= 1;
397 *count -= 1;
398 if *count == 0 {
398 if *count == 0 {
399 match all_copies.remove(&parent_rev) {
399 match all_copies.remove(&parent_rev) {
400 Some(c) => Some(c),
400 Some(c) => Some(c),
401 None => Some(InternalPathCopies::default()),
401 None => Some(InternalPathCopies::default()),
402 }
402 }
403 } else {
403 } else {
404 match all_copies.get(&parent_rev) {
404 match all_copies.get(&parent_rev) {
405 Some(c) => Some(c.clone()),
405 Some(c) => Some(c.clone()),
406 None => Some(InternalPathCopies::default()),
406 None => Some(InternalPathCopies::default()),
407 }
407 }
408 }
408 }
409 }
409 }
410
410
411 /// Combine ChangedFiles with some existing PathCopies information and return
411 /// Combine ChangedFiles with some existing PathCopies information and return
412 /// the result
412 /// the result
413 fn chain_changes<'a>(
413 fn chain_changes<'a>(
414 path_map: &mut TwoWayPathMap,
414 path_map: &mut TwoWayPathMap,
415 base_p1_copies: Option<InternalPathCopies>,
415 base_p1_copies: Option<InternalPathCopies>,
416 base_p2_copies: Option<InternalPathCopies>,
416 base_p2_copies: Option<InternalPathCopies>,
417 copy_actions: impl Iterator<Item = Action<'a>>,
417 copy_actions: impl Iterator<Item = Action<'a>>,
418 current_rev: Revision,
418 current_rev: Revision,
419 ) -> (Option<InternalPathCopies>, Option<InternalPathCopies>) {
419 ) -> (Option<InternalPathCopies>, Option<InternalPathCopies>) {
420 // Fast path the "nothing to do" case.
420 // Fast path the "nothing to do" case.
421 if let (None, None) = (&base_p1_copies, &base_p2_copies) {
421 if let (None, None) = (&base_p1_copies, &base_p2_copies) {
422 return (None, None);
422 return (None, None);
423 }
423 }
424
424
425 let mut p1_copies = base_p1_copies.clone();
425 let mut p1_copies = base_p1_copies.clone();
426 let mut p2_copies = base_p2_copies.clone();
426 let mut p2_copies = base_p2_copies.clone();
427 for action in copy_actions {
427 for action in copy_actions {
428 match action {
428 match action {
429 Action::CopiedFromP1(path_dest, path_source) => {
429 Action::CopiedFromP1(path_dest, path_source) => {
430 match &mut p1_copies {
430 match &mut p1_copies {
431 None => (), // This is not a vertex we should proceed.
431 None => (), // This is not a vertex we should proceed.
432 Some(copies) => add_one_copy(
432 Some(copies) => add_one_copy(
433 current_rev,
433 current_rev,
434 path_map,
434 path_map,
435 copies,
435 copies,
436 base_p1_copies.as_ref().unwrap(),
436 base_p1_copies.as_ref().unwrap(),
437 path_dest,
437 path_dest,
438 path_source,
438 path_source,
439 ),
439 ),
440 }
440 }
441 }
441 }
442 Action::CopiedFromP2(path_dest, path_source) => {
442 Action::CopiedFromP2(path_dest, path_source) => {
443 match &mut p2_copies {
443 match &mut p2_copies {
444 None => (), // This is not a vertex we should proceed.
444 None => (), // This is not a vertex we should proceed.
445 Some(copies) => add_one_copy(
445 Some(copies) => add_one_copy(
446 current_rev,
446 current_rev,
447 path_map,
447 path_map,
448 copies,
448 copies,
449 base_p2_copies.as_ref().unwrap(),
449 base_p2_copies.as_ref().unwrap(),
450 path_dest,
450 path_dest,
451 path_source,
451 path_source,
452 ),
452 ),
453 }
453 }
454 }
454 }
455 Action::Removed(deleted_path) => {
455 Action::Removed(deleted_path) => {
456 // We must drop copy information for removed file.
456 // We must drop copy information for removed file.
457 //
457 //
458 // We need to explicitly record them as dropped to
458 // We need to explicitly record them as dropped to
459 // propagate this information when merging two
459 // propagate this information when merging two
460 // InternalPathCopies object.
460 // InternalPathCopies object.
461 let deleted = path_map.tokenize(deleted_path);
461 let deleted = path_map.tokenize(deleted_path);
462
462
463 let p1_entry = match &mut p1_copies {
463 let p1_entry = match &mut p1_copies {
464 None => None,
464 None => None,
465 Some(copies) => match copies.entry(deleted) {
465 Some(copies) => match copies.entry(deleted) {
466 Entry::Occupied(e) => Some(e),
466 Entry::Occupied(e) => Some(e),
467 Entry::Vacant(_) => None,
467 Entry::Vacant(_) => None,
468 },
468 },
469 };
469 };
470 let p2_entry = match &mut p2_copies {
470 let p2_entry = match &mut p2_copies {
471 None => None,
471 None => None,
472 Some(copies) => match copies.entry(deleted) {
472 Some(copies) => match copies.entry(deleted) {
473 Entry::Occupied(e) => Some(e),
473 Entry::Occupied(e) => Some(e),
474 Entry::Vacant(_) => None,
474 Entry::Vacant(_) => None,
475 },
475 },
476 };
476 };
477
477
478 match (p1_entry, p2_entry) {
478 match (p1_entry, p2_entry) {
479 (None, None) => (),
479 (None, None) => (),
480 (Some(mut e), None) => {
480 (Some(mut e), None) => {
481 e.get_mut().mark_delete(current_rev)
481 e.get_mut().mark_delete(current_rev)
482 }
482 }
483 (None, Some(mut e)) => {
483 (None, Some(mut e)) => {
484 e.get_mut().mark_delete(current_rev)
484 e.get_mut().mark_delete(current_rev)
485 }
485 }
486 (Some(mut e1), Some(mut e2)) => {
486 (Some(mut e1), Some(mut e2)) => {
487 let cs1 = e1.get_mut();
487 let cs1 = e1.get_mut();
488 let cs2 = e2.get();
488 let cs2 = e2.get();
489 if cs1 == cs2 {
489 if cs1 == cs2 {
490 cs1.mark_delete(current_rev);
490 cs1.mark_delete(current_rev);
491 } else {
491 } else {
492 cs1.mark_delete_with_pair(current_rev, &cs2);
492 cs1.mark_delete_with_pair(current_rev, &cs2);
493 }
493 }
494 e2.insert(cs1.clone());
494 e2.insert(cs1.clone());
495 }
495 }
496 }
496 }
497 }
497 }
498 }
498 }
499 }
499 }
500 (p1_copies, p2_copies)
500 (p1_copies, p2_copies)
501 }
501 }
502
502
503 // insert one new copy information in an InternalPathCopies
503 // insert one new copy information in an InternalPathCopies
504 //
504 //
505 // This deal with chaining and overwrite.
505 // This deal with chaining and overwrite.
506 fn add_one_copy(
506 fn add_one_copy(
507 current_rev: Revision,
507 current_rev: Revision,
508 path_map: &mut TwoWayPathMap,
508 path_map: &mut TwoWayPathMap,
509 copies: &mut InternalPathCopies,
509 copies: &mut InternalPathCopies,
510 base_copies: &InternalPathCopies,
510 base_copies: &InternalPathCopies,
511 path_dest: &HgPath,
511 path_dest: &HgPath,
512 path_source: &HgPath,
512 path_source: &HgPath,
513 ) {
513 ) {
514 let dest = path_map.tokenize(path_dest);
514 let dest = path_map.tokenize(path_dest);
515 let source = path_map.tokenize(path_source);
515 let source = path_map.tokenize(path_source);
516 let entry;
516 let entry;
517 if let Some(v) = base_copies.get(&source) {
517 if let Some(v) = base_copies.get(&source) {
518 entry = match &v.path {
518 entry = match &v.path {
519 Some(path) => Some((*(path)).to_owned()),
519 Some(path) => Some((*(path)).to_owned()),
520 None => Some(source.to_owned()),
520 None => Some(source.to_owned()),
521 }
521 }
522 } else {
522 } else {
523 entry = Some(source.to_owned());
523 entry = Some(source.to_owned());
524 }
524 }
525 // Each new entry is introduced by the children, we
525 // Each new entry is introduced by the children, we
526 // record this information as we will need it to take
526 // record this information as we will need it to take
527 // the right decision when merging conflicting copy
527 // the right decision when merging conflicting copy
528 // information. See merge_copies_dict for details.
528 // information. See merge_copies_dict for details.
529 match copies.entry(dest) {
529 match copies.entry(dest) {
530 Entry::Vacant(slot) => {
530 Entry::Vacant(slot) => {
531 let ttpc = CopySource::new(current_rev, entry);
531 let ttpc = CopySource::new(current_rev, entry);
532 slot.insert(ttpc);
532 slot.insert(ttpc);
533 }
533 }
534 Entry::Occupied(mut slot) => {
534 Entry::Occupied(mut slot) => {
535 let ttpc = slot.get_mut();
535 let ttpc = slot.get_mut();
536 ttpc.overwrite(current_rev, entry);
536 ttpc.overwrite(current_rev, entry);
537 }
537 }
538 }
538 }
539 }
539 }
540
540
541 /// merge two copies-mapping together, minor and major
541 /// merge two copies-mapping together, minor and major
542 ///
542 ///
543 /// In case of conflict, value from "major" will be picked, unless in some
543 /// In case of conflict, value from "major" will be picked, unless in some
544 /// cases. See inline documentation for details.
544 /// cases. See inline documentation for details.
545 fn merge_copies_dict(
545 fn merge_copies_dict(
546 path_map: &TwoWayPathMap,
546 path_map: &TwoWayPathMap,
547 current_merge: Revision,
547 current_merge: Revision,
548 minor: InternalPathCopies,
548 minor: InternalPathCopies,
549 major: InternalPathCopies,
549 major: InternalPathCopies,
550 get_merge_case: impl Fn(&HgPath) -> MergeCase + Copy,
550 get_merge_case: impl Fn(&HgPath) -> MergeCase + Copy,
551 ) -> InternalPathCopies {
551 ) -> InternalPathCopies {
552 use crate::utils::{ordmap_union_with_merge, MergeResult};
552 use crate::utils::{ordmap_union_with_merge, MergeResult};
553
553
554 ordmap_union_with_merge(minor, major, |&dest, src_minor, src_major| {
554 ordmap_union_with_merge(minor, major, |&dest, src_minor, src_major| {
555 let (pick, overwrite) = compare_value(
555 let (pick, overwrite) = compare_value(
556 current_merge,
556 current_merge,
557 || get_merge_case(path_map.untokenize(dest)),
557 || get_merge_case(path_map.untokenize(dest)),
558 src_minor,
558 src_minor,
559 src_major,
559 src_major,
560 );
560 );
561 if overwrite {
561 if overwrite {
562 let (winner, loser) = match pick {
562 let (winner, loser) = match pick {
563 MergePick::Major | MergePick::Any => (src_major, src_minor),
563 MergePick::Major | MergePick::Any => (src_major, src_minor),
564 MergePick::Minor => (src_minor, src_major),
564 MergePick::Minor => (src_minor, src_major),
565 };
565 };
566 MergeResult::UseNewValue(CopySource::new_from_merge(
566 MergeResult::UseNewValue(CopySource::new_from_merge(
567 current_merge,
567 current_merge,
568 winner,
568 winner,
569 loser,
569 loser,
570 ))
570 ))
571 } else {
571 } else {
572 match pick {
572 match pick {
573 MergePick::Any | MergePick::Major => {
573 MergePick::Any | MergePick::Major => {
574 MergeResult::UseRightValue
574 MergeResult::UseRightValue
575 }
575 }
576 MergePick::Minor => MergeResult::UseLeftValue,
576 MergePick::Minor => MergeResult::UseLeftValue,
577 }
577 }
578 }
578 }
579 })
579 })
580 }
580 }
581
581
582 /// represent the side that should prevail when merging two
582 /// represent the side that should prevail when merging two
583 /// InternalPathCopies
583 /// InternalPathCopies
584 #[derive(Debug, PartialEq)]
584 #[derive(Debug, PartialEq)]
585 enum MergePick {
585 enum MergePick {
586 /// The "major" (p1) side prevails
586 /// The "major" (p1) side prevails
587 Major,
587 Major,
588 /// The "minor" (p2) side prevails
588 /// The "minor" (p2) side prevails
589 Minor,
589 Minor,
590 /// Any side could be used (because they are the same)
590 /// Any side could be used (because they are the same)
591 Any,
591 Any,
592 }
592 }
593
593
594 /// decide which side prevails in case of conflicting values
594 /// decide which side prevails in case of conflicting values
595 #[allow(clippy::if_same_then_else)]
595 #[allow(clippy::if_same_then_else)]
596 fn compare_value(
596 fn compare_value(
597 current_merge: Revision,
597 current_merge: Revision,
598 merge_case_for_dest: impl Fn() -> MergeCase,
598 merge_case_for_dest: impl Fn() -> MergeCase,
599 src_minor: &CopySource,
599 src_minor: &CopySource,
600 src_major: &CopySource,
600 src_major: &CopySource,
601 ) -> (MergePick, bool) {
601 ) -> (MergePick, bool) {
602 if src_major == src_minor {
602 if src_major == src_minor {
603 (MergePick::Any, false)
603 (MergePick::Any, false)
604 } else if src_major.rev == current_merge {
604 } else if src_major.rev == current_merge {
605 // minor is different according to per minor == major check earlier
605 // minor is different according to per minor == major check earlier
606 debug_assert!(src_minor.rev != current_merge);
606 debug_assert!(src_minor.rev != current_merge);
607
607
608 // The last value comes the current merge, this value -will- win
608 // The last value comes the current merge, this value -will- win
609 // eventually.
609 // eventually.
610 (MergePick::Major, true)
610 (MergePick::Major, true)
611 } else if src_minor.rev == current_merge {
611 } else if src_minor.rev == current_merge {
612 // The last value comes the current merge, this value -will- win
612 // The last value comes the current merge, this value -will- win
613 // eventually.
613 // eventually.
614 (MergePick::Minor, true)
614 (MergePick::Minor, true)
615 } else if src_major.path == src_minor.path {
615 } else if src_major.path == src_minor.path {
616 debug_assert!(src_major.rev != src_major.rev);
616 debug_assert!(src_major.rev != src_minor.rev);
617 // we have the same value, but from other source;
617 // we have the same value, but from other source;
618 if src_major.is_overwritten_by(src_minor) {
618 if src_major.is_overwritten_by(src_minor) {
619 (MergePick::Minor, false)
619 (MergePick::Minor, false)
620 } else if src_minor.is_overwritten_by(src_major) {
620 } else if src_minor.is_overwritten_by(src_major) {
621 (MergePick::Major, false)
621 (MergePick::Major, false)
622 } else {
622 } else {
623 (MergePick::Any, true)
623 (MergePick::Any, true)
624 }
624 }
625 } else {
625 } else {
626 debug_assert!(src_major.rev != src_major.rev);
626 debug_assert!(src_major.rev != src_minor.rev);
627 let action = merge_case_for_dest();
627 let action = merge_case_for_dest();
628 if src_minor.path.is_some()
628 if src_minor.path.is_some()
629 && src_major.path.is_none()
629 && src_major.path.is_none()
630 && action == MergeCase::Salvaged
630 && action == MergeCase::Salvaged
631 {
631 {
632 // If the file is "deleted" in the major side but was
632 // If the file is "deleted" in the major side but was
633 // salvaged by the merge, we keep the minor side alive
633 // salvaged by the merge, we keep the minor side alive
634 (MergePick::Minor, true)
634 (MergePick::Minor, true)
635 } else if src_major.path.is_some()
635 } else if src_major.path.is_some()
636 && src_minor.path.is_none()
636 && src_minor.path.is_none()
637 && action == MergeCase::Salvaged
637 && action == MergeCase::Salvaged
638 {
638 {
639 // If the file is "deleted" in the minor side but was
639 // If the file is "deleted" in the minor side but was
640 // salvaged by the merge, unconditionnaly preserve the
640 // salvaged by the merge, unconditionnaly preserve the
641 // major side.
641 // major side.
642 (MergePick::Major, true)
642 (MergePick::Major, true)
643 } else if src_minor.is_overwritten_by(src_major) {
643 } else if src_minor.is_overwritten_by(src_major) {
644 // The information from the minor version are strictly older than
644 // The information from the minor version are strictly older than
645 // the major version
645 // the major version
646 if action == MergeCase::Merged {
646 if action == MergeCase::Merged {
647 // If the file was actively merged, its means some non-copy
647 // If the file was actively merged, its means some non-copy
648 // activity happened on the other branch. It
648 // activity happened on the other branch. It
649 // mean the older copy information are still relevant.
649 // mean the older copy information are still relevant.
650 //
650 //
651 // The major side wins such conflict.
651 // The major side wins such conflict.
652 (MergePick::Major, true)
652 (MergePick::Major, true)
653 } else {
653 } else {
654 // No activity on the minor branch, pick the newer one.
654 // No activity on the minor branch, pick the newer one.
655 (MergePick::Major, false)
655 (MergePick::Major, false)
656 }
656 }
657 } else if src_major.is_overwritten_by(src_minor) {
657 } else if src_major.is_overwritten_by(src_minor) {
658 if action == MergeCase::Merged {
658 if action == MergeCase::Merged {
659 // If the file was actively merged, its means some non-copy
659 // If the file was actively merged, its means some non-copy
660 // activity happened on the other branch. It
660 // activity happened on the other branch. It
661 // mean the older copy information are still relevant.
661 // mean the older copy information are still relevant.
662 //
662 //
663 // The major side wins such conflict.
663 // The major side wins such conflict.
664 (MergePick::Major, true)
664 (MergePick::Major, true)
665 } else {
665 } else {
666 // No activity on the minor branch, pick the newer one.
666 // No activity on the minor branch, pick the newer one.
667 (MergePick::Minor, false)
667 (MergePick::Minor, false)
668 }
668 }
669 } else if src_minor.path.is_none() {
669 } else if src_minor.path.is_none() {
670 // the minor side has no relevant information, pick the alive one
670 // the minor side has no relevant information, pick the alive one
671 (MergePick::Major, true)
671 (MergePick::Major, true)
672 } else if src_major.path.is_none() {
672 } else if src_major.path.is_none() {
673 // the major side has no relevant information, pick the alive one
673 // the major side has no relevant information, pick the alive one
674 (MergePick::Minor, true)
674 (MergePick::Minor, true)
675 } else {
675 } else {
676 // by default the major side wins
676 // by default the major side wins
677 (MergePick::Major, true)
677 (MergePick::Major, true)
678 }
678 }
679 }
679 }
680 }
680 }
General Comments 0
You need to be logged in to leave comments. Login now