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: