Show More
@@ -183,10 +183,27 def _revinfogetter(repo): | |||
|
183 | 183 | * p1copies: mapping of copies from p1 |
|
184 | 184 | * p2copies: mapping of copies from p2 |
|
185 | 185 | * removed: a list of removed files |
|
186 | * ismerged: a callback to know if file was merged in that revision | |
|
186 | 187 | """ |
|
187 | 188 | cl = repo.changelog |
|
188 | 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 | 207 | if repo.filecopiesmode == b'changeset-sidedata': |
|
191 | 208 | changelogrevision = cl.changelogrevision |
|
192 | 209 | flags = cl.flags |
@@ -218,6 +235,7 def _revinfogetter(repo): | |||
|
218 | 235 | |
|
219 | 236 | def revinfo(rev): |
|
220 | 237 | p1, p2 = parents(rev) |
|
238 | value = None | |
|
221 | 239 | if flags(rev) & REVIDX_SIDEDATA: |
|
222 | 240 | e = merge_caches.pop(rev, None) |
|
223 | 241 | if e is not None: |
@@ -228,12 +246,22 def _revinfogetter(repo): | |||
|
228 | 246 | removed = c.filesremoved |
|
229 | 247 | if p1 != node.nullrev and p2 != node.nullrev: |
|
230 | 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 | 257 | else: |
|
233 | 258 | p1copies = {} |
|
234 | 259 | p2copies = {} |
|
235 | 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 | 266 | else: |
|
239 | 267 | |
@@ -242,7 +270,7 def _revinfogetter(repo): | |||
|
242 | 270 | ctx = repo[rev] |
|
243 | 271 | p1copies, p2copies = ctx._copies |
|
244 | 272 | removed = ctx.filesremoved() |
|
245 | return p1, p2, p1copies, p2copies, removed | |
|
273 | return p1, p2, p1copies, p2copies, removed, get_ismerged(rev) | |
|
246 | 274 | |
|
247 | 275 | return revinfo |
|
248 | 276 | |
@@ -256,6 +284,7 def _changesetforwardcopies(a, b, match) | |||
|
256 | 284 | revinfo = _revinfogetter(repo) |
|
257 | 285 | |
|
258 | 286 | cl = repo.changelog |
|
287 | isancestor = cl.isancestorrev # XXX we should had chaching to this. | |
|
259 | 288 | missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()]) |
|
260 | 289 | mrset = set(missingrevs) |
|
261 | 290 | roots = set() |
@@ -283,10 +312,14 def _changesetforwardcopies(a, b, match) | |||
|
283 | 312 | iterrevs.update(roots) |
|
284 | 313 | iterrevs.remove(b.rev()) |
|
285 | 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 | 323 | """combine the copies information for each item of iterrevs |
|
291 | 324 | |
|
292 | 325 | revs: sorted iterable of revision to visit |
@@ -305,7 +338,7 def _combinechangesetcopies(revs, childr | |||
|
305 | 338 | # this is a root |
|
306 | 339 | copies = {} |
|
307 | 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 | 342 | if r == p1: |
|
310 | 343 | parent = 1 |
|
311 | 344 | childcopies = p1copies |
@@ -319,9 +352,12 def _combinechangesetcopies(revs, childr | |||
|
319 | 352 | } |
|
320 | 353 | newcopies = copies |
|
321 | 354 | if childcopies: |
|
322 |
newcopies = |
|
|
323 | # _chain makes a copies, we can avoid doing so in some | |
|
324 | # simple/linear cases. | |
|
355 | newcopies = copies.copy() | |
|
356 | for dest, source in pycompat.iteritems(childcopies): | |
|
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 | 361 | assert newcopies is not copies |
|
326 | 362 | for f in removed: |
|
327 | 363 | if f in newcopies: |
@@ -330,7 +366,7 def _combinechangesetcopies(revs, childr | |||
|
330 | 366 | # branches. when there are no other branches, this |
|
331 | 367 | # could be avoided. |
|
332 | 368 | newcopies = copies.copy() |
|
333 |
|
|
|
369 | newcopies[f] = (c, None) | |
|
334 | 370 | othercopies = all_copies.get(c) |
|
335 | 371 | if othercopies is None: |
|
336 | 372 | all_copies[c] = newcopies |
@@ -338,21 +374,55 def _combinechangesetcopies(revs, childr | |||
|
338 | 374 | # we are the second parent to work on c, we need to merge our |
|
339 | 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 | 377 | # In case of conflict, parent 1 take precedence over parent 2. |
|
347 | 378 | # This is an arbitrary choice made anew when implementing |
|
348 | 379 | # changeset based copies. It was made without regards with |
|
349 | 380 | # potential filelog related behavior. |
|
350 | 381 | if parent == 1: |
|
351 |
|
|
|
382 | _merge_copies_dict( | |
|
383 | othercopies, newcopies, isancestor, ismerged | |
|
384 | ) | |
|
352 | 385 | else: |
|
353 |
|
|
|
386 | _merge_copies_dict( | |
|
387 | newcopies, othercopies, isancestor, ismerged | |
|
388 | ) | |
|
354 | 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 | 428 | def _forwardcopies(a, b, base=None, match=None): |
@@ -1,3 +1,5 | |||
|
1 | #testcases filelog compatibility sidedata | |
|
2 | ||
|
1 | 3 | ===================================================== |
|
2 | 4 | Test Copy tracing for chain of copies involving merge |
|
3 | 5 | ===================================================== |
@@ -6,6 +8,7 This test files covers copies/rename cas | |||
|
6 | 8 | are involved. It cheks we do not have unwanted update of behavior and that the |
|
7 | 9 | different options to retrieve copies behave correctly. |
|
8 | 10 | |
|
11 | ||
|
9 | 12 | Setup |
|
10 | 13 | ===== |
|
11 | 14 | |
@@ -18,6 +21,22 use git diff to see rename | |||
|
18 | 21 | > logtemplate={rev} {desc}\n |
|
19 | 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 | 40 | $ hg init repo-chain |
|
22 | 41 | $ cd repo-chain |
|
23 | 42 | |
@@ -453,17 +472,26 Comparing with a merge with colliding re | |||
|
453 | 472 | 0 4 0dd616bc7ab1 000000000000 000000000000 |
|
454 | 473 | 1 10 6da5a2eecb9c 000000000000 000000000000 |
|
455 | 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 | 480 | $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")' |
|
457 | 481 | M f |
|
482 | b (no-filelog !) | |
|
458 | 483 | R b |
|
459 | 484 | $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")' |
|
460 | 485 | M f |
|
486 | b (no-filelog !) | |
|
461 | 487 | R b |
|
462 | 488 | $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")' |
|
463 | 489 | M f |
|
490 | d (no-filelog !) | |
|
464 | 491 | R d |
|
465 | 492 | $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")' |
|
466 | 493 | M f |
|
494 | d (no-filelog !) | |
|
467 | 495 | R d |
|
468 | 496 | $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")' |
|
469 | 497 | A f |
@@ -473,6 +501,18 Comparing with a merge with colliding re | |||
|
473 | 501 | A f |
|
474 | 502 | b |
|
475 | 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 | 516 | $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")' |
|
477 | 517 | A f |
|
478 | 518 | d |
@@ -480,7 +520,8 Comparing with a merge with colliding re | |||
|
480 | 520 | R d |
|
481 | 521 | $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")' |
|
482 | 522 | A f |
|
483 | d | |
|
523 | d (filelog !) | |
|
524 | b (no-filelog !) | |
|
484 | 525 | R b |
|
485 | 526 | R d |
|
486 | 527 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")' |
@@ -490,7 +531,8 Comparing with a merge with colliding re | |||
|
490 | 531 | R b |
|
491 | 532 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")' |
|
492 | 533 | A f |
|
493 | a | |
|
534 | a (filelog !) | |
|
535 | b (no-filelog !) | |
|
494 | 536 | R a |
|
495 | 537 | R b |
|
496 | 538 | |
@@ -563,21 +605,25 The overwriting should take over. Howeve | |||
|
563 | 605 | R h |
|
564 | 606 | $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")' |
|
565 | 607 | M d |
|
608 | h (no-filelog !) | |
|
566 | 609 | R h |
|
567 | 610 | $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")' |
|
568 | 611 | M b |
|
569 | 612 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")' |
|
570 | 613 | M b |
|
571 | 614 | M d |
|
615 | i (no-filelog !) | |
|
572 | 616 | R i |
|
573 | 617 | $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")' |
|
574 | 618 | M d |
|
619 | h (no-filelog !) | |
|
575 | 620 | R h |
|
576 | 621 | $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")' |
|
577 | 622 | M b |
|
578 | 623 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")' |
|
579 | 624 | M b |
|
580 | 625 | M d |
|
626 | i (no-filelog !) | |
|
581 | 627 | R i |
|
582 | 628 | |
|
583 | 629 | The following graphlog is wrong, the "a -> c -> d" chain was overwritten and should not appear. |
@@ -645,9 +691,15 consider history and rename on both bran | |||
|
645 | 691 | | |
|
646 | 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 | 700 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")' |
|
649 | 701 | A d |
|
650 | a | |
|
702 | a (filelog !) | |
|
651 | 703 | R a |
|
652 | 704 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")' |
|
653 | 705 | A d |
@@ -740,7 +792,8 Note: | |||
|
740 | 792 | |
|
741 | 793 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")' |
|
742 | 794 | A d |
|
743 | a | |
|
795 | h (no-filelog !) | |
|
796 | a (filelog !) | |
|
744 | 797 | R a |
|
745 | 798 | R h |
|
746 | 799 | $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")' |
@@ -754,15 +807,19 Note: | |||
|
754 | 807 | M d |
|
755 | 808 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")' |
|
756 | 809 | M d |
|
810 | i (no-filelog !) | |
|
757 | 811 | R i |
|
758 | 812 | $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")' |
|
759 | 813 | M d |
|
814 | i (no-filelog !) | |
|
760 | 815 | R i |
|
761 | 816 | $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")' |
|
762 | 817 | M d |
|
818 | h (no-filelog !) | |
|
763 | 819 | R h |
|
764 | 820 | $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")' |
|
765 | 821 | M d |
|
822 | h (no-filelog !) | |
|
766 | 823 | R h |
|
767 | 824 | |
|
768 | 825 | $ hg log -Gfr 'desc("mFGm-0")' d |
General Comments 0
You need to be logged in to leave comments.
Login now