Show More
@@ -183,10 +183,27 b' def _revinfogetter(repo):' | |||||
183 | * p1copies: mapping of copies from p1 |
|
183 | * p1copies: mapping of copies from p1 | |
184 | * p2copies: mapping of copies from p2 |
|
184 | * p2copies: mapping of copies from p2 | |
185 | * removed: a list of removed files |
|
185 | * removed: a list of removed files | |
|
186 | * ismerged: a callback to know if file was merged in that revision | |||
186 | """ |
|
187 | """ | |
187 | cl = repo.changelog |
|
188 | cl = repo.changelog | |
188 | parents = cl.parentrevs |
|
189 | parents = cl.parentrevs | |
189 |
|
190 | |||
|
191 | def get_ismerged(rev): | |||
|
192 | ctx = repo[rev] | |||
|
193 | ||||
|
194 | def ismerged(path): | |||
|
195 | if path not in ctx.files(): | |||
|
196 | return False | |||
|
197 | fctx = ctx[path] | |||
|
198 | parents = fctx._filelog.parents(fctx._filenode) | |||
|
199 | nb_parents = 0 | |||
|
200 | for n in parents: | |||
|
201 | if n != node.nullid: | |||
|
202 | nb_parents += 1 | |||
|
203 | return nb_parents >= 2 | |||
|
204 | ||||
|
205 | return ismerged | |||
|
206 | ||||
190 | if repo.filecopiesmode == b'changeset-sidedata': |
|
207 | if repo.filecopiesmode == b'changeset-sidedata': | |
191 | changelogrevision = cl.changelogrevision |
|
208 | changelogrevision = cl.changelogrevision | |
192 | flags = cl.flags |
|
209 | flags = cl.flags | |
@@ -218,6 +235,7 b' def _revinfogetter(repo):' | |||||
218 |
|
235 | |||
219 | def revinfo(rev): |
|
236 | def revinfo(rev): | |
220 | p1, p2 = parents(rev) |
|
237 | p1, p2 = parents(rev) | |
|
238 | value = None | |||
221 | if flags(rev) & REVIDX_SIDEDATA: |
|
239 | if flags(rev) & REVIDX_SIDEDATA: | |
222 | e = merge_caches.pop(rev, None) |
|
240 | e = merge_caches.pop(rev, None) | |
223 | if e is not None: |
|
241 | if e is not None: | |
@@ -228,12 +246,22 b' def _revinfogetter(repo):' | |||||
228 | removed = c.filesremoved |
|
246 | removed = c.filesremoved | |
229 | if p1 != node.nullrev and p2 != node.nullrev: |
|
247 | if p1 != node.nullrev and p2 != node.nullrev: | |
230 | # XXX some case we over cache, IGNORE |
|
248 | # XXX some case we over cache, IGNORE | |
231 |
merge_caches[rev] = ( |
|
249 | value = merge_caches[rev] = ( | |
|
250 | p1, | |||
|
251 | p2, | |||
|
252 | p1copies, | |||
|
253 | p2copies, | |||
|
254 | removed, | |||
|
255 | get_ismerged(rev), | |||
|
256 | ) | |||
232 | else: |
|
257 | else: | |
233 | p1copies = {} |
|
258 | p1copies = {} | |
234 | p2copies = {} |
|
259 | p2copies = {} | |
235 | removed = [] |
|
260 | removed = [] | |
236 | return p1, p2, p1copies, p2copies, removed |
|
261 | ||
|
262 | if value is None: | |||
|
263 | value = (p1, p2, p1copies, p2copies, removed, get_ismerged(rev)) | |||
|
264 | return value | |||
237 |
|
265 | |||
238 | else: |
|
266 | else: | |
239 |
|
267 | |||
@@ -242,7 +270,7 b' def _revinfogetter(repo):' | |||||
242 | ctx = repo[rev] |
|
270 | ctx = repo[rev] | |
243 | p1copies, p2copies = ctx._copies |
|
271 | p1copies, p2copies = ctx._copies | |
244 | removed = ctx.filesremoved() |
|
272 | removed = ctx.filesremoved() | |
245 | return p1, p2, p1copies, p2copies, removed |
|
273 | return p1, p2, p1copies, p2copies, removed, get_ismerged(rev) | |
246 |
|
274 | |||
247 | return revinfo |
|
275 | return revinfo | |
248 |
|
276 | |||
@@ -256,6 +284,7 b' def _changesetforwardcopies(a, b, match)' | |||||
256 | revinfo = _revinfogetter(repo) |
|
284 | revinfo = _revinfogetter(repo) | |
257 |
|
285 | |||
258 | cl = repo.changelog |
|
286 | cl = repo.changelog | |
|
287 | isancestor = cl.isancestorrev # XXX we should had chaching to this. | |||
259 | missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()]) |
|
288 | missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()]) | |
260 | mrset = set(missingrevs) |
|
289 | mrset = set(missingrevs) | |
261 | roots = set() |
|
290 | roots = set() | |
@@ -283,10 +312,14 b' def _changesetforwardcopies(a, b, match)' | |||||
283 | iterrevs.update(roots) |
|
312 | iterrevs.update(roots) | |
284 | iterrevs.remove(b.rev()) |
|
313 | iterrevs.remove(b.rev()) | |
285 | revs = sorted(iterrevs) |
|
314 | revs = sorted(iterrevs) | |
286 |
return _combinechangesetcopies( |
|
315 | return _combinechangesetcopies( | |
|
316 | revs, children, b.rev(), revinfo, match, isancestor | |||
|
317 | ) | |||
287 |
|
318 | |||
288 |
|
319 | |||
289 | def _combinechangesetcopies(revs, children, targetrev, revinfo, match): |
|
320 | def _combinechangesetcopies( | |
|
321 | revs, children, targetrev, revinfo, match, isancestor | |||
|
322 | ): | |||
290 | """combine the copies information for each item of iterrevs |
|
323 | """combine the copies information for each item of iterrevs | |
291 |
|
324 | |||
292 | revs: sorted iterable of revision to visit |
|
325 | revs: sorted iterable of revision to visit | |
@@ -305,7 +338,7 b' def _combinechangesetcopies(revs, childr' | |||||
305 | # this is a root |
|
338 | # this is a root | |
306 | copies = {} |
|
339 | copies = {} | |
307 | for i, c in enumerate(children[r]): |
|
340 | for i, c in enumerate(children[r]): | |
308 | p1, p2, p1copies, p2copies, removed = revinfo(c) |
|
341 | p1, p2, p1copies, p2copies, removed, ismerged = revinfo(c) | |
309 | if r == p1: |
|
342 | if r == p1: | |
310 | parent = 1 |
|
343 | parent = 1 | |
311 | childcopies = p1copies |
|
344 | childcopies = p1copies | |
@@ -319,9 +352,12 b' def _combinechangesetcopies(revs, childr' | |||||
319 | } |
|
352 | } | |
320 | newcopies = copies |
|
353 | newcopies = copies | |
321 | if childcopies: |
|
354 | if childcopies: | |
322 |
newcopies = |
|
355 | newcopies = copies.copy() | |
323 | # _chain makes a copies, we can avoid doing so in some |
|
356 | for dest, source in pycompat.iteritems(childcopies): | |
324 | # simple/linear cases. |
|
357 | prev = copies.get(source) | |
|
358 | if prev is not None and prev[1] is not None: | |||
|
359 | source = prev[1] | |||
|
360 | newcopies[dest] = (c, source) | |||
325 | assert newcopies is not copies |
|
361 | assert newcopies is not copies | |
326 | for f in removed: |
|
362 | for f in removed: | |
327 | if f in newcopies: |
|
363 | if f in newcopies: | |
@@ -330,7 +366,7 b' def _combinechangesetcopies(revs, childr' | |||||
330 | # branches. when there are no other branches, this |
|
366 | # branches. when there are no other branches, this | |
331 | # could be avoided. |
|
367 | # could be avoided. | |
332 | newcopies = copies.copy() |
|
368 | newcopies = copies.copy() | |
333 |
|
|
369 | newcopies[f] = (c, None) | |
334 | othercopies = all_copies.get(c) |
|
370 | othercopies = all_copies.get(c) | |
335 | if othercopies is None: |
|
371 | if othercopies is None: | |
336 | all_copies[c] = newcopies |
|
372 | all_copies[c] = newcopies | |
@@ -338,21 +374,55 b' def _combinechangesetcopies(revs, childr' | |||||
338 | # we are the second parent to work on c, we need to merge our |
|
374 | # we are the second parent to work on c, we need to merge our | |
339 | # work with the other. |
|
375 | # work with the other. | |
340 | # |
|
376 | # | |
341 | # Unlike when copies are stored in the filelog, we consider |
|
|||
342 | # it a copy even if the destination already existed on the |
|
|||
343 | # other branch. It's simply too expensive to check if the |
|
|||
344 | # file existed in the manifest. |
|
|||
345 | # |
|
|||
346 | # In case of conflict, parent 1 take precedence over parent 2. |
|
377 | # In case of conflict, parent 1 take precedence over parent 2. | |
347 | # This is an arbitrary choice made anew when implementing |
|
378 | # This is an arbitrary choice made anew when implementing | |
348 | # changeset based copies. It was made without regards with |
|
379 | # changeset based copies. It was made without regards with | |
349 | # potential filelog related behavior. |
|
380 | # potential filelog related behavior. | |
350 | if parent == 1: |
|
381 | if parent == 1: | |
351 |
|
|
382 | _merge_copies_dict( | |
|
383 | othercopies, newcopies, isancestor, ismerged | |||
|
384 | ) | |||
352 | else: |
|
385 | else: | |
353 |
|
|
386 | _merge_copies_dict( | |
|
387 | newcopies, othercopies, isancestor, ismerged | |||
|
388 | ) | |||
354 | all_copies[c] = newcopies |
|
389 | all_copies[c] = newcopies | |
355 | return all_copies[targetrev] |
|
390 | ||
|
391 | final_copies = {} | |||
|
392 | for dest, (tt, source) in all_copies[targetrev].items(): | |||
|
393 | if source is not None: | |||
|
394 | final_copies[dest] = source | |||
|
395 | return final_copies | |||
|
396 | ||||
|
397 | ||||
|
398 | def _merge_copies_dict(minor, major, isancestor, ismerged): | |||
|
399 | """merge two copies-mapping together, minor and major | |||
|
400 | ||||
|
401 | In case of conflict, value from "major" will be picked. | |||
|
402 | ||||
|
403 | - `isancestors(low_rev, high_rev)`: callable return True if `low_rev` is an | |||
|
404 | ancestors of `high_rev`, | |||
|
405 | ||||
|
406 | - `ismerged(path)`: callable return True if `path` have been merged in the | |||
|
407 | current revision, | |||
|
408 | """ | |||
|
409 | for dest, value in major.items(): | |||
|
410 | other = minor.get(dest) | |||
|
411 | if other is None: | |||
|
412 | minor[dest] = value | |||
|
413 | else: | |||
|
414 | new_tt = value[0] | |||
|
415 | other_tt = other[0] | |||
|
416 | if value[1] == other[1]: | |||
|
417 | continue | |||
|
418 | # content from "major" wins, unless it is older | |||
|
419 | # than the branch point or there is a merge | |||
|
420 | if ( | |||
|
421 | new_tt == other_tt | |||
|
422 | or not isancestor(new_tt, other_tt) | |||
|
423 | or ismerged(dest) | |||
|
424 | ): | |||
|
425 | minor[dest] = value | |||
356 |
|
426 | |||
357 |
|
427 | |||
358 | def _forwardcopies(a, b, base=None, match=None): |
|
428 | def _forwardcopies(a, b, base=None, match=None): |
@@ -1,3 +1,5 b'' | |||||
|
1 | #testcases filelog compatibility sidedata | |||
|
2 | ||||
1 | ===================================================== |
|
3 | ===================================================== | |
2 | Test Copy tracing for chain of copies involving merge |
|
4 | Test Copy tracing for chain of copies involving merge | |
3 | ===================================================== |
|
5 | ===================================================== | |
@@ -6,6 +8,7 b' This test files covers copies/rename cas' | |||||
6 | are involved. It cheks we do not have unwanted update of behavior and that the |
|
8 | are involved. It cheks we do not have unwanted update of behavior and that the | |
7 | different options to retrieve copies behave correctly. |
|
9 | different options to retrieve copies behave correctly. | |
8 |
|
10 | |||
|
11 | ||||
9 | Setup |
|
12 | Setup | |
10 | ===== |
|
13 | ===== | |
11 |
|
14 | |||
@@ -18,6 +21,22 b' use git diff to see rename' | |||||
18 | > logtemplate={rev} {desc}\n |
|
21 | > logtemplate={rev} {desc}\n | |
19 | > EOF |
|
22 | > EOF | |
20 |
|
23 | |||
|
24 | #if compatibility | |||
|
25 | $ cat >> $HGRCPATH << EOF | |||
|
26 | > [experimental] | |||
|
27 | > copies.read-from = compatibility | |||
|
28 | > EOF | |||
|
29 | #endif | |||
|
30 | ||||
|
31 | #if sidedata | |||
|
32 | $ cat >> $HGRCPATH << EOF | |||
|
33 | > [format] | |||
|
34 | > exp-use-side-data = yes | |||
|
35 | > exp-use-copies-side-data-changeset = yes | |||
|
36 | > EOF | |||
|
37 | #endif | |||
|
38 | ||||
|
39 | ||||
21 | $ hg init repo-chain |
|
40 | $ hg init repo-chain | |
22 | $ cd repo-chain |
|
41 | $ cd repo-chain | |
23 |
|
42 | |||
@@ -453,17 +472,26 b' Comparing with a merge with colliding re' | |||||
453 | 0 4 0dd616bc7ab1 000000000000 000000000000 |
|
472 | 0 4 0dd616bc7ab1 000000000000 000000000000 | |
454 | 1 10 6da5a2eecb9c 000000000000 000000000000 |
|
473 | 1 10 6da5a2eecb9c 000000000000 000000000000 | |
455 | 2 19 eb806e34ef6b 0dd616bc7ab1 6da5a2eecb9c |
|
474 | 2 19 eb806e34ef6b 0dd616bc7ab1 6da5a2eecb9c | |
|
475 | ||||
|
476 | # Here the filelog based implementation is not looking at the rename | |||
|
477 | # information (because the file exist on both side). However the changelog | |||
|
478 | # based on works fine. We have different output. | |||
|
479 | ||||
456 | $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")' |
|
480 | $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")' | |
457 | M f |
|
481 | M f | |
|
482 | b (no-filelog !) | |||
458 | R b |
|
483 | R b | |
459 | $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")' |
|
484 | $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")' | |
460 | M f |
|
485 | M f | |
|
486 | b (no-filelog !) | |||
461 | R b |
|
487 | R b | |
462 | $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")' |
|
488 | $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")' | |
463 | M f |
|
489 | M f | |
|
490 | d (no-filelog !) | |||
464 | R d |
|
491 | R d | |
465 | $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")' |
|
492 | $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")' | |
466 | M f |
|
493 | M f | |
|
494 | d (no-filelog !) | |||
467 | R d |
|
495 | R d | |
468 | $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")' |
|
496 | $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")' | |
469 | A f |
|
497 | A f | |
@@ -473,6 +501,18 b' Comparing with a merge with colliding re' | |||||
473 | A f |
|
501 | A f | |
474 | b |
|
502 | b | |
475 | R b |
|
503 | R b | |
|
504 | ||||
|
505 | # From here, we run status against revision where both source file exists. | |||
|
506 | # | |||
|
507 | # The filelog based implementation picks an arbitrary side based on revision | |||
|
508 | # numbers. So the same side "wins" whatever the parents order is. This is | |||
|
509 | # sub-optimal because depending on revision numbers means the result can be | |||
|
510 | # different from one repository to the next. | |||
|
511 | # | |||
|
512 | # The changeset based algorithm use the parent order to break tie on conflicting | |||
|
513 | # information and will have a different order depending on who is p1 and p2. | |||
|
514 | # That order is stable accross repositories. (data from p1 prevails) | |||
|
515 | ||||
476 | $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")' |
|
516 | $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")' | |
477 | A f |
|
517 | A f | |
478 | d |
|
518 | d | |
@@ -480,7 +520,8 b' Comparing with a merge with colliding re' | |||||
480 | R d |
|
520 | R d | |
481 | $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")' |
|
521 | $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")' | |
482 | A f |
|
522 | A f | |
483 | d |
|
523 | d (filelog !) | |
|
524 | b (no-filelog !) | |||
484 | R b |
|
525 | R b | |
485 | R d |
|
526 | R d | |
486 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")' |
|
527 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")' | |
@@ -490,7 +531,8 b' Comparing with a merge with colliding re' | |||||
490 | R b |
|
531 | R b | |
491 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")' |
|
532 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")' | |
492 | A f |
|
533 | A f | |
493 | a |
|
534 | a (filelog !) | |
|
535 | b (no-filelog !) | |||
494 | R a |
|
536 | R a | |
495 | R b |
|
537 | R b | |
496 |
|
538 | |||
@@ -563,21 +605,25 b' The overwriting should take over. Howeve' | |||||
563 | R h |
|
605 | R h | |
564 | $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")' |
|
606 | $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")' | |
565 | M d |
|
607 | M d | |
|
608 | h (no-filelog !) | |||
566 | R h |
|
609 | R h | |
567 | $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")' |
|
610 | $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")' | |
568 | M b |
|
611 | M b | |
569 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")' |
|
612 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")' | |
570 | M b |
|
613 | M b | |
571 | M d |
|
614 | M d | |
|
615 | i (no-filelog !) | |||
572 | R i |
|
616 | R i | |
573 | $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")' |
|
617 | $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")' | |
574 | M d |
|
618 | M d | |
|
619 | h (no-filelog !) | |||
575 | R h |
|
620 | R h | |
576 | $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")' |
|
621 | $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")' | |
577 | M b |
|
622 | M b | |
578 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")' |
|
623 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")' | |
579 | M b |
|
624 | M b | |
580 | M d |
|
625 | M d | |
|
626 | i (no-filelog !) | |||
581 | R i |
|
627 | R i | |
582 |
|
628 | |||
583 | The following graphlog is wrong, the "a -> c -> d" chain was overwritten and should not appear. |
|
629 | The following graphlog is wrong, the "a -> c -> d" chain was overwritten and should not appear. | |
@@ -645,9 +691,15 b' consider history and rename on both bran' | |||||
645 | | |
|
691 | | | |
646 | o 0 i-0 initial commit: a b h |
|
692 | o 0 i-0 initial commit: a b h | |
647 |
|
693 | |||
|
694 | One side of the merge have a long history with rename. The other side of the | |||
|
695 | merge point to a new file with a smaller history. Each side is "valid". | |||
|
696 | ||||
|
697 | (and again the filelog based algorithm only explore one, with a pick based on | |||
|
698 | revision numbers) | |||
|
699 | ||||
648 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")' |
|
700 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")' | |
649 | A d |
|
701 | A d | |
650 | a |
|
702 | a (filelog !) | |
651 | R a |
|
703 | R a | |
652 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")' |
|
704 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")' | |
653 | A d |
|
705 | A d | |
@@ -740,7 +792,8 b' Note:' | |||||
740 |
|
792 | |||
741 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")' |
|
793 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")' | |
742 | A d |
|
794 | A d | |
743 | a |
|
795 | h (no-filelog !) | |
|
796 | a (filelog !) | |||
744 | R a |
|
797 | R a | |
745 | R h |
|
798 | R h | |
746 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")' |
|
799 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")' | |
@@ -754,15 +807,19 b' Note:' | |||||
754 | M d |
|
807 | M d | |
755 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")' |
|
808 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")' | |
756 | M d |
|
809 | M d | |
|
810 | i (no-filelog !) | |||
757 | R i |
|
811 | R i | |
758 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")' |
|
812 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")' | |
759 | M d |
|
813 | M d | |
|
814 | i (no-filelog !) | |||
760 | R i |
|
815 | R i | |
761 | $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")' |
|
816 | $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")' | |
762 | M d |
|
817 | M d | |
|
818 | h (no-filelog !) | |||
763 | R h |
|
819 | R h | |
764 | $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")' |
|
820 | $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")' | |
765 | M d |
|
821 | M d | |
|
822 | h (no-filelog !) | |||
766 | R h |
|
823 | R h | |
767 |
|
824 | |||
768 | $ hg log -Gfr 'desc("mFGm-0")' d |
|
825 | $ hg log -Gfr 'desc("mFGm-0")' d |
General Comments 0
You need to be logged in to leave comments.
Login now