This open the way for having "smarter" value as action, making the usage code
simpler and more flexible.
We have to explicitly use bytes call in a couple of place because Python2…
( )
Alphare |
hg-reviewers |
This open the way for having "smarter" value as action, making the usage code
simpler and more flexible.
We have to explicitly use bytes call in a couple of place because Python2…
Automatic diff as part of commit; lint not applicable. |
Automatic diff as part of commit; unit tests not applicable. |
Path | Packages | |||
---|---|---|---|---|
M | hgext/largefiles/overrides.py (2 lines) | |||
M | mercurial/merge.py (15 lines) | |||
M | mercurial/mergestate.py (66 lines) |
Status | Author | Revision | |
---|---|---|---|
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute | ||
Closed | marmoute |
ACTION_GET = mergestatemod.ACTION_GET | ACTION_GET = mergestatemod.ACTION_GET | ||||
ACTION_KEEP = mergestatemod.ACTION_KEEP | ACTION_KEEP = mergestatemod.ACTION_KEEP | ||||
ACTION_REMOVE = mergestatemod.ACTION_REMOVE | ACTION_REMOVE = mergestatemod.ACTION_REMOVE | ||||
eh = exthelper.exthelper() | eh = exthelper.exthelper() | ||||
lfstatus = lfutil.lfstatus | lfstatus = lfutil.lfstatus | ||||
MERGE_ACTION_LARGEFILE_MARK_REMOVED = b'lfmr' | MERGE_ACTION_LARGEFILE_MARK_REMOVED = mergestatemod.MergeAction('lfmr') | ||||
# -- Utility functions: commonly/repeatedly needed functionality --------------- | # -- Utility functions: commonly/repeatedly needed functionality --------------- | ||||
def composelargefilematcher(match, manifest): | def composelargefilematcher(match, manifest): | ||||
"""create a matcher that matches only the largefiles in the original | """create a matcher that matches only the largefiles in the original | ||||
matcher""" | matcher""" | ||||
m = copy.copy(match) | m = copy.copy(match) |
# blindly update final mergeresult commitinfo with what we get | # blindly update final mergeresult commitinfo with what we get | ||||
# from mergeresult object for each ancestor | # from mergeresult object for each ancestor | ||||
# TODO: some commitinfo depends on what bid merge choose and hence | # TODO: some commitinfo depends on what bid merge choose and hence | ||||
# we will need to make commitinfo also depend on bid merge logic | # we will need to make commitinfo also depend on bid merge logic | ||||
mresult._commitinfo.update(mresult1._commitinfo) | mresult._commitinfo.update(mresult1._commitinfo) | ||||
for f, a in mresult1.filemap(sort=True): | for f, a in mresult1.filemap(sort=True): | ||||
m, args, msg = a | m, args, msg = a | ||||
repo.ui.debug(b' %s: %s -> %s\n' % (f, msg, m)) | repo.ui.debug(b' %s: %s -> %s\n' % (f, msg, m.__bytes__())) | ||||
if f in fbids: | if f in fbids: | ||||
d = fbids[f] | d = fbids[f] | ||||
if m in d: | if m in d: | ||||
d[m].append(a) | d[m].append(a) | ||||
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( | repo.ui.note( | ||||
_(b'\nauction for merging merge bids (%d ancestors)\n') | _(b'\nauction for merging merge bids (%d ancestors)\n') | ||||
% len(ancestors) | % len(ancestors) | ||||
) | ) | ||||
for f, bids in sorted(fbids.items()): | for f, bids in sorted(fbids.items()): | ||||
if repo.ui.debugflag: | if repo.ui.debugflag: | ||||
repo.ui.debug(b" list of bids for %s:\n" % f) | repo.ui.debug(b" list of bids for %s:\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.debug(b' %s -> %s\n' % (msg, m)) | repo.ui.debug(b' %s -> %s\n' % (msg, m.__bytes__())) | ||||
# 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.__bytes__()) | |||||
) | |||||
mresult.addfile(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) | ||||
mresult.addfile(f, *bids[mergestatemod.ACTION_KEEP][0]) | mresult.addfile(f, *bids[mergestatemod.ACTION_KEEP][0]) | ||||
continue | continue | ||||
# If keep absent is an option, just do that | # If keep absent is an option, just do that | ||||
repo.ui.note(_(b" %s: picking 'get' action\n") % f) | repo.ui.note(_(b" %s: picking 'get' action\n") % f) | ||||
mresult.addfile(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.__bytes__())) | ||||
# 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.__bytes__()) | |||||
) | ) | ||||
mresult.addfile(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')) | ||||
mresult.updatevalues(diverge, renamedelete) | mresult.updatevalues(diverge, renamedelete) | ||||
if wctx.rev() is None: | if wctx.rev() is None: | ||||
_forgetremoved(wctx, mctx, branchmerge, mresult) | _forgetremoved(wctx, mctx, branchmerge, mresult) | ||||
(mergestatemod.ACTION_ADD_MODIFIED,), sort=True | (mergestatemod.ACTION_ADD_MODIFIED,), sort=True | ||||
): | ): | ||||
repo.ui.debug(b" %s: %s -> am\n" % (f, msg)) | repo.ui.debug(b" %s: %s -> am\n" % (f, msg)) | ||||
progress.increment(item=f) | progress.increment(item=f) | ||||
# keep (noop, just log it) | # keep (noop, just log it) | ||||
for a in mergestatemod.NO_OP_ACTIONS: | for a in mergestatemod.NO_OP_ACTIONS: | ||||
for f, args, msg in mresult.getactions((a,), sort=True): | for f, args, msg in mresult.getactions((a,), sort=True): | ||||
repo.ui.debug(b" %s: %s -> %s\n" % (f, msg, a)) | repo.ui.debug(b" %s: %s -> %s\n" % (f, msg, a.__bytes__())) | ||||
# no progress | # no progress | ||||
# directory rename, move local | # directory rename, move local | ||||
for f, args, msg in mresult.getactions( | for f, args, msg in mresult.getactions( | ||||
(mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,), sort=True | (mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,), sort=True | ||||
): | ): | ||||
repo.ui.debug(b" %s: %s -> dm\n" % (f, msg)) | repo.ui.debug(b" %s: %s -> dm\n" % (f, msg)) | ||||
progress.increment(item=f) | progress.increment(item=f) |
# This record was release in 3.7 and usage was removed in 5.6 | # This record was release in 3.7 and usage was removed in 5.6 | ||||
LEGACY_RECORD_DRIVER_RESOLVED = b'd' | LEGACY_RECORD_DRIVER_RESOLVED = b'd' | ||||
# This record was release in 3.7 and usage was removed in 5.6 | # This record was release in 3.7 and usage was removed in 5.6 | ||||
LEGACY_MERGE_DRIVER_STATE = b'm' | LEGACY_MERGE_DRIVER_STATE = b'm' | ||||
# This record was release in 3.7 and usage was removed in 5.6 | # This record was release in 3.7 and usage was removed in 5.6 | ||||
LEGACY_MERGE_DRIVER_MERGE = b'D' | LEGACY_MERGE_DRIVER_MERGE = b'D' | ||||
ACTION_FORGET = b'f' | class MergeAction(object): | ||||
ACTION_REMOVE = b'r' | """represent an "action" merge need to take for a given file | ||||
ACTION_ADD = b'a' | |||||
ACTION_GET = b'g' | Attributes: | ||||
ACTION_PATH_CONFLICT = b'p' | |||||
ACTION_PATH_CONFLICT_RESOLVE = b'pr' | _short: internal representation used to identify each action | ||||
ACTION_ADD_MODIFIED = b'am' | """ | ||||
ACTION_CREATED = b'c' | |||||
ACTION_DELETED_CHANGED = b'dc' | def __init__(self, short): | ||||
ACTION_CHANGED_DELETED = b'cd' | self._short = short | ||||
ACTION_MERGE = b'm' | |||||
ACTION_LOCAL_DIR_RENAME_GET = b'dg' | def __hash__(self): | ||||
ACTION_DIR_RENAME_MOVE_LOCAL = b'dm' | return hash(self._short) | ||||
ACTION_KEEP = b'k' | |||||
def __repr__(self): | |||||
return 'MergeAction<%s>' % self._short.decode('ascii') | |||||
def __bytes__(self): | |||||
return self._short | |||||
def __eq__(self, other): | |||||
if other is None: | |||||
return False | |||||
assert isinstance(other, MergeAction) | |||||
return self._short == other._short | |||||
def __lt__(self, other): | |||||
return self._short < other._short | |||||
ACTION_FORGET = MergeAction(b'f') | |||||
ACTION_REMOVE = MergeAction(b'r') | |||||
ACTION_ADD = MergeAction(b'a') | |||||
ACTION_GET = MergeAction(b'g') | |||||
ACTION_PATH_CONFLICT = MergeAction(b'p') | |||||
ACTION_PATH_CONFLICT_RESOLVE = MergeAction('pr') | |||||
ACTION_ADD_MODIFIED = MergeAction(b'am') | |||||
ACTION_CREATED = MergeAction(b'c') | |||||
ACTION_DELETED_CHANGED = MergeAction(b'dc') | |||||
ACTION_CHANGED_DELETED = MergeAction(b'cd') | |||||
ACTION_MERGE = MergeAction(b'm') | |||||
ACTION_LOCAL_DIR_RENAME_GET = MergeAction(b'dg') | |||||
ACTION_DIR_RENAME_MOVE_LOCAL = MergeAction(b'dm') | |||||
ACTION_KEEP = MergeAction(b'k') | |||||
# the file was absent on local side before merge and we should | # the file was absent on local side before merge and we should | ||||
# keep it absent (absent means file not present, it can be a result | # keep it absent (absent means file not present, it can be a result | ||||
# of file deletion, rename etc.) | # of file deletion, rename etc.) | ||||
ACTION_KEEP_ABSENT = b'ka' | ACTION_KEEP_ABSENT = MergeAction(b'ka') | ||||
# the file is absent on the ancestor and remote side of the merge | # the file is absent on the ancestor and remote side of the merge | ||||
# hence this file is new and we should keep it | # hence this file is new and we should keep it | ||||
ACTION_KEEP_NEW = b'kn' | ACTION_KEEP_NEW = MergeAction(b'kn') | ||||
ACTION_EXEC = b'e' | ACTION_EXEC = MergeAction(b'e') | ||||
ACTION_CREATED_MERGE = b'cm' | ACTION_CREATED_MERGE = MergeAction(b'cm') | ||||
# actions which are no op | # actions which are no op | ||||
NO_OP_ACTIONS = ( | NO_OP_ACTIONS = ( | ||||
ACTION_KEEP, | ACTION_KEEP, | ||||
ACTION_KEEP_ABSENT, | ACTION_KEEP_ABSENT, | ||||
ACTION_KEEP_NEW, | ACTION_KEEP_NEW, | ||||
) | ) | ||||