Exemplo n.º 1
0
def updatebookmarks(repo, changes, name="git_handler"):
    """abstract writing bookmarks for backwards compatibility"""
    bms = repo._bookmarks
    tr = lock = wlock = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()
        tr = repo.transaction(name)
        if hgutil.safehasattr(bms, "applychanges"):
            # applychanges was added in mercurial 4.3
            bms.applychanges(repo, tr, changes)
        else:
            for name, node in changes:
                if node is None:
                    del bms[name]
                else:
                    bms[name] = node
            if hgutil.safehasattr(bms, "recordchange"):
                # recordchange was added in mercurial 3.2
                bms.recordchange(tr)
            else:
                bms.write()
        tr.close()
    finally:
        lockmod.release(tr, lock, wlock)
Exemplo n.º 2
0
 def testlock(self):
     state = teststate(self, tempfile.mkdtemp(dir=os.getcwd()))
     lock = state.makelock()
     state.assertacquirecalled(True)
     lock.release()
     state.assertreleasecalled(True)
     state.assertpostreleasecalled(True)
     state.assertlockexists(False)
Exemplo n.º 3
0
def metarewrite(repo, old, newbases, commitopts, copypreds=None):
    """Return (nodeid, created) where nodeid is the identifier of the
    changeset generated by the rewrite process, and created is True if
    nodeid was actually created. If created is False, nodeid
    references a changeset existing before the rewrite call.
    """
    wlock = lock = tr = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()
        tr = repo.transaction("rewrite")
        updatebookmarks = bookmarksupdater(repo, old.node())

        message = cmdutil.logmessage(repo, commitopts)
        if not message:
            message = old.description()

        user = commitopts.get("user") or old.user()
        date = commitopts.get("date") or None  # old.date()
        extra = dict(commitopts.get("extra", old.extra()))
        extra["branch"] = old.branch()
        preds = [old.node()]
        mutop = "metaedit"
        if copypreds:
            preds.extend(copypreds)
            mutop = "metaedit-copy"
        mutinfo = mutation.record(repo, extra, preds, mutop)
        loginfo = {"predecessors": old.hex(), "mutation": mutop}

        new = context.metadataonlyctx(
            repo,
            old,
            parents=newbases,
            text=message,
            user=user,
            date=date,
            extra=extra,
            loginfo=loginfo,
            mutinfo=mutinfo,
        )

        if commitopts.get("edit"):
            new._text = cmdutil.commitforceeditor(repo, new, [])
        revcount = len(repo)
        newid = repo.commitctx(new)
        new = repo[newid]
        created = len(repo) != revcount
        updatebookmarks(newid)

        tr.close()
        return newid, created
    finally:
        lockmod.release(tr, lock, wlock)
Exemplo n.º 4
0
    def testinheritcheck(self):
        d = tempfile.mkdtemp(dir=os.getcwd())
        state = teststate(self, d)

        def check():
            raise error.LockInheritanceContractViolation("check failed")

        lock = state.makelock(inheritchecker=check)
        state.assertacquirecalled(True)

        with self.assertRaises(error.LockInheritanceContractViolation):
            with lock.inherit():
                pass

        lock.release()
Exemplo n.º 5
0
Arquivo: hg.py Projeto: xmonader/eden
 def putbookmarks(self, updatedbookmark):
     if not len(updatedbookmark):
         return
     wlock = lock = tr = None
     try:
         wlock = self.repo.wlock()
         lock = self.repo.lock()
         tr = self.repo.transaction("bookmark")
         self.ui.status(_("updating bookmarks\n"))
         destmarks = self.repo._bookmarks
         changes = [(bookmark, nodemod.bin(updatedbookmark[bookmark]))
                    for bookmark in updatedbookmark]
         destmarks.applychanges(self.repo, tr, changes)
         tr.close()
     finally:
         lockmod.release(lock, wlock, tr)
Exemplo n.º 6
0
def _moveto(repo, bookmark, ctx, clean=False):
    """Moves the given bookmark and the working copy to the given revision.
    By default it does not overwrite the working copy contents unless clean is
    True.

    Assumes the wlock is already taken.
    """
    # Move working copy over
    if clean:
        merge.update(
            repo,
            ctx.node(),
            False,  # not a branchmerge
            True,  # force overwriting files
            None,
        )  # not a partial update
    else:
        # Mark any files that are different between the two as normal-lookup
        # so they show up correctly in hg status afterwards.
        wctx = repo[None]
        m1 = wctx.manifest()
        m2 = ctx.manifest()
        diff = m1.diff(m2)

        changedfiles = []
        changedfiles.extend(pycompat.iterkeys(diff))

        dirstate = repo.dirstate
        dirchanges = [f for f in dirstate if dirstate[f] != "n"]
        changedfiles.extend(dirchanges)

        if changedfiles or ctx.node() != repo["."].node():
            with dirstate.parentchange():
                dirstate.rebuild(ctx.node(), m2, changedfiles)

    # Move bookmark over
    if bookmark:
        lock = tr = None
        try:
            lock = repo.lock()
            tr = repo.transaction("reset")
            changes = [(bookmark, ctx.node())]
            repo._bookmarks.applychanges(repo, tr, changes)
            tr.close()
        finally:
            lockmod.release(lock, tr)
Exemplo n.º 7
0
    def testlockfork(self):
        state = teststate(self, tempfile.mkdtemp(dir=os.getcwd()))
        lock = state.makelock()
        state.assertacquirecalled(True)

        # fake a fork
        forklock = copy.deepcopy(lock)
        forklock._pidoffset = 1
        forklock.release()
        state.assertreleasecalled(False)
        state.assertpostreleasecalled(False)
        state.assertlockexists(True)

        # release the actual lock
        lock.release()
        state.assertreleasecalled(True)
        state.assertpostreleasecalled(True)
        state.assertlockexists(False)
Exemplo n.º 8
0
def fixupamend(ui, repo, noconflict=None, noconflictmsg=None):
    """rebases any children found on the preamend changset and strips the
    preamend changset
    """
    wlock = None
    lock = None
    tr = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()
        current = repo["."]

        # Use obsolescence information to fix up the amend.
        common.restackonce(
            ui, repo, current.rev(), noconflict=noconflict, noconflictmsg=noconflictmsg
        )
    finally:
        lockmod.release(wlock, lock, tr)
Exemplo n.º 9
0
    def testrecursivelock(self):
        state = teststate(self, tempfile.mkdtemp(dir=os.getcwd()))
        lock = state.makelock()
        state.assertacquirecalled(True)

        state.resetacquirefn()
        lock.lock()
        # recursive lock should not call acquirefn again
        state.assertacquirecalled(False)

        lock.release()  # brings lock refcount down from 2 to 1
        state.assertreleasecalled(False)
        state.assertpostreleasecalled(False)
        state.assertlockexists(True)

        lock.release()  # releases the lock
        state.assertreleasecalled(True)
        state.assertpostreleasecalled(True)
        state.assertlockexists(False)
Exemplo n.º 10
0
        def run(self):
            state = self.state
            repo, ctxnode = state.repo, state.parentctxnode
            with repo.wlock(), repo.lock(), repo.transaction("histedit"):
                hg.update(repo, ctxnode)

            # release locks so the program can call hg and then relock.
            lock.release(state.lock, state.wlock)

            try:
                ctx = repo[ctxnode]
                shell = encoding.environ.get("SHELL", None)
                cmd = self.command
                if shell and self.repo.ui.config("fbhistedit",
                                                 "exec_in_user_shell"):
                    cmd = "%s -c -i %s" % (shell, quote(cmd))
                rc = repo.ui.system(
                    cmd,
                    environ={"HGNODE": ctx.hex()},
                    cwd=self.cwd,
                    blockedtag="histedit_exec",
                )
            except OSError as ose:
                raise error.InterventionRequired(
                    _("Cannot execute command '%s': %s") % (self.command, ose))
            finally:
                # relock the repository
                state.wlock = repo.wlock()
                state.lock = repo.lock()
                repo.invalidateall()

            if rc != 0:
                raise error.InterventionRequired(
                    _("Command '%s' failed with exit status %d") %
                    (self.command, rc))

            m, a, r, d = self.repo.status()[:4]
            if m or a or r or d:
                self.continuedirty()
            return self.continueclean()
Exemplo n.º 11
0
        def testlockfork(self):
            state = teststate(self, tempfile.mkdtemp(dir=os.getcwd()))
            lock = state.makelock()
            state.assertacquirecalled(True)

            pid = os.fork()
            if pid == 0:
                lock._pidoffset = os.getpid()
                lock.release()
                state.assertreleasecalled(False)
                state.assertpostreleasecalled(False)
                state.assertlockexists(True)
                os._exit(0)

            _, status = os.waitpid(pid, 0)
            self.assertTrue(((status >> 8) & 0x7F) == 0)

            # release the actual lock
            lock.release()
            state.assertreleasecalled(True)
            state.assertpostreleasecalled(True)
            state.assertlockexists(False)
Exemplo n.º 12
0
def prune(ui, repo, *revs, **opts):
    """hide changesets by marking them obsolete

    Pruned changesets are obsolete with no successors. If they also have no
    descendants, they are hidden (invisible to all commands).

    Non-obsolete descendants of pruned changesets become "unstable". Use
    :hg:`evolve` to handle this situation.

    When you prune the parent of your working copy, Mercurial updates the
    working copy to a non-obsolete parent.

    You can use ``--succ`` to tell Mercurial that a newer version (successor)
    of the pruned changeset exists. Mercurial records successor revisions in
    obsolescence markers.

    You can use the ``--biject`` option to specify a 1-1 mapping (bijection)
    between revisions to pruned (precursor) and successor changesets. This
    option may be removed in a future release (with the functionality provided
    automatically).

    If you specify multiple revisions in ``--succ``, you are recording a
    "split" and must acknowledge it by passing ``--split``. Similarly, when you
    prune multiple changesets with a single successor, you must pass the
    ``--fold`` option.
    """
    if opts.get("keep", False):
        hint = "strip-uncommit"
    else:
        hint = "strip-hide"
    hintutil.trigger(hint)

    revs = scmutil.revrange(repo, list(revs) + opts.get("rev", []))
    succs = opts.get("succ", [])
    bookmarks = set(opts.get("bookmark", ()))
    metadata = _getmetadata(**opts)
    biject = opts.get("biject")
    fold = opts.get("fold")
    split = opts.get("split")

    options = [o for o in ("biject", "fold", "split") if opts.get(o)]
    if 1 < len(options):
        raise error.Abort(_("can only specify one of %s") % ", ".join(options))

    if bookmarks:
        revs += bookmarksmod.reachablerevs(repo, bookmarks)
        if not revs:
            # No revs are reachable exclusively from these bookmarks, just
            # delete the bookmarks.
            with repo.wlock(), repo.lock(), repo.transaction(
                    "prune-bookmarks") as tr:
                bookmarksmod.delete(repo, tr, bookmarks)
            for bookmark in sorted(bookmarks):
                ui.write(_("bookmark '%s' deleted\n") % bookmark)
            return 0

    if not revs:
        raise error.Abort(_("nothing to prune"))

    wlock = lock = tr = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()
        tr = repo.transaction("prune")
        # defines pruned changesets
        precs = []
        revs.sort()
        for p in revs:
            cp = repo[p]
            if not cp.mutable():
                # note: createmarkers() would have raised something anyway
                raise error.Abort(
                    "cannot prune immutable changeset: %s" % cp,
                    hint="see 'hg help phases' for details",
                )
            precs.append(cp)
        if not precs:
            raise error.Abort("nothing to prune")

        # defines successors changesets
        sucs = scmutil.revrange(repo, succs)
        sucs.sort()
        sucs = tuple(repo[n] for n in sucs)
        if not biject and len(sucs) > 1 and len(precs) > 1:
            msg = "Can't use multiple successors for multiple precursors"
            hint = _("use --biject to mark a series as a replacement"
                     " for another")
            raise error.Abort(msg, hint=hint)
        elif biject and len(sucs) != len(precs):
            msg = "Can't use %d successors for %d precursors" % (len(sucs),
                                                                 len(precs))
            raise error.Abort(msg)
        elif (len(precs) == 1 and len(sucs) > 1) and not split:
            msg = "please add --split if you want to do a split"
            raise error.Abort(msg)
        elif len(sucs) == 1 and len(precs) > 1 and not fold:
            msg = "please add --fold if you want to do a fold"
            raise error.Abort(msg)
        elif biject:
            relations = [(p, (s, )) for p, s in zip(precs, sucs)]
        else:
            relations = [(p, sucs) for p in precs]

        wdp = repo["."]

        if len(sucs) == 1 and len(precs) == 1 and wdp in precs:
            # '.' killed, so update to the successor
            newnode = sucs[0]
        else:
            # update to an unkilled parent
            newnode = wdp

            while newnode in precs or newnode.obsolete():
                newnode = newnode.parents()[0]

        if newnode.node() != wdp.node():
            if opts.get("keep", False):
                # This is largely the same as the implementation in
                # strip.stripcmd(). We might want to refactor this somewhere
                # common at some point.

                # only reset the dirstate for files that would actually change
                # between the working context and uctx
                descendantrevs = repo.revs("%d::." % newnode.rev())
                changedfiles = []
                for rev in descendantrevs:
                    # blindly reset the files, regardless of what actually
                    # changed
                    changedfiles.extend(repo[rev].files())

                # reset files that only changed in the dirstate too
                dirstate = repo.dirstate
                dirchanges = [f for f in dirstate if dirstate[f] != "n"]
                changedfiles.extend(dirchanges)
                repo.dirstate.rebuild(newnode.node(), newnode.manifest(),
                                      changedfiles)
                dirstate.write(tr)
            else:
                bookactive = repo._activebookmark
                # Active bookmark that we don't want to delete (with -B option)
                # we deactivate and move it before the update and reactivate it
                # after
                movebookmark = bookactive and not bookmarks
                if movebookmark:
                    bookmarksmod.deactivate(repo)
                    changes = [(bookactive, newnode.node())]
                    repo._bookmarks.applychanges(repo, tr, changes)
                commands.update(ui, repo, newnode.hex())
                ui.status(
                    _("working directory now at %s\n") %
                    ui.label(str(newnode), "evolve.node"))
                if movebookmark:
                    bookmarksmod.activate(repo, bookactive)

        # update bookmarks
        if bookmarks:
            with repo.wlock(), repo.lock(), repo.transaction(
                    "prune-bookmarks") as tr:
                bookmarksmod.delete(repo, tr, bookmarks)
            for bookmark in sorted(bookmarks):
                ui.write(_("bookmark '%s' deleted\n") % bookmark)

        # create markers
        obsolete.createmarkers(repo,
                               relations,
                               metadata=metadata,
                               operation="prune")

        # hide nodes
        visibility.remove(repo, [c.node() for c in precs])

        # informs that changeset have been pruned
        ui.status(_("%i changesets pruned\n") % len(precs))

        for ctx in repo.set("bookmark() and %ld", precs):
            # used to be:
            #
            #   ldest = list(repo.set('max((::%d) - obsolete())', ctx))
            #   if ldest:
            #      c = ldest[0]
            #
            # but then revset took a lazy arrow in the knee and became much
            # slower. The new forms makes as much sense and a much faster.
            for dest in ctx.ancestors():
                if not dest.obsolete():
                    updatebookmarks = common.bookmarksupdater(repo, ctx.node())
                    updatebookmarks(dest.node())
                    break

        tr.close()
    finally:
        lockmod.release(tr, lock, wlock)
Exemplo n.º 13
0
def _dounshelve(ui, repo, *shelved, **opts):
    abortf = opts.get("abort")
    continuef = opts.get("continue")
    if not abortf and not continuef:
        cmdutil.checkunfinished(repo)
    shelved = list(shelved)
    if opts.get("name"):
        shelved.append(opts["name"])

    if abortf or continuef:
        if abortf and continuef:
            raise error.Abort(_("cannot use both abort and continue"))
        if shelved:
            raise error.Abort(
                _("cannot combine abort/continue with "
                  "naming a shelved change"))
        if abortf and opts.get("tool", False):
            ui.warn(_("tool option will be ignored\n"))

        try:
            state = shelvedstate.load(repo)
            if opts.get("keep") is None:
                opts["keep"] = state.keep
        except IOError as err:
            if err.errno != errno.ENOENT:
                raise
            cmdutil.wrongtooltocontinue(repo, _("unshelve"))
        except error.CorruptedState as err:
            ui.debug(str(err) + "\n")
            if continuef:
                msg = _("corrupted shelved state file")
                hint = _("please run hg unshelve --abort to abort unshelve "
                         "operation")
                raise error.Abort(msg, hint=hint)
            elif abortf:
                msg = _("could not read shelved state file, your working copy "
                        "may be in an unexpected state\nplease update to some "
                        "commit\n")
                ui.warn(msg)
                shelvedstate.clear(repo)
            return

        if abortf:
            return unshelveabort(ui, repo, state, opts)
        elif continuef:
            return unshelvecontinue(ui, repo, state, opts)
    elif len(shelved) > 1:
        raise error.Abort(_("can only unshelve one change at a time"))
    elif not shelved:
        shelved = listshelves(repo)
        if not shelved:
            raise error.Abort(_("no shelved changes to apply!"))
        basename = util.split(shelved[0][1])[1]
        ui.status(_("unshelving change '%s'\n") % basename)
    else:
        basename = shelved[0]

    if not shelvedfile(repo, basename, patchextension).exists():
        raise error.Abort(_("shelved change '%s' not found") % basename)

    lock = tr = None
    obsshelve = True
    obsshelvedfile = shelvedfile(repo, basename, "oshelve")
    if not obsshelvedfile.exists():
        # although we can unshelve a obs-based shelve technically,
        # this particular shelve was created using a traditional way
        obsshelve = False
        ui.note(
            _("falling back to traditional unshelve since "
              "shelve was traditional"))
    try:
        lock = repo.lock()
        tr = repo.transaction("unshelve", report=lambda x: None)
        oldtiprev = len(repo)

        pctx = repo["."]
        tmpwctx = pctx
        # The goal is to have a commit structure like so:
        # ...-> pctx -> tmpwctx -> shelvectx
        # where tmpwctx is an optional commit with the user's pending changes
        # and shelvectx is the unshelved changes. Then we merge it all down
        # to the original pctx.

        activebookmark = _backupactivebookmark(repo)
        tmpwctx, addedbefore = _commitworkingcopychanges(
            ui, repo, opts, tmpwctx)
        repo, shelvectx = _unshelverestorecommit(ui, repo, basename, obsshelve)
        _checkunshelveuntrackedproblems(ui, repo, shelvectx)
        branchtorestore = ""
        if shelvectx.branch() != shelvectx.p1().branch():
            branchtorestore = shelvectx.branch()

        rebaseconfigoverrides = {
            ("ui", "forcemerge"): opts.get("tool", ""),
            ("experimental", "rebaseskipobsolete"): "off",
        }
        with ui.configoverride(rebaseconfigoverrides, "unshelve"):
            shelvectx = _rebaserestoredcommit(
                ui,
                repo,
                opts,
                tr,
                oldtiprev,
                basename,
                pctx,
                tmpwctx,
                shelvectx,
                branchtorestore,
                activebookmark,
                obsshelve,
            )
            mergefiles(ui, repo, pctx, shelvectx)
            restorebranch(ui, repo, branchtorestore)
            _forgetunknownfiles(repo, shelvectx, addedbefore)

        if obsshelve:
            _obsoleteredundantnodes(repo, tr, pctx, shelvectx, tmpwctx)

        shelvedstate.clear(repo)
        _finishunshelve(repo, oldtiprev, tr, activebookmark, obsshelve)
        unshelvecleanup(ui, repo, basename, opts)
    finally:
        if tr:
            tr.release()
        lockmod.release(lock)
Exemplo n.º 14
0
def rewrite(repo, old, updates, head, newbases, commitopts, mutop=None):
    """Return (nodeid, created) where nodeid is the identifier of the
    changeset generated by the rewrite process, and created is True if
    nodeid was actually created. If created is False, nodeid
    references a changeset existing before the rewrite call.
    """
    wlock = lock = tr = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()
        tr = repo.transaction("rewrite")
        if len(old.parents()) > 1:  # XXX remove this unnecessary limitation.
            raise error.Abort(_("cannot amend merge changesets"))
        base = old.p1()
        updatebookmarks = bookmarksupdater(repo, [old.node()] +
                                           [u.node() for u in updates])

        # commit a new version of the old changeset, including the update
        # collect all files which might be affected
        files = set(old.files())
        for u in updates:
            files.update(u.files())

        # Recompute copies (avoid recording a -> b -> a)
        copied = copies.pathcopies(base, head)

        # prune files which were reverted by the updates
        def samefile(f):
            if f in head.manifest():
                a = head.filectx(f)
                if f in base.manifest():
                    b = base.filectx(f)
                    return a.data() == b.data() and a.flags() == b.flags()
                else:
                    return False
            else:
                return f not in base.manifest()

        files = [f for f in files if not samefile(f)]
        # commit version of these files as defined by head
        headmf = head.manifest()

        def filectxfn(repo, ctx, path):
            if path in headmf:
                fctx = head[path]
                flags = fctx.flags()
                mctx = context.memfilectx(
                    repo,
                    ctx,
                    fctx.path(),
                    fctx.data(),
                    islink="l" in flags,
                    isexec="x" in flags,
                    copied=copied.get(path),
                )
                return mctx
            return None

        message = cmdutil.logmessage(repo, commitopts)
        if not message:
            message = old.description()

        user = commitopts.get("user") or old.user()
        # TODO: In case not date is given, we should take the old commit date
        # if we are working one one changeset or mimic the fold behavior about
        # date
        date = commitopts.get("date") or None
        extra = dict(commitopts.get("extra", old.extra()))
        extra["branch"] = head.branch()
        mutation.record(repo, extra, [c.node() for c in updates], mutop)
        loginfo = {
            "predecessors": " ".join(c.hex() for c in updates),
            "mutation": mutop,
        }

        new = context.memctx(
            repo,
            parents=newbases,
            text=message,
            files=files,
            filectxfn=filectxfn,
            user=user,
            date=date,
            extra=extra,
            loginfo=loginfo,
        )

        if commitopts.get("edit"):
            new._text = cmdutil.commitforceeditor(repo, new, [])
        revcount = len(repo)
        newid = repo.commitctx(new)
        new = repo[newid]
        created = len(repo) != revcount
        updatebookmarks(newid)

        tr.close()
        return newid, created
    finally:
        lockmod.release(tr, lock, wlock)
Exemplo n.º 15
0
def amend(ui, repo, *pats, **opts):
    """save pending changes to the current commit

    Replaces your current commit with a new commit that contains the contents
    of the original commit, plus any pending changes.

    By default, all pending changes (in other words, those reported by 'hg
    status') are committed. To commit only some of your changes,
    you can:

    - Specify an exact list of files for which you want changes committed.

    - Use the -I or -X flags to pattern match file names to exclude or
      include by using a fileset. See 'hg help filesets' for more
      information.

    - Specify the --interactive flag to open a UI that will enable you
      to select individual insertions or deletions.

    By default, hg amend reuses your existing commit message and does not
    prompt you for changes. To change your commit message, you can:

    - Specify --edit / -e to open your configured editor to update the
      existing commit message.

    - Specify --message / -m to replace the entire commit message, including
      any commit template fields, with a string that you specify.

    .. note::

       Specifying -m overwrites all information in the commit message,
       including information specified as part of a pre-loaded commit
       template. For example, any information associating this commit with
       a code review system will be lost and might result in breakages.

    When you amend a commit that has descendants, those descendants are
    rebased on top of the amended version of the commit, unless doing so
    would result in merge conflicts. If this happens, run 'hg restack'
    to manually trigger the rebase so that you can go through the merge
    conflict resolution process.
    """
    # 'rebase' is a tristate option: None=auto, True=force, False=disable
    rebase = opts.get("rebase")
    to = opts.get("to")

    if rebase and _histediting(repo):
        # if a histedit is in flight, it's dangerous to remove old commits
        hint = _("during histedit, use amend without --rebase")
        raise error.Abort("histedit in progress", hint=hint)

    badflags = [flag for flag in ["rebase", "fixup"] if opts.get(flag, None)]
    if opts.get("interactive") and badflags:
        raise error.Abort(
            _("--interactive and --%s are mutually exclusive") % badflags[0])

    fixup = opts.get("fixup")

    badtoflags = [
        "rebase",
        "fixup",
        "addremove",
        "edit",
        "interactive",
        "include",
        "exclude",
        "message",
        "logfile",
        "date",
        "user",
        "no-move-detection",
        "stack",
        "template",
    ]

    if to and any(opts.get(flag, None) for flag in badtoflags):
        raise error.Abort(_("--to cannot be used with any other options"))

    if fixup:
        ui.warn(
            _("warning: --fixup is deprecated and WILL BE REMOVED. use 'hg restack' instead.\n"
              ))
        fixupamend(ui, repo)
        return

    if to:
        amendtocommit(ui, repo, to)
        return

    old = repo["."]
    if old.phase() == phases.public:
        raise error.Abort(_("cannot amend public changesets"))
    if len(repo[None].parents()) > 1:
        raise error.Abort(_("cannot amend while merging"))

    haschildren = len(old.children()) > 0

    opts["message"] = cmdutil.logmessage(repo, opts)
    # Avoid further processing of any logfile. If such a file existed, its
    # contents have been copied into opts['message'] by logmessage
    opts["logfile"] = ""

    if not opts.get("noeditmessage") and not opts.get("message"):
        opts["message"] = old.description()

    commitdate = opts.get("date")
    if not commitdate:
        if ui.config("amend", "date") == "implicitupdate":
            commitdate = "now"
        else:
            commitdate = old.date()

    oldbookmarks = old.bookmarks()
    tr = None
    wlock = None
    lock = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        if opts.get("interactive"):
            # Strip the interactive flag to avoid infinite recursive loop
            opts.pop("interactive")
            cmdutil.dorecord(ui, repo, amend, None, False,
                             cmdutil.recordfilter, *pats, **opts)
            return

        else:
            node = cmdutil.amend(ui, repo, old, {}, pats, opts)

        if node == old.node():
            ui.status(_("nothing changed\n"))
            return 1

        conf = ui.config("amend", "autorestack", RESTACK_DEFAULT)
        noconflict = None

        # RESTACK_NO_CONFLICT requires IMM.
        if conf == RESTACK_NO_CONFLICT and not ui.config(
                "rebase", "experimental.inmemory", False):
            conf = RESTACK_DEFAULT

        # If they explicitly disabled the old behavior, disable the new behavior
        # too, for now.
        # internal config: commands.amend.autorebase
        if ui.configbool("commands", "amend.autorebase") is False:
            # In the future we'll add a nag message here.
            conf = RESTACK_NEVER

        if conf not in RESTACK_VALUES:
            ui.warn(
                _('invalid amend.autorestack config of "%s"; falling back to %s\n'
                  ) % (conf, RESTACK_DEFAULT))
            conf = RESTACK_DEFAULT

        if haschildren and rebase is None and not _histediting(repo):
            if conf == RESTACK_ALWAYS:
                rebase = True
            elif conf == RESTACK_NO_CONFLICT:
                if repo[None].dirty():
                    # For now, only restack if the WC is clean (t31742174).
                    ui.status(
                        _("not restacking because working copy is dirty\n"))
                    rebase = False
                else:
                    # internal config: amend.autorestackmsg
                    msg = ui.config(
                        "amend",
                        "autorestackmsg",
                        _("restacking children automatically (unless they conflict)"
                          ),
                    )
                    if msg:
                        ui.status("%s\n" % msg)
                    rebase = True
                    noconflict = True
            elif conf == RESTACK_ONLY_TRIVIAL:
                newcommit = repo[node]
                # If the rebase did not change the manifest and the
                # working copy is clean, force the children to be
                # restacked.
                rebase = (old.manifestnode() == newcommit.manifestnode()
                          and not repo[None].dirty())
                if rebase:
                    hintutil.trigger("amend-autorebase")
            else:
                rebase = False

        if haschildren and not rebase and not _histediting(repo):
            hintutil.trigger("amend-restack", old.node())

        changes = []
        # move old bookmarks to new node
        for bm in oldbookmarks:
            changes.append((bm, node))

        tr = repo.transaction("fixupamend")
        repo._bookmarks.applychanges(repo, tr, changes)
        tr.close()

        if rebase and haschildren:
            noconflictmsg = _(
                "restacking would create conflicts (%s in %s), so you must run it manually\n(run `hg restack` manually to restack this commit's children)"
            )
            revs = [
                c.hex() for c in repo.set("(%n::)-%n", old.node(), old.node())
            ]
            with ui.configoverride({
                ("rebase", "noconflictmsg"): noconflictmsg
            }):
                # Note: this has effects on linearizing (old:: - old). That can
                # fail. If that fails, it might make sense to try a plain
                # rebase -s (old:: - old) -d new.
                restack.restack(ui, repo, rev=revs, noconflict=noconflict)

        showtemplate(ui, repo, repo[node], **opts)
    finally:
        lockmod.release(wlock, lock, tr)