diff mercurial/commands.py @ 52360:f2fc0a91faca

commands: create a "mercurial.cmd_impls" module to host graft The "mercurial.commands" have been overweight for a while. We create a namespace dedicated to host smaller modules containing code revelant to a specific command. This should result in more isolated snd manageable module. We start with moving the code for "hg graft" in "mercurial.cmd_impls.graft" before doing more work on it. Since that code was about 5% of "commands.py" this seems like a success.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Tue, 19 Nov 2024 15:46:12 +0100
parents f4733654f144
children 0a81f3ef054c
line wrap: on
line diff
--- a/mercurial/commands.py	Wed Nov 27 22:09:37 2024 -0500
+++ b/mercurial/commands.py	Tue Nov 19 15:46:12 2024 +0100
@@ -68,6 +68,9 @@
     vfs as vfsmod,
     wireprotoserver,
 )
+
+from .cmd_impls import graft as graft_impl
+
 from .utils import (
     dateutil,
     procutil,
@@ -3185,285 +3188,7 @@
     Returns 0 on successful completion, 1 if there are unresolved files.
     """
     with repo.wlock():
-        return _dograft(ui, repo, *revs, **opts)
-
-
-def _dograft(ui, repo, *revs, **opts):
-    if revs and opts.get('rev'):
-        ui.warn(
-            _(
-                b'warning: inconsistent use of --rev might give unexpected '
-                b'revision ordering!\n'
-            )
-        )
-
-    revs = list(revs)
-    revs.extend(opts.get('rev'))
-    # a dict of data to be stored in state file
-    statedata = {}
-    # list of new nodes created by ongoing graft
-    statedata[b'newnodes'] = []
-
-    cmdutil.resolve_commit_options(ui, opts)
-
-    editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
-
-    cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
-
-    cont = False
-    if opts.get('no_commit'):
-        cmdutil.check_incompatible_arguments(
-            opts,
-            'no_commit',
-            ['edit', 'currentuser', 'currentdate', 'log'],
-        )
-
-    graftstate = statemod.cmdstate(repo, b'graftstate')
-
-    if opts.get('stop'):
-        cmdutil.check_incompatible_arguments(
-            opts,
-            'stop',
-            [
-                'edit',
-                'log',
-                'user',
-                'date',
-                'currentdate',
-                'currentuser',
-                'rev',
-            ],
-        )
-        return _stopgraft(ui, repo, graftstate)
-    elif opts.get('abort'):
-        cmdutil.check_incompatible_arguments(
-            opts,
-            'abort',
-            [
-                'edit',
-                'log',
-                'user',
-                'date',
-                'currentdate',
-                'currentuser',
-                'rev',
-            ],
-        )
-        return cmdutil.abortgraft(ui, repo, graftstate)
-    elif opts.get('continue'):
-        cont = True
-        if revs:
-            raise error.InputError(_(b"can't specify --continue and revisions"))
-        # read in unfinished revisions
-        if graftstate.exists():
-            statedata = cmdutil.readgraftstate(repo, graftstate)
-            if statedata.get(b'date'):
-                opts['date'] = statedata[b'date']
-            if statedata.get(b'user'):
-                opts['user'] = statedata[b'user']
-            if statedata.get(b'log'):
-                opts['log'] = True
-            if statedata.get(b'no_commit'):
-                opts['no_commit'] = statedata.get(b'no_commit')
-            if statedata.get(b'base'):
-                opts['base'] = statedata.get(b'base')
-            nodes = statedata[b'nodes']
-            revs = [repo[node].rev() for node in nodes]
-        else:
-            cmdutil.wrongtooltocontinue(repo, _(b'graft'))
-    else:
-        if not revs:
-            raise error.InputError(_(b'no revisions specified'))
-        cmdutil.checkunfinished(repo)
-        cmdutil.bailifchanged(repo)
-        revs = logcmdutil.revrange(repo, revs)
-
-    skipped = set()
-    basectx = None
-    if opts.get('base'):
-        basectx = logcmdutil.revsingle(repo, opts['base'], None)
-    if basectx is None:
-        # check for merges
-        for rev in repo.revs(b'%ld and merge()', revs):
-            ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
-            skipped.add(rev)
-    revs = [r for r in revs if r not in skipped]
-    if not revs:
-        return -1
-    if basectx is not None and len(revs) != 1:
-        raise error.InputError(_(b'only one revision allowed with --base '))
-
-    # Don't check in the --continue case, in effect retaining --force across
-    # --continues. That's because without --force, any revisions we decided to
-    # skip would have been filtered out here, so they wouldn't have made their
-    # way to the graftstate. With --force, any revisions we would have otherwise
-    # skipped would not have been filtered out, and if they hadn't been applied
-    # already, they'd have been in the graftstate.
-    if not (cont or opts.get('force')) and basectx is None:
-        # check for ancestors of dest branch
-        ancestors = repo.revs(b'%ld & (::.)', revs)
-        for rev in ancestors:
-            ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
-
-        revs = [r for r in revs if r not in ancestors]
-
-        if not revs:
-            return -1
-
-        # analyze revs for earlier grafts
-        ids = {}
-        for ctx in repo.set(b"%ld", revs):
-            ids[ctx.hex()] = ctx.rev()
-            n = ctx.extra().get(b'source')
-            if n:
-                ids[n] = ctx.rev()
-
-        # check ancestors for earlier grafts
-        ui.debug(b'scanning for duplicate grafts\n')
-
-        # The only changesets we can be sure doesn't contain grafts of any
-        # revs, are the ones that are common ancestors of *all* revs:
-        for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
-            ctx = repo[rev]
-            n = ctx.extra().get(b'source')
-            if n in ids:
-                try:
-                    r = repo[n].rev()
-                except error.RepoLookupError:
-                    r = None
-                if r in revs:
-                    ui.warn(
-                        _(
-                            b'skipping revision %d:%s '
-                            b'(already grafted to %d:%s)\n'
-                        )
-                        % (r, repo[r], rev, ctx)
-                    )
-                    revs.remove(r)
-                elif ids[n] in revs:
-                    if r is None:
-                        ui.warn(
-                            _(
-                                b'skipping already grafted revision %d:%s '
-                                b'(%d:%s also has unknown origin %s)\n'
-                            )
-                            % (ids[n], repo[ids[n]], rev, ctx, n[:12])
-                        )
-                    else:
-                        ui.warn(
-                            _(
-                                b'skipping already grafted revision %d:%s '
-                                b'(%d:%s also has origin %d:%s)\n'
-                            )
-                            % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
-                        )
-                    revs.remove(ids[n])
-            elif ctx.hex() in ids:
-                r = ids[ctx.hex()]
-                if r in revs:
-                    ui.warn(
-                        _(
-                            b'skipping already grafted revision %d:%s '
-                            b'(was grafted from %d:%s)\n'
-                        )
-                        % (r, repo[r], rev, ctx)
-                    )
-                    revs.remove(r)
-        if not revs:
-            return -1
-
-    if opts.get('no_commit'):
-        statedata[b'no_commit'] = True
-    if opts.get('base'):
-        statedata[b'base'] = opts['base']
-    for pos, ctx in enumerate(repo.set(b"%ld", revs)):
-        desc = b'%d:%s "%s"' % (
-            ctx.rev(),
-            ctx,
-            ctx.description().split(b'\n', 1)[0],
-        )
-        names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
-        if names:
-            desc += b' (%s)' % b' '.join(names)
-        ui.status(_(b'grafting %s\n') % desc)
-        if opts.get('dry_run'):
-            continue
-
-        source = ctx.extra().get(b'source')
-        extra = {}
-        if source:
-            extra[b'source'] = source
-            extra[b'intermediate-source'] = ctx.hex()
-        else:
-            extra[b'source'] = ctx.hex()
-        user = ctx.user()
-        if opts.get('user'):
-            user = opts['user']
-            statedata[b'user'] = user
-        date = ctx.date()
-        if opts.get('date'):
-            date = opts['date']
-            statedata[b'date'] = date
-        message = ctx.description()
-        if opts.get('log'):
-            message += b'\n(grafted from %s)' % ctx.hex()
-            statedata[b'log'] = True
-
-        # we don't merge the first commit when continuing
-        if not cont:
-            # perform the graft merge with p1(rev) as 'ancestor'
-            overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
-            base = ctx.p1() if basectx is None else basectx
-            with ui.configoverride(overrides, b'graft'):
-                stats = mergemod.graft(
-                    repo, ctx, base, [b'local', b'graft', b'parent of graft']
-                )
-            # report any conflicts
-            if stats.unresolvedcount > 0:
-                # write out state for --continue
-                nodes = [repo[rev].hex() for rev in revs[pos:]]
-                statedata[b'nodes'] = nodes
-                stateversion = 1
-                graftstate.save(stateversion, statedata)
-                ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
-                ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
-                return 1
-        else:
-            cont = False
-
-        # commit if --no-commit is false
-        if not opts.get('no_commit'):
-            node = repo.commit(
-                text=message, user=user, date=date, extra=extra, editor=editor
-            )
-            if node is None:
-                ui.warn(
-                    _(b'note: graft of %d:%s created no changes to commit\n')
-                    % (ctx.rev(), ctx)
-                )
-            # checking that newnodes exist because old state files won't have it
-            elif statedata.get(b'newnodes') is not None:
-                nn = statedata[b'newnodes']
-                assert isinstance(nn, list)  # list of bytes
-                nn.append(node)
-
-    # remove state when we complete successfully
-    if not opts.get('dry_run'):
-        graftstate.delete()
-
-    return 0
-
-
-def _stopgraft(ui, repo, graftstate):
-    """stop the interrupted graft"""
-    if not graftstate.exists():
-        raise error.StateError(_(b"no interrupted graft found"))
-    pctx = repo[b'.']
-    mergemod.clean_update(pctx)
-    graftstate.delete()
-    ui.status(_(b"stopped the interrupted graft\n"))
-    ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
-    return 0
+        return graft_impl.cmd_graft(ui, repo, *revs, **opts)
 
 
 statemod.addunfinished(