Esempio n. 1
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)
Esempio n. 2
0
def commitextra(ui, repo, *pats, **opts):
    """make a commit with extra fields"""
    fields = opts.get("field")
    extras = {}
    for field in fields:
        k, v = field.split("=", 1)
        extras[k] = v
    message = cmdutil.logmessage(repo, opts)
    repo.commit(
        message,
        opts.get("user"),
        opts.get("date"),
        match=scmutil.match(repo[None], pats, opts),
        extra=extras,
    )
    return 0
Esempio n. 3
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)
Esempio n. 4
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)