Skip to content

Commit

Permalink
reparent: add a --commit option
Browse files Browse the repository at this point in the history
Add an option `git imerge reparent --commit=COMMIT`, which allows the
parents of an arbitrary commit to be changed. Moreover, the
descendants of COMMIT are also rewritten all the way to HEAD.

This feature was suggested by Ke Ma <k.ma@alibaba-inc.com>, and the
implementation is partly derived from PR #130 submitted by Ke Ma.
  • Loading branch information
mhagger committed Jun 24, 2018
1 parent 269969e commit e92eb0f
Showing 1 changed file with 68 additions and 5 deletions.
73 changes: 68 additions & 5 deletions git-imerge
Original file line number Diff line number Diff line change
Expand Up @@ -3628,19 +3628,63 @@ def cmd_diagram(parser, options):
)


def reparent_recursively(git, start_commit, parents, end_commit):
"""Change the parents of start_commit and its descendants.
Change start_commit to have the specified parents, and reparent
all commits on the ancestry path between start_commit and
end_commit accordingly. Return the replacement end_commit.
start_commit, parents, and end_commit must all be resolved OIDs.
"""

# A map {old_oid : new_oid} keeping track of which replacements
# have to be made:
replacements = {}

# Reparent start_commit:
replacements[start_commit] = git.reparent(start_commit, parents)

for (commit, parents) in git.rev_list_with_parents(
'--ancestry-path', '--topo-order', '--reverse',
'%s..%s' % (start_commit, end_commit)
):
parents = [replacements.get(p, p) for p in parents]
replacements[commit] = git.reparent(commit, parents)

try:
return replacements[end_commit]
except KeyError:
raise ValueError(
"%s is not an ancestor of %s" % (start_commit, end_commit),
)


def cmd_reparent(parser, options):
git = GitRepository()
try:
commit_sha1 = git.get_commit_sha1('HEAD')
commit = git.get_commit_sha1(options.commit)
except ValueError:
sys.exit('%s is not a valid commit', options.commit)

try:
head = git.get_commit_sha1('HEAD')
except ValueError:
sys.exit('HEAD is not a valid commit')

try:
parent_sha1s = [git.get_commit_sha1(p) for p in options.parents]
parents = [git.get_commit_sha1(p) for p in options.parents]
except ValueError as e:
sys.exit(e.message)

sys.stderr.write('Reparenting %s..HEAD\n' % (options.commit,))

try:
new_head = reparent_recursively(git, commit, parents, head)
except ValueError as e:
sys.exit(e.message)

sys.stdout.write('%s\n' % (git.reparent(commit_sha1, parent_sha1s),))
sys.stdout.write('%s\n' % (new_head,))


def main(args):
Expand Down Expand Up @@ -3927,10 +3971,29 @@ def main(args):

subparser = subparsers.add_parser(
'reparent',
help='change the parents of the HEAD commit',
help=(
'change the parents of the specified commit and propagate the '
'change to HEAD'
),
)
subparser.add_argument(
'parents', nargs='*', help='[PARENT...]',
'--commit', metavar='COMMIT', default='HEAD',
help=(
'target commit to reparent. Reparent will happen from this '
'commit all the way back to HEAD. The reparent operation '
'creates a new commit object like the original commit, but '
'with the specified parents. The reparent command first '
'executes the reparent operation on the commit specified by '
'the --commit argument to generate a new commit '
'object, then it uses this object to replace the first parent '
'of its child commit on the path towards the HEAD commit. The '
'command repeats this process till the HEAD commit so that a '
'list of new commit objects are created.'
),
)
subparser.add_argument(
'parents', nargs='*', metavar='PARENT',
help='a list of commits',
)

options = parser.parse_args(args)
Expand Down

0 comments on commit e92eb0f

Please sign in to comment.