mirror of
https://github.com/mhagger/git-imerge.git
synced 2024-09-21 04:28:11 +03:00
Tighten up logic in MergeFrontier.compute_by_bisection().
This removes some redundant automerge attempts. Also add lots of comments and expand the method docstring.
This commit is contained in:
parent
3922ea6563
commit
9fd67a3670
134
git-imerge
134
git-imerge
@ -714,45 +714,127 @@ class MergeFrontier(object):
|
||||
|
||||
@staticmethod
|
||||
def compute_by_bisection(block):
|
||||
"""Return a MergeFrontier instance for block."""
|
||||
"""Return a MergeFrontier instance for block.
|
||||
|
||||
if block.len1 <= 1 or block.len2 <= 1:
|
||||
We make the following assumptions (using Python subscript
|
||||
notation):
|
||||
|
||||
0. All of the merges in block[1:,0] and block[0,1:] are
|
||||
already known. (This is an invariant of the Block class.)
|
||||
|
||||
1. If a direct merge can be done between block[i1-1,0] and
|
||||
block[0,i2-1], then all of the pairwise merges in
|
||||
block[1:i1, 1:i2] can also be done.
|
||||
|
||||
2. If a direct merge fails between block[i1-1,0] and
|
||||
block[0,i2-1], then all of the pairwise merges in
|
||||
block[i1-1:,i2-1:] would also fail.
|
||||
|
||||
Under these assumptions, the merge frontier is a stepstair
|
||||
pattern going from the bottom-left to the top-right, and
|
||||
bisection can be used to find the transition between mergeable
|
||||
and conflicting in any row or column.
|
||||
|
||||
Of course these assumptions are not rigorously true, so the
|
||||
MergeFrontier returned by this function is only an
|
||||
approximation of the real merge diagram. We check for and
|
||||
correct such inconsistencies later."""
|
||||
|
||||
# Given that these diagrams typically have few blocks, check
|
||||
# the end of a range first to see if the whole range can be
|
||||
# determined, and fall back to bisection otherwise. We
|
||||
# determine the frontier block by block, starting in the lower
|
||||
# left.
|
||||
|
||||
if block.len1 <= 1 or block.len2 <= 1 or not block.is_mergeable(1, 1):
|
||||
# There are no mergeable blocks in block.
|
||||
return MergeFrontier(block, [])
|
||||
|
||||
blocks = []
|
||||
|
||||
# At this point, we know that there is at least one mergeable
|
||||
# commit in the first column. Find the height of the success
|
||||
# block in column 1:
|
||||
i1 = 1
|
||||
i2 = block.len2
|
||||
i2 = find_first_false(
|
||||
lambda i: block.is_mergeable(i1, i),
|
||||
1, block.len2,
|
||||
)
|
||||
|
||||
# Now we know that (1,i2-1) is mergeable but (1,i2) is not;
|
||||
# e.g., (i1, i2) is like 'A' (or maybe 'B') in the following
|
||||
# diagram (where '*' means mergeable, 'x' means not mergeable,
|
||||
# and '?' means indeterminate) and that the merge under 'A' is
|
||||
# not mergeable:
|
||||
#
|
||||
# i1
|
||||
#
|
||||
# 0123456
|
||||
# 0 *******
|
||||
# 1 **?????
|
||||
# i2 2 **?????
|
||||
# 3 **?????
|
||||
# 4 *Axxxxx
|
||||
# 5 *xxxxxx
|
||||
# B
|
||||
|
||||
# Given that these diagrams typically have few blocks, check the
|
||||
# end of a range first to see if the whole range can be filled in,
|
||||
# and fall back to bisection otherwise.
|
||||
while True:
|
||||
# Find the width of the success rectangle at row (i2-1) and fill it in:
|
||||
if block.is_mergeable(block.len1 - 1, i2 - 1):
|
||||
newi1 = block.len1
|
||||
else:
|
||||
newi1 = find_first_false(
|
||||
lambda i: block.is_mergeable(i, i2 - 1),
|
||||
i1, block.len1 - 1,
|
||||
)
|
||||
blocks.append(block[:newi1,:i2])
|
||||
i1 = newi1
|
||||
|
||||
if i1 == block.len1:
|
||||
if i2 == 1:
|
||||
break
|
||||
|
||||
# Find the height of the conflict rectangle at column i1 and fill it in:
|
||||
if not block.is_mergeable(i1, 1):
|
||||
newi2 = 1
|
||||
# At this point in the loop, we know that any blocks to
|
||||
# the left of 'A' have already been recorded, (i1, i2-1)
|
||||
# is mergeable but (i1, i2) is not; e.g., we are at a
|
||||
# place like 'A' in the following diagram:
|
||||
#
|
||||
# i1
|
||||
#
|
||||
# 0123456
|
||||
# 0 **|****
|
||||
# 1 **|*???
|
||||
# i2 2 **|*???
|
||||
# 3 **|Axxx
|
||||
# 4 --+xxxx
|
||||
# 5 *xxxxxx
|
||||
#
|
||||
# This implies that (i1, i2) is the first unmergeable
|
||||
# commit in a blocker block (though blocker blocks are not
|
||||
# recorded explicitly). It also implies that a mergeable
|
||||
# block has its last mergeable commit somewhere in row
|
||||
# i2-1; find its width.
|
||||
if block.is_mergeable(block.len1 - 1, i2 - 1):
|
||||
i1 = block.len1
|
||||
blocks.append(block[:i1,:i2])
|
||||
break
|
||||
else:
|
||||
newi2 = find_first_false(
|
||||
lambda i: block.is_mergeable(i1, i),
|
||||
2, i2,
|
||||
i1 = find_first_false(
|
||||
lambda i: block.is_mergeable(i, i2 - 1),
|
||||
i1 + 1, block.len1 - 1,
|
||||
)
|
||||
i2 = newi2
|
||||
blocks.append(block[:i1,:i2])
|
||||
|
||||
if i2 == 1:
|
||||
# At this point in the loop, (i1-1, i2-1) is mergeable but
|
||||
# (i1, i2-1) is not; e.g., 'A' in the following diagram:
|
||||
#
|
||||
# i1
|
||||
#
|
||||
# 0123456
|
||||
# 0 **|*|**
|
||||
# 1 **|*|??
|
||||
# i2 2 --+-+xx
|
||||
# 3 **|xxAx
|
||||
# 4 --+xxxx
|
||||
# 5 *xxxxxx
|
||||
#
|
||||
# The block ending at (i1-1,i2-1) has just been recorded.
|
||||
# Now find the height of the conflict rectangle at column
|
||||
# i1 and fill it in:
|
||||
if block.is_mergeable(i1, 1):
|
||||
i2 = find_first_false(
|
||||
lambda i: block.is_mergeable(i1, i),
|
||||
2, i2 - 1,
|
||||
)
|
||||
else:
|
||||
break
|
||||
|
||||
return MergeFrontier(block, blocks)
|
||||
|
Loading…
Reference in New Issue
Block a user