Mercurial > hg
comparison hgext/largefiles/overrides.py @ 23529:38e55e55ae4d
largefiles: rewrite merge code using dictionary with entry per file
In overridecalculateupdates(), we currently only deal with conflicts
that result in a 'g' action for either the largefile or a standin. We
will soon want to deal cases with 'cd' and 'dc' actions here. It will
be easier to reason about such cases if we rewrite it using a dict
from filename to action.
A side-effect of this change is that the output can only have one
action per file (which should be a good change). Before this change,
when one of the tests in test-issue3084 received this input (the 'a'
in the input was a result of 'cd' conflict resolved in favor of the
modified file):
'g': [('.hglf/f', ('',), 'remote created')],
'a': [('f', None, 'prompt keep')],
and the user chose to keep the local largefile, it produced this
output:
'g': [('.hglf/f', ('',), 'remote created')],
'r': [('f', None, 'replaced by standin')],
'a': [('f', None, 'prompt keep')],
Although 'a' actions are processed after 'r' actions by
recordupdates(), it still worked because 'a' actions have no effect on
merges (only on updates). After this change, the output is:
'g': [('.hglf/f', ('',), 'remote created')],
'r': [('f', None, 'replaced by standin')],
Similarly, there are several tests in test-largefiles-update that get
inputs like:
'a': [('.hglf/large2', None, 'prompt keep')],
'g': [('large2', ('',), 'remote created')],
and when the user chooses to keep the local largefile, they produce
this output:
'a': [('.hglf/large2', None, 'prompt keep'),
('.hglf/large2', None, 'keep standin')],
'lfmr': [('large2', None, 'forget non-standin largefile')],
In this case, it was not a merge but an update, so the 'a' action does
have an effect. However, since dirstate.add() is idempotent, it still
has no obserable effect.
After this change, the output is:
'a': [('.hglf/large2', None, 'keep standin')],
'lfmr': [('large2', None, 'forget non-standin largefile')],
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Tue, 09 Dec 2014 22:03:53 -0800 |
parents | 5a6d85bae97f |
children | 42ae1b1f048f |
comparison
equal
deleted
inserted
replaced
23528:5a6d85bae97f | 23529:38e55e55ae4d |
---|---|
423 followcopies) | 423 followcopies) |
424 | 424 |
425 if overwrite: | 425 if overwrite: |
426 return actions, diverge, renamedelete | 426 return actions, diverge, renamedelete |
427 | 427 |
428 # Convert to dictionary with filename as key and action as value. | |
429 actionbyfile = {} | |
430 for m, l in actions.iteritems(): | |
431 for f, args, msg in l: | |
432 actionbyfile[f] = m, args, msg | |
433 | |
428 removes = set(a[0] for a in actions['r']) | 434 removes = set(a[0] for a in actions['r']) |
429 | 435 |
430 newglist = [] | |
431 lfmr = [] # LargeFiles: Mark as Removed | |
432 for action in actions['g']: | 436 for action in actions['g']: |
433 f, args, msg = action | 437 f, args, msg = action |
434 splitstandin = f and lfutil.splitstandin(f) | 438 splitstandin = f and lfutil.splitstandin(f) |
435 if (splitstandin is not None and | 439 if (splitstandin is not None and |
436 splitstandin in p1 and splitstandin not in removes): | 440 splitstandin in p1 and splitstandin not in removes): |
440 standin = f | 444 standin = f |
441 usermsg = _('remote turned local normal file %s into a largefile\n' | 445 usermsg = _('remote turned local normal file %s into a largefile\n' |
442 'use (l)argefile or keep (n)ormal file?' | 446 'use (l)argefile or keep (n)ormal file?' |
443 '$$ &Largefile $$ &Normal file') % lfile | 447 '$$ &Largefile $$ &Normal file') % lfile |
444 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile | 448 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile |
445 actions['r'].append((lfile, None, 'replaced by standin')) | 449 actionbyfile[lfile] = ('r', None, 'replaced by standin') |
446 newglist.append(action) | |
447 else: # keep local normal file | 450 else: # keep local normal file |
448 if branchmerge: | 451 if branchmerge: |
449 actions['k'].append((standin, None, | 452 actionbyfile[standin] = ('k', None, |
450 'replaced by non-standin')) | 453 'replaced by non-standin') |
451 else: | 454 else: |
452 actions['r'].append((standin, None, | 455 actionbyfile[standin] = ('r', None, |
453 'replaced by non-standin')) | 456 'replaced by non-standin') |
454 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes: | 457 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes: |
455 # Case 2: largefile in the working copy, normal file in | 458 # Case 2: largefile in the working copy, normal file in |
456 # the second parent | 459 # the second parent |
457 standin = lfutil.standin(f) | 460 standin = lfutil.standin(f) |
458 lfile = f | 461 lfile = f |
460 'keep (l)argefile or use (n)ormal file?' | 463 'keep (l)argefile or use (n)ormal file?' |
461 '$$ &Largefile $$ &Normal file') % lfile | 464 '$$ &Largefile $$ &Normal file') % lfile |
462 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile | 465 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile |
463 if branchmerge: | 466 if branchmerge: |
464 # largefile can be restored from standin safely | 467 # largefile can be restored from standin safely |
465 actions['k'].append((lfile, None, 'replaced by standin')) | 468 actionbyfile[lfile] = ('k', None, 'replaced by standin') |
466 else: | 469 else: |
467 # "lfile" should be marked as "removed" without | 470 # "lfile" should be marked as "removed" without |
468 # removal of itself | 471 # removal of itself |
469 lfmr.append((lfile, None, 'forget non-standin largefile')) | 472 actionbyfile[lfile] = ('lfmr', None, |
473 'forget non-standin largefile') | |
470 | 474 |
471 # linear-merge should treat this largefile as 're-added' | 475 # linear-merge should treat this largefile as 're-added' |
472 actions['a'].append((standin, None, 'keep standin')) | 476 actionbyfile[standin] = ('a', None, 'keep standin') |
473 else: # pick remote normal file | 477 else: # pick remote normal file |
474 actions['r'].append((standin, None, 'replaced by non-standin')) | 478 actionbyfile[standin] = ('r', None, 'replaced by non-standin') |
475 newglist.append(action) | 479 |
476 else: | 480 # Convert back to dictionary-of-lists format |
477 newglist.append(action) | 481 for l in actions.itervalues(): |
478 | 482 l[:] = [] |
479 actions['g'] = newglist | 483 actions['lfmr'] = [] |
480 if lfmr: | 484 for f, (m, args, msg) in actionbyfile.iteritems(): |
481 actions['lfmr'] = lfmr | 485 actions[m].append((f, args, msg)) |
482 | 486 |
483 return actions, diverge, renamedelete | 487 return actions, diverge, renamedelete |
484 | 488 |
485 def mergerecordupdates(orig, repo, actions, branchmerge): | 489 def mergerecordupdates(orig, repo, actions, branchmerge): |
486 if 'lfmr' in actions: | 490 if 'lfmr' in actions: |