Пример #1
0
def _amend(orig, ui, repo, old, extra, pats, opts):
    """Wraps amend to collect copytrace data on amend

    If a file is created in one commit, modified in a subsequent commit, and
    then renamed or copied by amending the original commit, restacking the
    commits that modify the file will fail:

    file modified here    B     B'  restack of B to B' will fail
                          |     :
    file created here     A --> A'  file renamed in amended commit
                          |    /
                          o --

    This function collects information about copies and renames from amend
    commits, and saves it for use during rebases onto the amend commit.  This
    lets rebases onto files that been renamed or copied in an amend commit
    work without conflicts.

    This function collects the copytrace information from the working copy and
    stores it against the amended commit in a separate dbm file. Later,
    in _domergecopies, this information will be merged with the rebase
    copytrace data to incorporate renames and copies made during the amend.
    """

    # Check if amend copytracing has been disabled.
    if not ui.configbool("copytrace", "enableamendcopytrace"):
        return orig(ui, repo, old, extra, pats, opts)

    # Need to get the amend-copies before calling the command because files from
    # the working copy will be used during the amend.
    wctx = repo[None]

    # Find the amend-copies.
    matcher = scmutil.match(wctx, pats, opts)
    amend_copies = copiesmod.pathcopies(old, wctx, matcher)

    # Finally, invoke the command.
    node = orig(ui, repo, old, extra, pats, opts)
    amended_ctx = repo[node]

    # Store the amend-copies against the amended context.
    if amend_copies:
        db, error = opendbm(repo, "c")
        if db is None:
            # Database locked, can't record these amend-copies.
            ui.log("copytrace", "Failed to open amendcopytrace db: %s" % error)
            return node

        # Merge in any existing amend copies from any previous amends.
        try:
            orig_data = db[old.node()]
        except KeyError:
            orig_data = "{}"
        except error as e:
            ui.log(
                "copytrace",
                "Failed to read key %s from amendcopytrace db: %s" %
                (old.hex(), e),
            )
            return node

        orig_encoded = json.loads(orig_data)
        orig_amend_copies = dict(
            (
                pycompat.decodeutf8(
                    codecs.decode(pycompat.encodeutf8(k), "base64")),
                pycompat.decodeutf8(
                    codecs.decode(pycompat.encodeutf8(v), "base64")),
            ) for (k, v) in pycompat.iteritems(orig_encoded))

        # Copytrace information is not valid if it refers to a file that
        # doesn't exist in a commit.  We need to update or remove entries
        # that refer to files that might have only existed in the previous
        # amend commit.
        #
        # Find chained copies and renames (a -> b -> c) and collapse them to
        # (a -> c).  Delete the entry for b if this was a rename.
        for dst, src in pycompat.iteritems(amend_copies):
            if src in orig_amend_copies:
                amend_copies[dst] = orig_amend_copies[src]
                if src not in amended_ctx:
                    del orig_amend_copies[src]

        # Copy any left over copies from the previous context.
        for dst, src in pycompat.iteritems(orig_amend_copies):
            if dst not in amend_copies:
                amend_copies[dst] = src

        # Write out the entry for the new amend commit.
        encoded = dict(
            (
                pycompat.decodeutf8(
                    codecs.encode(pycompat.encodeutf8(k), "base64")),
                pycompat.decodeutf8(
                    codecs.encode(pycompat.encodeutf8(v), "base64")),
            ) for (k, v) in pycompat.iteritems(amend_copies))
        db[node] = json.dumps(encoded)
        try:
            db.close()
        except Exception as e:
            # Database corruption.  Not much we can do, so just log.
            ui.log("copytrace", "Failed to close amendcopytrace db: %s" % e)

    return node
Пример #2
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()
        mutinfo = 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,
            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)
Пример #3
0
 def d():
     copies.pathcopies(ctx1, ctx2)
Пример #4
0
def debugfillinfinitepushmetadata(ui, repo, **opts):
    """Special command that fills infinitepush metadata for a node
    """

    nodes = opts["node"]
    if not nodes:
        raise error.Abort(_("nodes are not specified"))

    filelimit = ui.configint("infinitepush", "metadatafilelimit", 100)
    nodesmetadata = {}
    for node in nodes:
        index = repo.bundlestore.index
        if not bool(index.getbundle(node)):
            raise error.Abort(_("node %s is not found") % node)

        if node not in repo:
            newbundlefile = server.downloadbundle(repo, bin(node))
            bundlepath = "bundle:%s+%s" % (repo.root, newbundlefile)
            bundlerepo = hg.repository(ui, bundlepath)
            repo = bundlerepo

        p1 = repo[node].p1().node()
        diffopts = patch.diffallopts(ui, {})
        match = scmutil.matchall(repo)
        chunks = patch.diff(repo, p1, node, match, None, diffopts, relroot="")
        difflines = util.iterlines(chunks)

        states = "modified added removed deleted unknown ignored clean".split()
        status = repo.status(p1, node)
        status = zip(states, status)

        filestatus = {}
        for state, files in status:
            for f in files:
                filestatus[f] = state

        diffstat = patch.diffstatdata(difflines)
        changed_files = {}
        copies = copiesmod.pathcopies(repo[p1], repo[node])
        for filename, adds, removes, isbinary in diffstat[:filelimit]:
            # use special encoding that allows non-utf8 filenames
            filename = pycompat.decodeutf8(
                encoding.jsonescape(pycompat.encodeutf8(filename),
                                    paranoid=True))
            changed_files[filename] = {
                "adds": adds,
                "removes": removes,
                "isbinary": isbinary,
                "status": filestatus.get(filename, "unknown"),
            }
            if filename in copies:
                changed_files[filename]["copies"] = copies[filename]

        output = {}
        output["changed_files"] = changed_files
        if len(diffstat) > filelimit:
            output["changed_files_truncated"] = True
        nodesmetadata[node] = output

    with index:
        for node, metadata in pycompat.iteritems(nodesmetadata):
            dumped = json.dumps(metadata, sort_keys=True)
            index.saveoptionaljsonmetadata(node, pycompat.encodeutf8(dumped))