We want to use mergeresult object at more and more places instead of this
actions dict to simplify code and further add new APIs to mergeresult object.
This patch introduces addfile() which adds a new file to the internal actions
dict for now.
indygreg |
hg-reviewers |
We want to use mergeresult object at more and more places instead of this
actions dict to simplify code and further add new APIs to mergeresult object.
This patch introduces addfile() which adds a new file to the internal actions
dict for now.
No Linters Available |
No Unit Test Coverage |
Oh, yay - no more indirect mutation of an internal attribute. I really like where this is going...
Path | Packages | |||
---|---|---|---|---|
M | hgext/largefiles/overrides.py (42 lines) | |||
M | mercurial/merge.py (137 lines) |
Commit | Parents | Author | Summary | Date |
---|---|---|---|---|
9b0f612c41a6 | 3fbc59f2adf7 | Pulkit Goyal | Jul 24 2020, 6:48 AM |
Status | Author | Revision | |
---|---|---|---|
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit | ||
Closed | pulkit |
_( | _( | ||||
b'remote turned local normal file %s into a largefile\n' | b'remote turned local normal file %s into a largefile\n' | ||||
b'use (l)argefile or keep (n)ormal file?' | b'use (l)argefile or keep (n)ormal file?' | ||||
b'$$ &Largefile $$ &Normal file' | b'$$ &Largefile $$ &Normal file' | ||||
) | ) | ||||
% lfile | % lfile | ||||
) | ) | ||||
if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile | if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile | ||||
mresult.actions[lfile] = (b'r', None, b'replaced by standin') | mresult.addfile(lfile, b'r', None, b'replaced by standin') | ||||
mresult.actions[standin] = (b'g', sargs, b'replaces standin') | mresult.addfile(standin, b'g', sargs, b'replaces standin') | ||||
else: # keep local normal file | else: # keep local normal file | ||||
mresult.actions[lfile] = (b'k', None, b'replaces standin') | mresult.addfile(lfile, b'k', None, b'replaces standin') | ||||
if branchmerge: | if branchmerge: | ||||
mresult.actions[standin] = ( | mresult.addfile( | ||||
b'k', | standin, b'k', None, b'replaced by non-standin', | ||||
None, | |||||
b'replaced by non-standin', | |||||
) | ) | ||||
else: | else: | ||||
mresult.actions[standin] = ( | mresult.addfile( | ||||
b'r', | standin, b'r', None, b'replaced by non-standin', | ||||
None, | |||||
b'replaced by non-standin', | |||||
) | ) | ||||
elif lm in (b'g', b'dc') and sm != b'r': | elif lm in (b'g', b'dc') and sm != b'r': | ||||
if lm == b'dc': | if lm == b'dc': | ||||
f1, f2, fa, move, anc = largs | f1, f2, fa, move, anc = largs | ||||
largs = (p2[f2].flags(), False) | largs = (p2[f2].flags(), False) | ||||
# Case 2: largefile in the working copy, normal file in | # Case 2: largefile in the working copy, normal file in | ||||
# the second parent | # the second parent | ||||
usermsg = ( | usermsg = ( | ||||
_( | _( | ||||
b'remote turned local largefile %s into a normal file\n' | b'remote turned local largefile %s into a normal file\n' | ||||
b'keep (l)argefile or use (n)ormal file?' | b'keep (l)argefile or use (n)ormal file?' | ||||
b'$$ &Largefile $$ &Normal file' | b'$$ &Largefile $$ &Normal file' | ||||
) | ) | ||||
% lfile | % lfile | ||||
) | ) | ||||
if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile | if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile | ||||
if branchmerge: | if branchmerge: | ||||
# largefile can be restored from standin safely | # largefile can be restored from standin safely | ||||
mresult.actions[lfile] = ( | mresult.addfile( | ||||
b'k', | lfile, b'k', None, b'replaced by standin', | ||||
None, | |||||
b'replaced by standin', | |||||
) | ) | ||||
mresult.actions[standin] = (b'k', None, b'replaces standin') | mresult.addfile(standin, b'k', None, b'replaces standin') | ||||
else: | else: | ||||
# "lfile" should be marked as "removed" without | # "lfile" should be marked as "removed" without | ||||
# removal of itself | # removal of itself | ||||
mresult.actions[lfile] = ( | mresult.addfile( | ||||
b'lfmr', | lfile, b'lfmr', None, b'forget non-standin largefile', | ||||
None, | |||||
b'forget non-standin largefile', | |||||
) | ) | ||||
# linear-merge should treat this largefile as 're-added' | # linear-merge should treat this largefile as 're-added' | ||||
mresult.actions[standin] = (b'a', None, b'keep standin') | mresult.addfile(standin, b'a', None, b'keep standin') | ||||
else: # pick remote normal file | else: # pick remote normal file | ||||
mresult.actions[lfile] = (b'g', largs, b'replaces standin') | mresult.addfile(lfile, b'g', largs, b'replaces standin') | ||||
mresult.actions[standin] = ( | mresult.addfile( | ||||
b'r', | standin, b'r', None, b'replaced by non-standin', | ||||
None, | |||||
b'replaced by non-standin', | |||||
) | ) | ||||
return mresult | return mresult | ||||
@eh.wrapfunction(mergestatemod, b'recordupdates') | @eh.wrapfunction(mergestatemod, b'recordupdates') | ||||
def mergerecordupdates(orig, repo, actions, branchmerge, getfiledata): | def mergerecordupdates(orig, repo, actions, branchmerge, getfiledata): | ||||
if b'lfmr' in actions: | if b'lfmr' in actions: |
commitinfo: dict containing data which should be used on commit | commitinfo: dict containing data which should be used on commit | ||||
contains a filename -> info mapping | contains a filename -> info mapping | ||||
""" | """ | ||||
self._actions = {} | self._actions = {} | ||||
self._diverge = {} | self._diverge = {} | ||||
self._renamedelete = {} | self._renamedelete = {} | ||||
self._commitinfo = {} | self._commitinfo = {} | ||||
def updatevalues(self, actions, diverge, renamedelete, commitinfo): | def updatevalues(self, diverge, renamedelete, commitinfo): | ||||
self._actions = actions | |||||
self._diverge = diverge | self._diverge = diverge | ||||
self._renamedelete = renamedelete | self._renamedelete = renamedelete | ||||
self._commitinfo = commitinfo | self._commitinfo = commitinfo | ||||
def addfile(self, filename, action, data, message): | |||||
""" adds a new file to the mergeresult object | |||||
filename: file which we are adding | |||||
action: one of mergestatemod.ACTION_* | |||||
data: a tuple of information like fctx and ctx related to this merge | |||||
message: a message about the merge | |||||
""" | |||||
self._actions[filename] = (action, data, message) | |||||
@property | @property | ||||
def actions(self): | def actions(self): | ||||
return self._actions | return self._actions | ||||
@property | @property | ||||
def diverge(self): | def diverge(self): | ||||
return self._diverge | return self._diverge | ||||
Merge wctx and p2 with ancestor pa and generate merge action list | Merge wctx and p2 with ancestor pa and generate merge action list | ||||
branchmerge and force are as passed in to update | branchmerge and force are as passed in to update | ||||
matcher = matcher to filter file lists | matcher = matcher to filter file lists | ||||
acceptremote = accept the incoming changes without prompting | acceptremote = accept the incoming changes without prompting | ||||
Returns an object of mergeresult class | Returns an object of mergeresult class | ||||
""" | """ | ||||
mresult = mergeresult() | |||||
if matcher is not None and matcher.always(): | if matcher is not None and matcher.always(): | ||||
matcher = None | matcher = None | ||||
# manifests fetched in order are going to be faster, so prime the caches | # manifests fetched in order are going to be faster, so prime the caches | ||||
[ | [ | ||||
x.manifest() | x.manifest() | ||||
for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev) | for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev) | ||||
] | ] | ||||
relevantfiles.add(copykey) | relevantfiles.add(copykey) | ||||
for movedirkey in branch_copies1.movewithdir: | for movedirkey in branch_copies1.movewithdir: | ||||
relevantfiles.add(movedirkey) | relevantfiles.add(movedirkey) | ||||
filesmatcher = scmutil.matchfiles(repo, relevantfiles) | filesmatcher = scmutil.matchfiles(repo, relevantfiles) | ||||
matcher = matchmod.intersectmatchers(matcher, filesmatcher) | matcher = matchmod.intersectmatchers(matcher, filesmatcher) | ||||
diff = m1.diff(m2, match=matcher) | diff = m1.diff(m2, match=matcher) | ||||
actions = {} | |||||
for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff): | for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff): | ||||
if n1 and n2: # file exists on both local and remote side | if n1 and n2: # file exists on both local and remote side | ||||
if f not in ma: | if f not in ma: | ||||
# TODO: what if they're renamed from different sources? | # TODO: what if they're renamed from different sources? | ||||
fa = branch_copies1.copy.get( | fa = branch_copies1.copy.get( | ||||
f, None | f, None | ||||
) or branch_copies2.copy.get(f, None) | ) or branch_copies2.copy.get(f, None) | ||||
if fa is not None: | if fa is not None: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_MERGE, | mergestatemod.ACTION_MERGE, | ||||
(f, f, fa, False, pa.node()), | (f, f, fa, False, pa.node()), | ||||
b'both renamed from %s' % fa, | b'both renamed from %s' % fa, | ||||
) | ) | ||||
else: | else: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_MERGE, | mergestatemod.ACTION_MERGE, | ||||
(f, f, None, False, pa.node()), | (f, f, None, False, pa.node()), | ||||
b'both created', | b'both created', | ||||
) | ) | ||||
else: | else: | ||||
a = ma[f] | a = ma[f] | ||||
fla = ma.flags(f) | fla = ma.flags(f) | ||||
nol = b'l' not in fl1 + fl2 + fla | nol = b'l' not in fl1 + fl2 + fla | ||||
if n2 == a and fl2 == fla: | if n2 == a and fl2 == fla: | ||||
actions[f] = ( | mresult.addfile( | ||||
mergestatemod.ACTION_KEEP, | f, mergestatemod.ACTION_KEEP, (), b'remote unchanged', | ||||
(), | |||||
b'remote unchanged', | |||||
) | ) | ||||
elif n1 == a and fl1 == fla: # local unchanged - use remote | elif n1 == a and fl1 == fla: # local unchanged - use remote | ||||
if n1 == n2: # optimization: keep local content | if n1 == n2: # optimization: keep local content | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_EXEC, | mergestatemod.ACTION_EXEC, | ||||
(fl2,), | (fl2,), | ||||
b'update permissions', | b'update permissions', | ||||
) | ) | ||||
else: | else: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_GET, | mergestatemod.ACTION_GET, | ||||
(fl2, False), | (fl2, False), | ||||
b'remote is newer', | b'remote is newer', | ||||
) | ) | ||||
if branchmerge: | if branchmerge: | ||||
commitinfo[f] = b'other' | commitinfo[f] = b'other' | ||||
elif nol and n2 == a: # remote only changed 'x' | elif nol and n2 == a: # remote only changed 'x' | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_EXEC, | mergestatemod.ACTION_EXEC, | ||||
(fl2,), | (fl2,), | ||||
b'update permissions', | b'update permissions', | ||||
) | ) | ||||
elif nol and n1 == a: # local only changed 'x' | elif nol and n1 == a: # local only changed 'x' | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_GET, | mergestatemod.ACTION_GET, | ||||
(fl1, False), | (fl1, False), | ||||
b'remote is newer', | b'remote is newer', | ||||
) | ) | ||||
if branchmerge: | if branchmerge: | ||||
commitinfo[f] = b'other' | commitinfo[f] = b'other' | ||||
else: # both changed something | else: # both changed something | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_MERGE, | mergestatemod.ACTION_MERGE, | ||||
(f, f, f, False, pa.node()), | (f, f, f, False, pa.node()), | ||||
b'versions differ', | b'versions differ', | ||||
) | ) | ||||
elif n1: # file exists only on local side | elif n1: # file exists only on local side | ||||
if f in copied2: | if f in copied2: | ||||
pass # we'll deal with it on m2 side | pass # we'll deal with it on m2 side | ||||
elif ( | elif ( | ||||
f in branch_copies1.movewithdir | f in branch_copies1.movewithdir | ||||
): # directory rename, move local | ): # directory rename, move local | ||||
f2 = branch_copies1.movewithdir[f] | f2 = branch_copies1.movewithdir[f] | ||||
if f2 in m2: | if f2 in m2: | ||||
actions[f2] = ( | mresult.addfile( | ||||
f2, | |||||
mergestatemod.ACTION_MERGE, | mergestatemod.ACTION_MERGE, | ||||
(f, f2, None, True, pa.node()), | (f, f2, None, True, pa.node()), | ||||
b'remote directory rename, both created', | b'remote directory rename, both created', | ||||
) | ) | ||||
else: | else: | ||||
actions[f2] = ( | mresult.addfile( | ||||
f2, | |||||
mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL, | mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL, | ||||
(f, fl1), | (f, fl1), | ||||
b'remote directory rename - move from %s' % f, | b'remote directory rename - move from %s' % f, | ||||
) | ) | ||||
elif f in branch_copies1.copy: | elif f in branch_copies1.copy: | ||||
f2 = branch_copies1.copy[f] | f2 = branch_copies1.copy[f] | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_MERGE, | mergestatemod.ACTION_MERGE, | ||||
(f, f2, f2, False, pa.node()), | (f, f2, f2, False, pa.node()), | ||||
b'local copied/moved from %s' % f2, | b'local copied/moved from %s' % f2, | ||||
) | ) | ||||
elif f in ma: # clean, a different, no remote | elif f in ma: # clean, a different, no remote | ||||
if n1 != ma[f]: | if n1 != ma[f]: | ||||
if acceptremote: | if acceptremote: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_REMOVE, | mergestatemod.ACTION_REMOVE, | ||||
None, | None, | ||||
b'remote delete', | b'remote delete', | ||||
) | ) | ||||
else: | else: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_CHANGED_DELETED, | mergestatemod.ACTION_CHANGED_DELETED, | ||||
(f, None, f, False, pa.node()), | (f, None, f, False, pa.node()), | ||||
b'prompt changed/deleted', | b'prompt changed/deleted', | ||||
) | ) | ||||
elif n1 == addednodeid: | elif n1 == addednodeid: | ||||
# This file was locally added. We should forget it instead of | # This file was locally added. We should forget it instead of | ||||
# deleting it. | # deleting it. | ||||
actions[f] = ( | mresult.addfile( | ||||
mergestatemod.ACTION_FORGET, | f, mergestatemod.ACTION_FORGET, None, b'remote deleted', | ||||
None, | |||||
b'remote deleted', | |||||
) | ) | ||||
else: | else: | ||||
actions[f] = ( | mresult.addfile( | ||||
mergestatemod.ACTION_REMOVE, | f, mergestatemod.ACTION_REMOVE, None, b'other deleted', | ||||
None, | |||||
b'other deleted', | |||||
) | ) | ||||
elif n2: # file exists only on remote side | elif n2: # file exists only on remote side | ||||
if f in copied1: | if f in copied1: | ||||
pass # we'll deal with it on m1 side | pass # we'll deal with it on m1 side | ||||
elif f in branch_copies2.movewithdir: | elif f in branch_copies2.movewithdir: | ||||
f2 = branch_copies2.movewithdir[f] | f2 = branch_copies2.movewithdir[f] | ||||
if f2 in m1: | if f2 in m1: | ||||
actions[f2] = ( | mresult.addfile( | ||||
f2, | |||||
mergestatemod.ACTION_MERGE, | mergestatemod.ACTION_MERGE, | ||||
(f2, f, None, False, pa.node()), | (f2, f, None, False, pa.node()), | ||||
b'local directory rename, both created', | b'local directory rename, both created', | ||||
) | ) | ||||
else: | else: | ||||
actions[f2] = ( | mresult.addfile( | ||||
f2, | |||||
mergestatemod.ACTION_LOCAL_DIR_RENAME_GET, | mergestatemod.ACTION_LOCAL_DIR_RENAME_GET, | ||||
(f, fl2), | (f, fl2), | ||||
b'local directory rename - get from %s' % f, | b'local directory rename - get from %s' % f, | ||||
) | ) | ||||
elif f in branch_copies2.copy: | elif f in branch_copies2.copy: | ||||
f2 = branch_copies2.copy[f] | f2 = branch_copies2.copy[f] | ||||
if f2 in m2: | if f2 in m2: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_MERGE, | mergestatemod.ACTION_MERGE, | ||||
(f2, f, f2, False, pa.node()), | (f2, f, f2, False, pa.node()), | ||||
b'remote copied from %s' % f2, | b'remote copied from %s' % f2, | ||||
) | ) | ||||
else: | else: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_MERGE, | mergestatemod.ACTION_MERGE, | ||||
(f2, f, f2, True, pa.node()), | (f2, f, f2, True, pa.node()), | ||||
b'remote moved from %s' % f2, | b'remote moved from %s' % f2, | ||||
) | ) | ||||
elif f not in ma: | elif f not in ma: | ||||
# local unknown, remote created: the logic is described by the | # local unknown, remote created: the logic is described by the | ||||
# following table: | # following table: | ||||
# | # | ||||
# force branchmerge different | action | # force branchmerge different | action | ||||
# n * * | create | # n * * | create | ||||
# y n * | create | # y n * | create | ||||
# y y n | create | # y y n | create | ||||
# y y y | merge | # y y y | merge | ||||
# | # | ||||
# Checking whether the files are different is expensive, so we | # Checking whether the files are different is expensive, so we | ||||
# don't do that when we can avoid it. | # don't do that when we can avoid it. | ||||
if not force: | if not force: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_CREATED, | mergestatemod.ACTION_CREATED, | ||||
(fl2,), | (fl2,), | ||||
b'remote created', | b'remote created', | ||||
) | ) | ||||
elif not branchmerge: | elif not branchmerge: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_CREATED, | mergestatemod.ACTION_CREATED, | ||||
(fl2,), | (fl2,), | ||||
b'remote created', | b'remote created', | ||||
) | ) | ||||
else: | else: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_CREATED_MERGE, | mergestatemod.ACTION_CREATED_MERGE, | ||||
(fl2, pa.node()), | (fl2, pa.node()), | ||||
b'remote created, get or merge', | b'remote created, get or merge', | ||||
) | ) | ||||
elif n2 != ma[f]: | elif n2 != ma[f]: | ||||
df = None | df = None | ||||
for d in branch_copies1.dirmove: | for d in branch_copies1.dirmove: | ||||
if f.startswith(d): | if f.startswith(d): | ||||
# new file added in a directory that was moved | # new file added in a directory that was moved | ||||
df = branch_copies1.dirmove[d] + f[len(d) :] | df = branch_copies1.dirmove[d] + f[len(d) :] | ||||
break | break | ||||
if df is not None and df in m1: | if df is not None and df in m1: | ||||
actions[df] = ( | mresult.addfile( | ||||
df, | |||||
mergestatemod.ACTION_MERGE, | mergestatemod.ACTION_MERGE, | ||||
(df, f, f, False, pa.node()), | (df, f, f, False, pa.node()), | ||||
b'local directory rename - respect move ' | b'local directory rename - respect move ' | ||||
b'from %s' % f, | b'from %s' % f, | ||||
) | ) | ||||
elif acceptremote: | elif acceptremote: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_CREATED, | mergestatemod.ACTION_CREATED, | ||||
(fl2,), | (fl2,), | ||||
b'remote recreating', | b'remote recreating', | ||||
) | ) | ||||
else: | else: | ||||
actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_DELETED_CHANGED, | mergestatemod.ACTION_DELETED_CHANGED, | ||||
(None, f, f, False, pa.node()), | (None, f, f, False, pa.node()), | ||||
b'prompt deleted/changed', | b'prompt deleted/changed', | ||||
) | ) | ||||
if repo.ui.configbool(b'experimental', b'merge.checkpathconflicts'): | if repo.ui.configbool(b'experimental', b'merge.checkpathconflicts'): | ||||
# If we are merging, look for path conflicts. | # If we are merging, look for path conflicts. | ||||
checkpathconflicts(repo, wctx, p2, actions) | checkpathconflicts(repo, wctx, p2, mresult.actions) | ||||
narrowmatch = repo.narrowmatch() | narrowmatch = repo.narrowmatch() | ||||
if not narrowmatch.always(): | if not narrowmatch.always(): | ||||
# Updates "actions" in place | # Updates "actions" in place | ||||
_filternarrowactions(narrowmatch, branchmerge, actions) | _filternarrowactions(narrowmatch, branchmerge, mresult.actions) | ||||
renamedelete = branch_copies1.renamedelete | renamedelete = branch_copies1.renamedelete | ||||
renamedelete.update(branch_copies2.renamedelete) | renamedelete.update(branch_copies2.renamedelete) | ||||
mresult = mergeresult() | mresult.updatevalues(diverge, renamedelete, commitinfo) | ||||
mresult.updatevalues(actions, diverge, renamedelete, commitinfo) | |||||
return mresult | return mresult | ||||
def _resolvetrivial(repo, wctx, mctx, ancestor, actions): | def _resolvetrivial(repo, wctx, mctx, ancestor, actions): | ||||
"""Resolves false conflicts where the nodeid changed but the content | """Resolves false conflicts where the nodeid changed but the content | ||||
remained the same.""" | remained the same.""" | ||||
# We force a copy of actions.items() because we're going to mutate | # We force a copy of actions.items() because we're going to mutate | ||||
# actions as we resolve trivial conflicts. | # actions as we resolve trivial conflicts. | ||||
else: | else: | ||||
d[m] = [a] | d[m] = [a] | ||||
else: | else: | ||||
fbids[f] = {m: [a]} | fbids[f] = {m: [a]} | ||||
# Call for bids | # Call for bids | ||||
# Pick the best bid for each file | # Pick the best bid for each file | ||||
repo.ui.note(_(b'\nauction for merging merge bids\n')) | repo.ui.note(_(b'\nauction for merging merge bids\n')) | ||||
actions = {} | mresult = mergeresult() | ||||
for f, bids in sorted(fbids.items()): | for f, bids in sorted(fbids.items()): | ||||
# bids is a mapping from action method to list af actions | # bids is a mapping from action method to list af actions | ||||
# Consensus? | # Consensus? | ||||
if len(bids) == 1: # all bids are the same kind of method | if len(bids) == 1: # all bids are the same kind of method | ||||
m, l = list(bids.items())[0] | m, l = list(bids.items())[0] | ||||
if all(a == l[0] for a in l[1:]): # len(bids) is > 1 | if all(a == l[0] for a in l[1:]): # len(bids) is > 1 | ||||
repo.ui.note(_(b" %s: consensus for %s\n") % (f, m)) | repo.ui.note(_(b" %s: consensus for %s\n") % (f, m)) | ||||
actions[f] = l[0] | mresult.addfile(f, *l[0]) | ||||
continue | continue | ||||
# If keep is an option, just do it. | # If keep is an option, just do it. | ||||
if mergestatemod.ACTION_KEEP in bids: | if mergestatemod.ACTION_KEEP in bids: | ||||
repo.ui.note(_(b" %s: picking 'keep' action\n") % f) | repo.ui.note(_(b" %s: picking 'keep' action\n") % f) | ||||
actions[f] = bids[mergestatemod.ACTION_KEEP][0] | mresult.addfile(f, *bids[mergestatemod.ACTION_KEEP][0]) | ||||
continue | continue | ||||
# If there are gets and they all agree [how could they not?], do it. | # If there are gets and they all agree [how could they not?], do it. | ||||
if mergestatemod.ACTION_GET in bids: | if mergestatemod.ACTION_GET in bids: | ||||
ga0 = bids[mergestatemod.ACTION_GET][0] | ga0 = bids[mergestatemod.ACTION_GET][0] | ||||
if all(a == ga0 for a in bids[mergestatemod.ACTION_GET][1:]): | if all(a == ga0 for a in bids[mergestatemod.ACTION_GET][1:]): | ||||
repo.ui.note(_(b" %s: picking 'get' action\n") % f) | repo.ui.note(_(b" %s: picking 'get' action\n") % f) | ||||
actions[f] = ga0 | mresult.addfile(f, *ga0) | ||||
continue | continue | ||||
# TODO: Consider other simple actions such as mode changes | # TODO: Consider other simple actions such as mode changes | ||||
# Handle inefficient democrazy. | # Handle inefficient democrazy. | ||||
repo.ui.note(_(b' %s: multiple bids for merge action:\n') % f) | repo.ui.note(_(b' %s: multiple bids for merge action:\n') % f) | ||||
for m, l in sorted(bids.items()): | for m, l in sorted(bids.items()): | ||||
for _f, args, msg in l: | for _f, args, msg in l: | ||||
repo.ui.note(b' %s -> %s\n' % (msg, m)) | repo.ui.note(b' %s -> %s\n' % (msg, m)) | ||||
# Pick random action. TODO: Instead, prompt user when resolving | # Pick random action. TODO: Instead, prompt user when resolving | ||||
m, l = list(bids.items())[0] | m, l = list(bids.items())[0] | ||||
repo.ui.warn( | repo.ui.warn( | ||||
_(b' %s: ambiguous merge - picked %s action\n') % (f, m) | _(b' %s: ambiguous merge - picked %s action\n') % (f, m) | ||||
) | ) | ||||
actions[f] = l[0] | mresult.addfile(f, *l[0]) | ||||
continue | continue | ||||
repo.ui.note(_(b'end of auction\n\n')) | repo.ui.note(_(b'end of auction\n\n')) | ||||
# TODO: think about commitinfo when bid merge is used | # TODO: think about commitinfo when bid merge is used | ||||
mresult = mergeresult() | mresult.updatevalues(diverge, renamedelete, {}) | ||||
mresult.updatevalues(actions, diverge, renamedelete, {}) | |||||
if wctx.rev() is None: | if wctx.rev() is None: | ||||
fractions = _forgetremoved(wctx, mctx, branchmerge) | fractions = _forgetremoved(wctx, mctx, branchmerge) | ||||
mresult.actions.update(fractions) | mresult.actions.update(fractions) | ||||
prunedactions = sparse.filterupdatesactions( | prunedactions = sparse.filterupdatesactions( | ||||
repo, wctx, mctx, branchmerge, mresult.actions | repo, wctx, mctx, branchmerge, mresult.actions | ||||
) | ) | ||||
_( | _( | ||||
b"local%(l)s changed %(f)s which other%(o)s deleted\n" | b"local%(l)s changed %(f)s which other%(o)s deleted\n" | ||||
b"use (c)hanged version or (d)elete?" | b"use (c)hanged version or (d)elete?" | ||||
b"$$ &Changed $$ &Delete" | b"$$ &Changed $$ &Delete" | ||||
) | ) | ||||
% prompts, | % prompts, | ||||
0, | 0, | ||||
): | ): | ||||
mresult.actions[f] = ( | mresult.addfile( | ||||
mergestatemod.ACTION_REMOVE, | f, mergestatemod.ACTION_REMOVE, None, b'prompt delete', | ||||
None, | |||||
b'prompt delete', | |||||
) | ) | ||||
elif f in p1: | elif f in p1: | ||||
mresult.actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_ADD_MODIFIED, | mergestatemod.ACTION_ADD_MODIFIED, | ||||
None, | None, | ||||
b'prompt keep', | b'prompt keep', | ||||
) | ) | ||||
else: | else: | ||||
mresult.actions[f] = ( | mresult.addfile( | ||||
mergestatemod.ACTION_ADD, | f, mergestatemod.ACTION_ADD, None, b'prompt keep', | ||||
None, | |||||
b'prompt keep', | |||||
) | ) | ||||
elif m == mergestatemod.ACTION_DELETED_CHANGED: | elif m == mergestatemod.ACTION_DELETED_CHANGED: | ||||
f1, f2, fa, move, anc = args | f1, f2, fa, move, anc = args | ||||
flags = p2[f2].flags() | flags = p2[f2].flags() | ||||
if ( | if ( | ||||
repo.ui.promptchoice( | repo.ui.promptchoice( | ||||
_( | _( | ||||
b"other%(o)s changed %(f)s which local%(l)s deleted\n" | b"other%(o)s changed %(f)s which local%(l)s deleted\n" | ||||
b"use (c)hanged version or leave (d)eleted?" | b"use (c)hanged version or leave (d)eleted?" | ||||
b"$$ &Changed $$ &Deleted" | b"$$ &Changed $$ &Deleted" | ||||
) | ) | ||||
% prompts, | % prompts, | ||||
0, | 0, | ||||
) | ) | ||||
== 0 | == 0 | ||||
): | ): | ||||
mresult.actions[f] = ( | mresult.addfile( | ||||
f, | |||||
mergestatemod.ACTION_GET, | mergestatemod.ACTION_GET, | ||||
(flags, False), | (flags, False), | ||||
b'prompt recreating', | b'prompt recreating', | ||||
) | ) | ||||
else: | else: | ||||
del mresult.actions[f] | del mresult.actions[f] | ||||
# Convert to dictionary-of-lists format | # Convert to dictionary-of-lists format |