mirror of
https://github.com/mhagger/git-imerge.git
synced 2024-09-11 15:16:16 +03:00
Merge branch 'simplify-to-path' into branch 'rebase-reversed'
This commit is contained in:
commit
7f8de720ea
205
git-imerge
205
git-imerge
@ -140,8 +140,6 @@ class Failure(Exception):
|
|||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class AnsiColor:
|
class AnsiColor:
|
||||||
BLACK = '\033[0;30m'
|
BLACK = '\033[0;30m'
|
||||||
@ -579,6 +577,23 @@ class TemporaryHead(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_ff(refname, commit):
|
||||||
|
"""Would updating refname to commit be a fast-forward update?
|
||||||
|
|
||||||
|
Return True iff refname is not currently set or it points to an
|
||||||
|
ancestor of commit.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
ref_oldval = get_commit_sha1(refname)
|
||||||
|
except ValueError:
|
||||||
|
# refname doesn't already exist; no problem.
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return MergeState._is_ancestor(ref_oldval, commit)
|
||||||
|
|
||||||
|
|
||||||
def reparent(commit, parent_sha1s, msg=None):
|
def reparent(commit, parent_sha1s, msg=None):
|
||||||
"""Create a new commit object like commit, but with the specified parents.
|
"""Create a new commit object like commit, but with the specified parents.
|
||||||
|
|
||||||
@ -628,6 +643,53 @@ def reparent(commit, parent_sha1s, msg=None):
|
|||||||
return out.strip()
|
return out.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def create_commit_chain(base, path):
|
||||||
|
"""Point refname at the chain of commits indicated by path.
|
||||||
|
|
||||||
|
path is a list [(commit, metadata), ...]. Create a series of
|
||||||
|
commits corresponding to the entries in path. Each commit's tree
|
||||||
|
is taken from the corresponding old commit, and each commit's
|
||||||
|
metadata is taken from the corresponding metadata commit. Use base
|
||||||
|
as the parent of the first commit, or make the first commit a root
|
||||||
|
commit if base is None. Reuse existing commits from the list
|
||||||
|
whenever possible.
|
||||||
|
|
||||||
|
Return a commit object corresponding to the last commit in the
|
||||||
|
chain.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
reusing = True
|
||||||
|
if base is None:
|
||||||
|
if not path:
|
||||||
|
raise ValueError('neither base nor path specified')
|
||||||
|
parents = []
|
||||||
|
else:
|
||||||
|
parents = [base]
|
||||||
|
|
||||||
|
for (commit, metadata) in path:
|
||||||
|
if reusing:
|
||||||
|
if commit == metadata and get_commit_parents(commit) == parents:
|
||||||
|
# We can reuse this commit, too.
|
||||||
|
parents = [commit]
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
reusing = False
|
||||||
|
|
||||||
|
# Create a commit, copying the old log message and author info
|
||||||
|
# from the metadata commit:
|
||||||
|
tree = get_tree(commit)
|
||||||
|
new_commit = commit_tree(
|
||||||
|
tree, parents,
|
||||||
|
msg=get_log_message(metadata),
|
||||||
|
metadata=get_author_info(metadata),
|
||||||
|
)
|
||||||
|
parents = [new_commit]
|
||||||
|
|
||||||
|
[commit] = parents
|
||||||
|
return commit
|
||||||
|
|
||||||
|
|
||||||
class AutomaticMergeFailed(Exception):
|
class AutomaticMergeFailed(Exception):
|
||||||
def __init__(self, commit1, commit2):
|
def __init__(self, commit1, commit2):
|
||||||
Exception.__init__(
|
Exception.__init__(
|
||||||
@ -1848,6 +1910,13 @@ class SubBlock(Block):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MissingMergeFailure(Failure):
|
||||||
|
def __init__(self, i1, i2):
|
||||||
|
Failure.__init__(self, 'Merge %d-%d is not yet done' % (i1, i2))
|
||||||
|
self.i1 = i1
|
||||||
|
self.i2 = i2
|
||||||
|
|
||||||
|
|
||||||
class MergeState(Block):
|
class MergeState(Block):
|
||||||
SOURCE_TABLE = {
|
SOURCE_TABLE = {
|
||||||
'auto': MergeRecord.SAVED_AUTO,
|
'auto': MergeRecord.SAVED_AUTO,
|
||||||
@ -2405,88 +2474,78 @@ class MergeState(Block):
|
|||||||
|
|
||||||
self._set_refname(refname, commit, force=force)
|
self._set_refname(refname, commit, force=force)
|
||||||
|
|
||||||
def simplify_to_rebase(self, refname, force=False):
|
def _simplify_to_path(self, refname, base, path, force=False):
|
||||||
i1 = self.len1 - 1
|
"""Simplify based on path and set refname to the result.
|
||||||
for i2 in range(1, self.len2):
|
|
||||||
if not (i1, i2) in self:
|
|
||||||
raise Failure(
|
|
||||||
'Cannot simplify to rebase because merge %d-%d is not yet done'
|
|
||||||
% (i1, i2)
|
|
||||||
)
|
|
||||||
|
|
||||||
if not force:
|
The base and path arguments are defined similarly to
|
||||||
# A rebase simplification is allowed to discard history,
|
create_commit_chain(), except that instead of SHA-1s they
|
||||||
# as long as the *pre-simplification* apex commit is a
|
represent commits via (i1, i2) tuples.
|
||||||
# descendant of the branch to be moved.
|
|
||||||
try:
|
|
||||||
ref_oldval = get_commit_sha1(refname)
|
|
||||||
except ValueError:
|
|
||||||
# refname doesn't already exist; no problem.
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
commit = self[-1, -1].sha1
|
|
||||||
if not MergeState._is_ancestor(ref_oldval, commit):
|
|
||||||
raise Failure(
|
|
||||||
'%s is not an ancestor of %s; use --force if you are sure'
|
|
||||||
% (commit, refname,)
|
|
||||||
)
|
|
||||||
|
|
||||||
commit = self[i1, 0].sha1
|
"""
|
||||||
for i2 in range(1, self.len2):
|
|
||||||
orig = self[0, i2].sha1
|
|
||||||
tree = get_tree(self[i1, i2].sha1)
|
|
||||||
authordata = get_author_info(orig)
|
|
||||||
|
|
||||||
# Create a commit, copying the old log message and author info:
|
base_sha1 = self[base].sha1
|
||||||
commit = commit_tree(
|
path_sha1 = []
|
||||||
tree, [commit], msg=get_log_message(orig), metadata=authordata,
|
for (commit, metadata) in path:
|
||||||
|
commit_record = self[commit]
|
||||||
|
if not commit_record.is_known():
|
||||||
|
raise MissingMergeFailure(*commit)
|
||||||
|
metadata_record = self[metadata]
|
||||||
|
if not metadata_record.is_known():
|
||||||
|
raise MissingMergeFailure(*metadata_record)
|
||||||
|
path_sha1.append((commit_record.sha1, metadata_record.sha1))
|
||||||
|
|
||||||
|
# A path simplification is allowed to discard history, as long
|
||||||
|
# as the *pre-simplification* apex commit is a descendant of
|
||||||
|
# the branch to be moved.
|
||||||
|
if path:
|
||||||
|
apex = path_sha1[-1][0]
|
||||||
|
else:
|
||||||
|
apex = base_sha1
|
||||||
|
|
||||||
|
if not force and not is_ff(refname, apex):
|
||||||
|
raise Failure(
|
||||||
|
'%s cannot be updated to %s without discarding history.\n'
|
||||||
|
'Use --force if you are sure, or choose a different reference'
|
||||||
|
% (refname, apex,)
|
||||||
)
|
)
|
||||||
|
|
||||||
# We checked above that the update is OK, so here we can set
|
# The update is OK, so here we can set force=True:
|
||||||
# force=True:
|
self._set_refname(
|
||||||
self._set_refname(refname, commit, force=True)
|
refname,
|
||||||
|
create_commit_chain(base_sha1, path_sha1),
|
||||||
|
force=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def simplify_to_rebase(self, refname, force=False):
|
||||||
|
i1 = self.len1 - 1
|
||||||
|
path = [
|
||||||
|
((i1, i2), (0, i2))
|
||||||
|
for i2 in range(1, self.len2)
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._simplify_to_path(refname, (i1, 0), path, force=force)
|
||||||
|
except MissingMergeFailure as e:
|
||||||
|
raise Failure(
|
||||||
|
'Cannot simplify to rebase because merge %d-%d is not yet done'
|
||||||
|
% (e.i1, e.i2)
|
||||||
|
)
|
||||||
|
|
||||||
def simplify_to_rebase_reversed(self, refname, force=False):
|
def simplify_to_rebase_reversed(self, refname, force=False):
|
||||||
i2 = self.len2 - 1
|
i2 = self.len2 - 1
|
||||||
for i1 in range(1, self.len1):
|
path = [
|
||||||
if not (i1, i2) in self:
|
((i1, i2), (i1, 0))
|
||||||
raise Failure(
|
for i1 in range(1, self.len1)
|
||||||
'Cannot simplify to rebase because merge %d-%d is not yet done'
|
]
|
||||||
% (i1, i2)
|
|
||||||
)
|
|
||||||
|
|
||||||
if not force:
|
try:
|
||||||
# A rebase simplification is allowed to discard history,
|
self._simplify_to_path(refname, (0, i2), path, force=force)
|
||||||
# as long as the *pre-simplification* apex commit is a
|
except MissingMergeFailure as e:
|
||||||
# descendant of the branch to be moved.
|
raise Failure(
|
||||||
try:
|
'Cannot simplify to rebase-reversed because merge %d-%d is not yet done'
|
||||||
ref_oldval = get_commit_sha1(refname)
|
% (e.i1, e.i2)
|
||||||
except ValueError:
|
|
||||||
# refname doesn't already exist; no problem.
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
commit = self[-1, -1].sha1
|
|
||||||
if not MergeState._is_ancestor(ref_oldval, commit):
|
|
||||||
raise Failure(
|
|
||||||
'%s is not an ancestor of %s; use --force if you are sure'
|
|
||||||
% (commit, refname,)
|
|
||||||
)
|
|
||||||
|
|
||||||
commit = self[0, i2].sha1
|
|
||||||
for i1 in range(1, self.len1):
|
|
||||||
orig = self[i1, 0].sha1
|
|
||||||
tree = get_tree(self[i1, i2].sha1)
|
|
||||||
authordata = get_author_info(orig)
|
|
||||||
|
|
||||||
# Create a commit, copying the old log message and author info:
|
|
||||||
commit = commit_tree(
|
|
||||||
tree, [commit], msg=get_log_message(orig), metadata=authordata,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# We checked above that the update is OK, so here we can set
|
|
||||||
# force=True:
|
|
||||||
self._set_refname(refname, commit, force=True)
|
|
||||||
|
|
||||||
def simplify_to_merge(self, refname, force=False):
|
def simplify_to_merge(self, refname, force=False):
|
||||||
if not (-1, -1) in self:
|
if not (-1, -1) in self:
|
||||||
raise Failure(
|
raise Failure(
|
||||||
|
Loading…
Reference in New Issue
Block a user