Ejemplo n.º 1
0
def _iscreatedbyfb(path):
    """Returns True if path was created by FB.

    This function is very slow. So it uses ~/.cache/testutil/authordb/ as cache.
    """
    cachepath = os.path.expanduser("~/.cache/testutil/authordb/%s" %
                                   hashlib.sha1(path).hexdigest())
    if not os.path.exists(cachepath):
        util.makedirs(os.path.dirname(cachepath))
        lines = sorted(
            subprocess.check_output(
                ["hg", "log", "-f", "-T{author|email}\n", path]).splitlines())
        result = all(l.endswith("@fb.com") for l in lines)
        open(cachepath, "w").write(repr(result))
    return ast.literal_eval(util.readfile(cachepath))
Ejemplo n.º 2
0
def symlink_failure(src, dst):
    raise OSError(1, "Operation not permitted")


os.symlink = symlink_failure
fscap.getfscap = lambda *args: None


def islink_failure(path):
    return False


os.path.islink = islink_failure

# dereference links as if a Samba server has exported this to a
# Windows client
for f in "test0/a.lnk", "test0/d/b.lnk":
    os.unlink(f)
    fp = open(f, "wb")
    fp.write(util.readfile(f[:-4]))
    fp.close()

# reload repository
u = uimod.ui.load()
repo = hg.repository(u, "test0")
commands.status(u, repo)

# try cloning a repo which contains symlinks
u = uimod.ui.load()
hg.clone(u, {}, BUNDLEPATH, "test1")
Ejemplo n.º 3
0
def metaedit(ui, repo, templ, *revs, **opts):
    """edit commit message and other metadata

    Edit commit message for the current commit. By default, opens your default
    editor so that you can edit the commit message interactively. Specify -m
    to specify the commit message on the command line.

    To edit the message for a different commit, specify -r. To edit the
    messages of multiple commits, specify --batch.

    You can edit other pieces of commit metadata, namely the user or date,
    by specifying -u or -d, respectively. The expected format for user is
    'Full Name <*****@*****.**>'.

    There is also automation-friendly JSON input mode which allows the caller
    to provide the mapping between commit and new message and username in the
    following format:

        {
            "<commit_hash>": {
                "message": "<message>",
                "user": "******" // optional
            }
        }

    .. note::

        You can specify --fold to fold multiple revisions into one when the
        given revisions form a linear unbroken chain. However, :hg:`fold` is
        the preferred command for this purpose. See :hg:`help fold` for more
        information.

    .. container:: verbose

     Some examples:

     - Edit the commit message for the current commit::

         hg metaedit

     - Change the username for the current commit::

         hg metaedit --user 'New User <*****@*****.**>'

    """
    revs = list(revs)
    revs.extend(opts["rev"])
    if not revs:
        if opts["fold"]:
            raise error.Abort(_("revisions must be specified with --fold"))
        revs = ["."]

    with repo.wlock(), repo.lock():
        revs = scmutil.revrange(repo, revs)
        msgmap = {
        }  # {node: message}, predefined messages, currently used by --batch
        usermap = {
        }  # {node: author}, predefined authors, used by --jsoninputfile

        if opts["fold"]:
            root, head = fold._foldcheck(repo, revs)
        else:
            if repo.revs("%ld and public()", revs):
                raise error.Abort(
                    _("cannot edit commit information for public "
                      "revisions"))
            root = head = repo[revs.first()]

        wctx = repo[None]
        p1 = wctx.p1()
        tr = repo.transaction("metaedit")
        newp1 = None
        try:
            commitopts = opts.copy()
            allctx = [repo[r] for r in revs]
            jsoninputfile = None

            if any(
                    commitopts.get(name)
                    for name in ["message", "logfile", "reuse_message"]):
                commitopts["edit"] = False
            else:
                if opts["fold"]:
                    msgs = [
                        _("HG: This is a fold of %d changesets.") % len(allctx)
                    ]
                    msgs += [
                        _("HG: Commit message of %s.\n\n%s\n") %
                        (nodemod.short(c.node()), c.description())
                        for c in allctx
                    ]
                else:
                    if opts["batch"] and len(revs) > 1:
                        msgmap = editmessages(repo, revs)

                    msgs = [head.description()]
                    jsoninputfile = opts.get("json_input_file")
                    if jsoninputfile:
                        try:
                            if cmdutil.isstdiofilename(jsoninputfile):
                                inputjson = pycompat.decodeutf8(ui.fin.read())
                            else:
                                inputjson = pycompat.decodeutf8(
                                    util.readfile(jsoninputfile))
                            msgusermap = json.loads(inputjson)
                        except IOError as inst:
                            raise error.Abort(
                                _("can't read JSON input file '%s': %s") %
                                (jsoninputfile,
                                 encoding.strtolocal(inst.strerror)))
                        except ValueError as inst:
                            raise error.Abort(
                                _("can't decode JSON input file '%s': %s") %
                                (jsoninputfile, str(inst)))

                        if not isinstance(msgusermap, dict):
                            raise error.Abort(
                                _("JSON input is not a dictionary (see --help for input format)"
                                  ))

                        try:
                            msgmap = {
                                bin(node): msguser.get("message")
                                for (node, msguser) in msgusermap.items()
                                if "message" in msguser
                            }

                            usermap = {
                                bin(node): msguser.get("user")
                                for (node, msguser) in msgusermap.items()
                                if "user" in msguser
                            }
                        except TypeError:
                            raise error.Abort(_("invalid JSON input"))

                commitopts["message"] = "\n".join(msgs)
                commitopts["edit"] = True

            if root == head:
                # fast path: use metarewrite
                replacemap = {}
                # adding commitopts to the revisions to metaedit
                allctxopt = [{
                    "ctx": ctx,
                    "commitopts": commitopts
                } for ctx in allctx]
                # all descendats that can be safely rewritten
                newunstable = common.newunstable(repo, revs)
                newunstableopt = [{
                    "ctx": ctx
                } for ctx in [repo[r] for r in newunstable]]
                # we need to edit descendants with the given revisions to not to
                # corrupt the stacks
                if _histediting(repo):
                    ui.note(
                        _("during histedit, the descendants of "
                          "the edited commit weren't auto-rebased\n"))
                else:
                    allctxopt += newunstableopt
                # we need topological order for all
                if mutation.enabled(repo):
                    allctxopt = mutation.toposort(
                        repo,
                        allctxopt,
                        nodefn=lambda copt: copt["ctx"].node())
                else:
                    allctxopt = sorted(allctxopt,
                                       key=lambda copt: copt["ctx"].rev())

                def _rewritesingle(c, _commitopts):
                    # Predefined message overrides other message editing choices.
                    msg = msgmap.get(c.node())
                    if jsoninputfile:
                        _commitopts["edit"] = False
                    if msg is not None:
                        _commitopts["message"] = msg
                        _commitopts["edit"] = False
                    user = usermap.get(c.node())
                    if user is not None:
                        _commitopts["user"] = user
                    if _commitopts.get("edit", False):
                        msg = "HG: Commit message of changeset %s\n%s" % (
                            str(c),
                            c.description(),
                        )
                        _commitopts["message"] = msg
                    bases = [
                        replacemap.get(c.p1().node(),
                                       c.p1().node()),
                        replacemap.get(c.p2().node(),
                                       c.p2().node()),
                    ]
                    newid, created = common.metarewrite(repo,
                                                        c,
                                                        bases,
                                                        commitopts=_commitopts)
                    if created:
                        replacemap[c.node()] = newid

                for copt in allctxopt:
                    _rewritesingle(
                        copt["ctx"],
                        copt.get("commitopts",
                                 {"date": commitopts.get("date") or None}),
                    )

                if p1.node() in replacemap:
                    repo.setparents(replacemap[p1.node()])
                if len(replacemap) > 0:
                    mapping = dict(
                        map(
                            lambda oldnew: (oldnew[0], [oldnew[1]]),
                            pycompat.iteritems(replacemap),
                        ))
                    templ.setprop("nodereplacements", mapping)
                    scmutil.cleanupnodes(repo, mapping, "metaedit")
                    # TODO: set poroper phase boundaries (affects secret
                    # phase only)
                else:
                    ui.status(_("nothing changed\n"))
                    return 1
            else:
                # slow path: create a new commit
                targetphase = max(c.phase() for c in allctx)

                # TODO: if the author and message are the same, don't create a
                # new hash. Right now we create a new hash because the date can
                # be different.
                newid, created = common.rewrite(
                    repo,
                    root,
                    allctx,
                    head,
                    [root.p1().node(), root.p2().node()],
                    commitopts=commitopts,
                    mutop="metaedit",
                )
                if created:
                    if p1.rev() in revs:
                        newp1 = newid
                    phases.retractboundary(repo, tr, targetphase, [newid])
                    mapping = dict([(repo[rev].node(), [newid])
                                    for rev in revs])
                    templ.setprop("nodereplacements", mapping)
                    scmutil.cleanupnodes(repo, mapping, "metaedit")
                else:
                    ui.status(_("nothing changed\n"))
                    return 1
            tr.close()
        finally:
            tr.release()

        if opts["fold"]:
            ui.status(_("%i changesets folded\n") % len(revs))
        if newp1 is not None:
            hg.update(repo, newp1)
Ejemplo n.º 4
0
def _summarize(repo, workingfilectx, otherctx, basectx):
    origfile = (
        None
        if workingfilectx.isabsent()
        else scmutil.origpath(repo.ui, repo, repo.wjoin(workingfilectx.path()))
    )

    def flags(context):
        if isinstance(context, absentfilectx):
            return {
                "contents": None,
                "exists": False,
                "isexec": None,
                "issymlink": None,
            }
        return {
            "contents": pycompat.decodeutf8(context.data()),
            "exists": True,
            "isexec": context.isexec(),
            "issymlink": context.islink(),
        }

    output = flags(workingfilectx)

    filestat = util.filestat.frompath(origfile) if origfile is not None else None
    if origfile and filestat.stat:
        # Since you can start a merge with a dirty working copy (either via
        # `up` or `merge -f`), "local" must reflect that, not the underlying
        # changeset. Those contents are available in the .orig version, so we
        # look there and mock up the schema to look like the other contexts.
        #
        # Test cases affected in test-merge-conflict-cornercases.t: #0
        local = {
            "contents": pycompat.decodeutf8(util.readfile(origfile)),
            "exists": True,
            "isexec": util.isexec(origfile),
            "issymlink": util.statislink(filestat.stat),
        }
    else:
        # No backup file. This happens whenever the merge was esoteric enough
        # that we didn't launch a merge tool*, and instead prompted the user to
        # "use (c)hanged version, (d)elete, or leave (u)nresolved".
        #
        # The only way to exit that prompt with a conflict is to choose "u",
        # which leaves the local version in the working copy (with all its
        # pre-merge properties including any local changes), so we can reuse
        # that.
        #
        # Affected test cases: #0b, #1, #6, #11, and #12.
        #
        # Another alternative might be to use repo['.'][path] but that wouldn't
        # have any dirty pre-merge changes.
        #
        # *If we had, we'd've we would've overwritten the working copy, made a
        # backup and hit the above case.
        #
        # Copy, so the addition of the `path` key below does not affect both
        # versions.
        local = copy.copy(output)

    output["path"] = repo.wjoin(workingfilectx.path())

    return {
        "base": flags(basectx),
        "local": local,
        "other": flags(otherctx),
        "output": output,
        "path": workingfilectx.path(),
    }