コード例 #1
0
ファイル: perf.py プロジェクト: leszfb/eden
def perfpathcopies(ui, repo, rev1, rev2, **opts):
    timer, fm = gettimer(ui, opts)
    ctx1 = scmutil.revsingle(repo, rev1, rev1)
    ctx2 = scmutil.revsingle(repo, rev2, rev2)

    def d():
        copies.pathcopies(ctx1, ctx2)

    timer(d)
    fm.end()
コード例 #2
0
def perftestsuitecmd(ui, repo, *revs, **opts):
    """Runs an in-depth performance suite and logs results to a metrics
    framework.

    The rebase distance is configurable::

        [perfsuite]
        rebase.masterdistance = 100
        immrebase.masterdistance = 100

    The metrics endpoint is configurable::

        [ods]
        endpoint = https://somehost/metrics
    """
    if opts["seed"]:
        random.seed(opts["seed"])

    if opts["rev"]:
        ui.status(_("updating to %s...\n") % (opts["rev"]))
        commands.update(ui, repo, scmutil.revsingle(repo, opts["rev"]).hex())

    suite = perftestsuite(
        repo,
        publish=opts["publish"],
        profile=opts["use_profile"],
        printout=opts["print"],
    )
    suite.run()
コード例 #3
0
ファイル: perf.py プロジェクト: leszfb/eden
def perfmergecalculate(ui, repo, rev, **opts):
    timer, fm = gettimer(ui, opts)
    wctx = repo[None]
    rctx = scmutil.revsingle(repo, rev, rev)
    ancestor = wctx.ancestor(rctx)
    # we don't want working dir files to be stat'd in the benchmark, so prime
    # that cache
    wctx.dirty()

    def d():
        # acceptremote is True because we don't want prompts in the middle of
        # our benchmark
        merge.calculateupdates(
            repo,
            wctx,
            rctx,
            [ancestor],
            False,
            False,
            acceptremote=True,
            followcopies=True,
        )

    timer(d)
    fm.end()
コード例 #4
0
ファイル: tweakdefaults.py プロジェクト: xmonader/eden
def _rebase(orig, ui, repo, *pats, **opts):
    if not opts.get("date") and not ui.configbool("tweakdefaults",
                                                  "rebasekeepdate"):
        opts["date"] = currentdate()

    if opts.get("continue") or opts.get("abort") or opts.get("restack"):
        return orig(ui, repo, *pats, **opts)

    # 'hg rebase' w/o args should do nothing
    if not opts.get("dest"):
        raise error.Abort("you must specify a destination (-d) for the rebase")

    # 'hg rebase' can fast-forward bookmark
    prev = repo["."]

    # Only fast-forward the bookmark if no source nodes were explicitly
    # specified.
    if not (opts.get("base") or opts.get("source") or opts.get("rev")):
        dest = scmutil.revsingle(repo, opts.get("dest"))
        common = dest.ancestor(prev)
        if prev == common:
            activebookmark = repo._activebookmark
            result = hg.updatetotally(ui, repo, dest.node(), activebookmark)
            if activebookmark:
                with repo.wlock():
                    bookmarks.update(repo, [prev.node()], dest.node())
            return result

    return orig(ui, repo, *pats, **opts)
コード例 #5
0
def _filterfetchpaths(repo, paths):
    """return a subset of paths whose history is long and need to fetch linelog
    from the server. works with remotefilelog and non-remotefilelog repos.
    """
    threshold = repo.ui.configint("fastannotate", "clientfetchthreshold", 10)
    if threshold <= 0:
        return paths

    master = repo.ui.config("fastannotate", "mainbranch") or "default"

    if "remotefilelog" in repo.requirements:
        ctx = scmutil.revsingle(repo, master)
        f = lambda path: len(ctx[path].ancestormap())
    else:
        f = lambda path: len(repo.file(path))

    result = []
    for path in paths:
        try:
            if f(path) >= threshold:
                result.append(path)
        except Exception:  # file not found etc.
            result.append(path)

    return result
コード例 #6
0
def _fullclean(ui, repo, exclude):
    start_time = time.perf_counter()
    ui.status(_("cleaning up uncommitted code\n"), component="snapshot")
    # The order of operations to cleanup here is very deliberate, to avoid errors.
    # Most errors happen due to file/dir clashes, see https://fburl.com/jwyhd0fk
    # Step 1: Forget files that were "hg added"
    # WARNING: Don't call cmdutil.forget because it might be slow
    wctx = repo[None]
    forget = set(wctx.added()) - set(exclude)
    if forget:
        wctx.forget(list(forget), "")
    # Step 2: Remove "untracked changes" (e.g. untracked files)
    repo.dirstate._fs.purge(
        scmutil.match(repo[None], opts={"exclude": exclude}),
        removefiles=True,
        removedirs=True,
        removeignored=False,
        dryrun=False,
    )
    # Step 3: Remove "tracked changes"
    cmdutil.revert(
        ui,
        repo,
        scmutil.revsingle(repo, None),
        repo.dirstate.parents(),
        all=True,
        no_backup=True,
        exclude=exclude,
    )
    duration = time.perf_counter() - start_time
    ui.status(
        _("cleaned up uncommitted code in {duration:0.5f} seconds\n").format(
            duration=duration),
        component="snapshot",
    )
コード例 #7
0
ファイル: __init__.py プロジェクト: jsoref/eden
def gverify(ui, repo, **opts):
    """verify that a Mercurial rev matches the corresponding Git rev

    Given a Mercurial revision that has a corresponding Git revision in the map,
    this attempts to answer whether that revision has the same contents as the
    corresponding Git revision.

    """
    ctx = scmutil.revsingle(repo, opts.get("rev"), ".")
    return verify.verify(ui, repo, ctx)
コード例 #8
0
ファイル: perf.py プロジェクト: leszfb/eden
def perfmanifest(ui, repo, rev, **opts):
    timer, fm = gettimer(ui, opts)
    ctx = scmutil.revsingle(repo, rev, rev)
    t = ctx.manifestnode()

    def d():
        repo.manifestlog.clearcaches()
        repo.manifestlog[t].read()

    timer(d)
    fm.end()
コード例 #9
0
def link(ui, repo, *args, **opts):
    if len(args) != 1:
        raise error.Abort(_("must specify a pull request"))

    pr_arg = args[0]
    pull_request = resolve_pr_arg(pr_arg, ui)
    if not pull_request:
        raise error.Abort(_("could not resolve pull request: '%%s'") % pr_arg)

    ctx = scmutil.revsingle(repo, opts.get("rev"), None)
    pr_store = PullRequestStore(repo)
    pr_store.map_commit_to_pull_request(ctx.node(), pull_request)
コード例 #10
0
def debugbuildannotatecache(ui, repo, *pats, **opts):
    """incrementally build fastannotate cache up to REV for specified files

    If REV is not specified, use the config 'fastannotate.mainbranch'.

    If fastannotate.client is True, download the annotate cache from the
    server. Otherwise, build the annotate cache locally.

    The annotate cache will be built using the default diff and follow
    options and lives in '.hg/fastannotate/default'.
    """
    rev = opts.get("REV") or ui.config("fastannotate", "mainbranch")
    if not rev:
        raise error.Abort(
            _("you need to provide a revision"),
            hint=_("set fastannotate.mainbranch or use --rev"),
        )
    if ui.configbool("fastannotate", "unfilteredrepo", True):
        repo = repo.unfiltered()
    ctx = scmutil.revsingle(repo, rev)
    m = scmutil.match(ctx, pats, opts)
    paths = list(ctx.walk(m))
    if util.safehasattr(repo, "prefetchfastannotate"):
        # client
        if opts.get("REV"):
            raise error.Abort(_("--rev cannot be used for client"))
        repo.prefetchfastannotate(paths)
    else:
        # server, or full repo
        with progress.bar(ui, _("building"), total=len(paths)) as prog:
            for i, path in enumerate(paths):
                prog.value = i
                with facontext.annotatecontext(repo, path) as actx:
                    try:
                        if actx.isuptodate(rev):
                            continue
                        actx.annotate(rev, rev)
                    except (faerror.CannotReuseError, faerror.CorruptedFileError):
                        # the cache is broken (could happen with renaming so the
                        # file history gets invalidated). rebuild and try again.
                        ui.debug("fastannotate: %s: rebuilding broken cache\n" % path)
                        actx.rebuild()
                        try:
                            actx.annotate(rev, rev)
                        except Exception as ex:
                            # possibly a bug, but should not stop us from
                            # building cache for other files.
                            ui.warn(
                                _("fastannotate: %s: failed to " "build cache: %r\n")
                                % (path, ex)
                            )
コード例 #11
0
ファイル: tweakdefaults.py プロジェクト: xmonader/eden
def rebaseorfastforward(orig, ui, repo, dest, **args):
    """Wrapper for rebasemodule.rebase that fast-forwards the working directory
    and any active bookmark to the rebase destination if there is actually
    nothing to rebase.
    """
    prev = repo["."]
    destrev = scmutil.revsingle(repo, dest)
    common = destrev.ancestor(prev)
    if prev == common and destrev != prev:
        result = hg.update(repo, destrev.node())
        if repo._activebookmark:
            with repo.wlock():
                bookmarks.update(repo, [prev.node()], destrev.node())
        ui.status(_("nothing to rebase - fast-forwarded to %s\n") % dest)
        return result
    return orig(ui, repo, dest=dest, **args)
コード例 #12
0
ファイル: catnotate.py プロジェクト: simpkins/eden
def catnotate(ui, repo, file1, *args, **opts):
    """output the current or given revision of files annotated with filename
    and line number.

    Print the specified files as they were at the given revision. If
    no revision is given, the parent of the working directory is used.

    Binary files are skipped unless -a/--text option is provided.
    """
    ctx = scmutil.revsingle(repo, opts.get("rev"))
    matcher = scmutil.match(ctx, (file1, ) + args, opts)
    prefix = ""

    err = 1

    # modified and stripped mercurial.cmdutil.cat follows
    def write(path):
        fp = cmdutil.makefileobj(repo,
                                 opts.get("output"),
                                 ctx.node(),
                                 pathname=os.path.join(prefix, path))
        data = ctx[path].data()
        if not opts.get("text") and util.binary(data):
            fp.write(b"%s: binary file\n" % path.encode("utf8"))
            return

        for (num, line) in enumerate(data.split(b"\n"), start=1):
            line = line + b"\n"
            fp.write(b"%s:%s: %s" %
                     (path.encode("utf8"), str(num).encode("utf8"), line))
        fp.close()

    # Automation often uses hg cat on single files, so special case it
    # for performance to avoid the cost of parsing the manifest.
    if len(matcher.files()) == 1 and not matcher.anypats():
        file = matcher.files()[0]
        mfl = repo.manifestlog
        mfnode = ctx.manifestnode()
        if mfnode and mfl[mfnode].find(file)[0]:
            write(file)
            return 0

    for abs in ctx.walk(matcher):
        write(abs)
        err = 0

    return err
コード例 #13
0
def _matchpaths(repo, rev, pats, opts, aopts=facontext.defaultopts):
    """generate paths matching given patterns"""
    perfhack = repo.ui.configbool("fastannotate", "perfhack")

    # disable perfhack if:
    # a) any walkopt is used
    # b) if we treat pats as plain file names, some of them do not have
    #    corresponding linelog files
    if perfhack:
        # cwd related to reporoot
        reporoot = os.path.dirname(repo.path)
        reldir = os.path.relpath(pycompat.getcwd(), reporoot)
        if reldir == ".":
            reldir = ""
        if any(opts.get(o[1]) for o in commands.walkopts):  # a)
            perfhack = False
        else:  # b)
            relpats = [
                os.path.relpath(p, reporoot) if os.path.isabs(p) else p for p in pats
            ]
            # disable perfhack on '..' since it allows escaping from the repo
            if any(
                (
                    ".." in f
                    or not os.path.isfile(
                        facontext.pathhelper(repo, f, aopts).linelogpath
                    )
                )
                for f in relpats
            ):
                perfhack = False

    # perfhack: emit paths directory without checking with manifest
    # this can be incorrect if the rev dos not have file.
    if perfhack:
        for p in relpats:
            yield os.path.join(reldir, p)
    else:

        def bad(x, y):
            raise error.Abort("%s: %s" % (x, y))

        ctx = scmutil.revsingle(repo, rev)
        m = scmutil.match(ctx, pats, opts, badfn=bad)
        for p in ctx.walk(m):
            yield p
コード例 #14
0
def debugserializecommit(ui, repo, *args, **opts):
    """serialize commit in format consumable by 'memcommit' command

    If no revision for serialization is specified, the current commit is
    serialized.

    This command is mainly intended for the testing the 'memcommit' command.
    """
    ctx = scmutil.revsingle(repo, opts.get("rev"))
    changelistbuilder = commitdata.changelistbuilder(ctx.p1().hex())

    for path in ctx.files():
        if path in ctx:
            fctx = ctx[path]
            renamed = fctx.renamed()
            copysource = renamed[0] if renamed else None

            info = commitdata.fileinfo(flags=fctx.flags(),
                                       content=fctx.data(),
                                       copysource=copysource)
        else:
            info = commitdata.fileinfo(deleted=True)

        changelistbuilder.addfile(path, info)

    changelist = changelistbuilder.build()

    bookmark = opts.get("dest")
    to = opts.get("to")
    pushrebase = opts.get("pushrebase")

    destination = commitdata.destination(bookmark=bookmark,
                                         pushrebase=pushrebase)

    parents = ([hex(p) for p in repo.nodes(to)]
               if to else [p.hex() for p in ctx.parents()])

    metadata = commitdata.metadata(
        author=ctx.user(),
        description=ctx.description(),
        parents=parents,
        extra=ctx.extra(),
    )

    params = commitdata.params(changelist, metadata, destination)
    ui.writebytes(serialization.serialize(params.todict()))
コード例 #15
0
ファイル: __init__.py プロジェクト: zerkella/eden
def _mainloop(repo, ui, tr, count, batchsize):
    i = 0
    batchcount = 0
    start = time.time()
    base = scmutil.revsingle(repo, ui.config("repogenerator", "startcommit", "tip"))
    goalrev = ui.configint("repogenerator", "numcommits", 10000) - 1
    ui.write(_("starting commit is: %d (goal is %d)\n") % (base.rev(), goalrev))
    generator = editsgenerator.randomeditsgenerator(base)

    while base.rev() < goalrev:
        # Make a commit:
        wctx = context.overlayworkingctx(repo)
        wctx.setbase(base)
        generator.makerandomedits(wctx)
        memctx = wctx.tomemctx("memory commit", parents=(base.rev(), None))
        newctx = repo[repo.commitctx(memctx)]

        # Log production rate:
        elapsed = time.time() - start
        if i % 5 == 0:
            ui.write(
                _(
                    "created %s, %0.2f sec elapsed "
                    "(%0.2f commits/sec, %s per hour, %s per day)\n"
                )
                % (
                    i,
                    elapsed,
                    i / elapsed,
                    "{:,}".format(int(i / elapsed * 3600)),
                    "{:,}".format(int(i / elapsed * 86400)),
                )
            )
        base = newctx
        i += 1
        batchcount += 1
        if batchcount > batchsize:
            ui.status(_("committing txn...\n"))
            tr.close()
            tr = repo.transaction("newtxn_")
            batchcount = 0
        if i >= count:
            ui.status(_("generated %d commits; quitting\n") % count)
            return
コード例 #16
0
def _fullclean(ui, repo):
    ui.status(_("cleaning up uncommitted code\n"), component="snapshot")
    # Remove "tracked changes"
    cmdutil.revert(
        ui,
        repo,
        scmutil.revsingle(repo, None),
        repo.dirstate.parents(),
        all=True,
        no_backup=True,
    )
    # Remove "untracked changes" (e.g. untracked files)
    repo.dirstate._fs.purge(
        scmutil.match(repo[None]),
        removefiles=True,
        removedirs=True,
        removeignored=False,
        dryrun=False,
    )
コード例 #17
0
ファイル: fbhistedit.py プロジェクト: zerkella/eden
def _rebase(orig, ui, repo, *pats, **opts):
    histedit = extensions.find("histedit")

    contf = opts.get("continue")
    abortf = opts.get("abort")

    if ((contf or abortf) and not repo.localvfs.exists("rebasestate")
            and repo.localvfs.exists("histedit.state")):
        msg = _("no rebase in progress")
        hint = _(
            "If you want to continue or abort an interactive rebase please"
            ' use "histedit --continue/--abort" instead.')
        raise error.Abort(msg, hint=hint)

    if not opts.get("interactive"):
        return orig(ui, repo, *pats, **opts)

    # the argument parsing has as lot of copy-paste from rebase.py
    # Validate input and define rebasing points
    destf = opts.get("dest", None)
    srcf = opts.get("source", None)
    basef = opts.get("base", None)
    revf = opts.get("rev", [])
    keepf = opts.get("keep", False)

    src = None

    if contf or abortf:
        raise error.Abort("no interactive rebase in progress")
    if destf:
        dest = scmutil.revsingle(repo, destf)
    else:
        raise error.Abort("you must specify a destination (-d) for the rebase")

    if srcf and basef:
        raise error.Abort(_("cannot specify both a source and a base"))
    if revf:
        raise error.Abort("--rev not supported with interactive rebase")
    elif srcf:
        src = scmutil.revsingle(repo, srcf)
    else:
        base = scmutil.revrange(repo, [basef or "."])
        if not base:
            ui.status(
                _('empty "base" revision set - '
                  "can't compute rebase set\n"))
            return 1
        commonanc = repo.revs("ancestor(%ld, %d)", base, dest).first()
        if commonanc is not None:
            src = repo.revs("min((%d::(%ld) - %d)::)", commonanc, base,
                            commonanc).first()
        else:
            src = None

    if src is None:
        raise error.Abort("no revisions to rebase")
    src = repo[src].node()

    topmost, empty = repo.dirstate.parents()
    revs = histedit.between(repo, src, topmost, keepf)

    if srcf and not revs:
        raise error.Abort(
            _("source revision (-s) must be an ancestor of the "
              "working directory for interactive rebase"))

    ctxs = [repo[r] for r in revs]
    state = histedit.histeditstate(repo)
    rules = [histedit.base(state, repo[dest])
             ] + [histedit.pick(state, ctx) for ctx in ctxs]
    editcomment = """#
# Interactive rebase is just a wrapper over histedit (adding the 'base' line as
# the first rule). To continue or abort it you should use:
# "hg histedit --continue" and "--abort"
#
"""
    editcomment += histedit.geteditcomment(ui, node.short(src),
                                           node.short(topmost))
    histedit.ruleeditor(repo, ui, rules, editcomment=editcomment)

    return histedit.histedit(
        ui,
        repo,
        node.hex(src),
        keep=keepf,
        commands=repo.localvfs.join("histedit-last-edit.txt"),
    )
コード例 #18
0
def dodiff(ui, repo, cmdline, pats, opts):
    """Do the actual diff:

    - copy to a temp structure if diffing 2 internal revisions
    - copy to a temp structure if diffing working revision with
      another one and more than 1 file is changed
    - just invoke the diff for a single file in the working dir
    """

    revs = opts.get("rev")
    change = opts.get("change")
    do3way = "$parent2" in cmdline

    if revs and change:
        msg = _("cannot specify --rev and --change at the same time")
        raise error.Abort(msg)
    elif change:
        node2 = scmutil.revsingle(repo, change, None).node()
        node1a, node1b = repo.changelog.parents(node2)
    else:
        node1a, node2 = scmutil.revpair(repo, revs)
        if not revs:
            node1b = repo.dirstate.p2()
        else:
            node1b = nullid

    # Disable 3-way merge if there is only one parent
    if do3way:
        if node1b == nullid:
            do3way = False

    matcher = scmutil.match(repo[node2], pats, opts)

    if opts.get("patch"):
        if node2 is None:
            raise error.Abort(_("--patch requires two revisions"))
    else:
        mod_a, add_a, rem_a = list(map(set, repo.status(node1a, node2, matcher)[:3]))
        if do3way:
            mod_b, add_b, rem_b = list(
                map(set, repo.status(node1b, node2, matcher)[:3])
            )
        else:
            mod_b, add_b, rem_b = set(), set(), set()
        modadd = mod_a | add_a | mod_b | add_b
        common = modadd | rem_a | rem_b
        if not common:
            return 0

    tmproot = tempfile.mkdtemp(prefix="extdiff.")
    try:
        if not opts.get("patch"):
            # Always make a copy of node1a (and node1b, if applicable)
            dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
            dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0]
            rev1a = "@%d" % repo[node1a].rev()
            if do3way:
                dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
                dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0]
                rev1b = "@%d" % repo[node1b].rev()
            else:
                dir1b = None
                rev1b = ""

            fnsandstat = []

            # If node2 in not the wc or there is >1 change, copy it
            dir2root = ""
            rev2 = ""
            if node2:
                dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0]
                rev2 = "@%d" % repo[node2].rev()
            elif len(common) > 1:
                # we only actually need to get the files to copy back to
                # the working dir in this case (because the other cases
                # are: diffing 2 revisions or single file -- in which case
                # the file is already directly passed to the diff tool).
                dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot)
            else:
                # This lets the diff tool open the changed file directly
                dir2 = ""
                dir2root = repo.root

            label1a = rev1a
            label1b = rev1b
            label2 = rev2

            # If only one change, diff the files instead of the directories
            # Handle bogus modifies correctly by checking if the files exist
            if len(common) == 1:
                common_file = util.localpath(common.pop())
                dir1a = os.path.join(tmproot, dir1a, common_file)
                label1a = common_file + rev1a
                if not os.path.isfile(dir1a):
                    dir1a = os.devnull
                if do3way:
                    dir1b = os.path.join(tmproot, dir1b, common_file)
                    label1b = common_file + rev1b
                    if not os.path.isfile(dir1b):
                        dir1b = os.devnull
                dir2 = os.path.join(dir2root, dir2, common_file)
                label2 = common_file + rev2
        else:
            template = "hg-%h.patch"
            cmdutil.export(
                repo,
                [repo[node1a].rev(), repo[node2].rev()],
                fntemplate=repo.localvfs.reljoin(tmproot, template),
                match=matcher,
            )
            label1a = cmdutil.makefilename(repo, template, node1a)
            label2 = cmdutil.makefilename(repo, template, node2)
            dir1a = repo.localvfs.reljoin(tmproot, label1a)
            dir2 = repo.localvfs.reljoin(tmproot, label2)
            dir1b = None
            label1b = None
            fnsandstat = []

        # Function to quote file/dir names in the argument string.
        # When not operating in 3-way mode, an empty string is
        # returned for parent2
        replace = {
            "parent": dir1a,
            "parent1": dir1a,
            "parent2": dir1b,
            "plabel1": label1a,
            "plabel2": label1b,
            "clabel": label2,
            "child": dir2,
            "root": repo.root,
        }

        def quote(match):
            pre = match.group(2)
            key = match.group(3)
            if not do3way and key == "parent2":
                return pre
            return pre + util.shellquote(replace[key])

        # Match parent2 first, so 'parent1?' will match both parent1 and parent
        regex = (
            r"""(['"]?)([^\s'"$]*)"""
            r"\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1"
        )
        if not do3way and not re.search(regex, cmdline):
            cmdline += " $parent1 $child"
        cmdline = re.sub(regex, quote, cmdline)

        ui.debug("running %r in %s\n" % (cmdline, tmproot))
        ui.system(cmdline, cwd=tmproot, blockedtag="extdiff")

        for copy_fn, working_fn, st in fnsandstat:
            cpstat = util.lstat(copy_fn)
            # Some tools copy the file and attributes, so mtime may not detect
            # all changes.  A size check will detect more cases, but not all.
            # The only certain way to detect every case is to diff all files,
            # which could be expensive.
            # copyfile() carries over the permission, so the mode check could
            # be in an 'elif' branch, but for the case where the file has
            # changed without affecting mtime or size.
            if (
                cpstat.st_mtime != st.st_mtime
                or cpstat.st_size != st.st_size
                or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100)
            ):
                ui.debug(
                    "file changed while diffing. "
                    "Overwriting: %s (src: %s)\n" % (working_fn, copy_fn)
                )
                util.copyfile(copy_fn, working_fn)

        return 1
    finally:
        ui.note(_("cleaning up temp directory\n"))
        shutil.rmtree(tmproot)
コード例 #19
0
ファイル: split.py プロジェクト: zerkella/eden
def split(ui, repo, *revs, **opts):
    """split a changeset into smaller changesets

    Prompt for hunks to be selected until exhausted. Each selection of hunks
    will form a separate changeset, in order from parent to child: the first
    selection will form the first changeset, the second selection will form
    the second changeset, and so on.

    Operates on the current revision by default. Use --rev to split a given
    changeset instead.
    """
    newcommits = []

    revarg = (list(revs) + opts.get("rev")) or ["."]
    if len(revarg) != 1:
        msg = _("more than one revset is given")
        hnt = _(
            "use either `hg split <rs>` or `hg split --rev <rs>`, not both")
        raise error.Abort(msg, hint=hnt)

    rev = scmutil.revsingle(repo, revarg[0])
    if opts.get("no_rebase"):
        torebase = ()
    else:
        torebase = list(
            map(hex, repo.nodes("descendants(%d) - (%d)", rev, rev)))

    with repo.wlock(), repo.lock():
        cmdutil.bailifchanged(repo)
        if torebase:
            cmdutil.checkunfinished(repo)
        ctx = repo[rev]
        r = ctx.hex()
        allowunstable = visibility.tracking(repo) or obsolete.isenabled(
            repo, obsolete.allowunstableopt)
        if not allowunstable:
            # XXX We should check head revs
            if repo.revs("(%d::) - %d", rev, rev):
                raise error.Abort(
                    _("cannot split commit: %s not a head") % ctx)

        if len(ctx.parents()) > 1:
            raise error.Abort(_("cannot split merge commits"))
        prev = ctx.p1()
        bmupdate = common.bookmarksupdater(repo, ctx.node())
        bookactive = repo._activebookmark
        if bookactive is not None:
            repo.ui.status(_("(leaving bookmark %s)\n") % repo._activebookmark)
        bookmarks.deactivate(repo)
        hg.update(repo, prev)

        commands.revert(ui, repo, rev=r, all=True)

        def haschanges():
            modified, added, removed, deleted = repo.status()[:4]
            return modified or added or removed or deleted

        # We need to detect the case where the user selects all remaining
        # changes, as that will end the split.  That's the commit we want to
        # mark as the result of the split.  To do this, wrap the recordfilter
        # function and compare the output to see if it contains all the
        # originalchunks.
        shouldrecordmutation = [False]

        def mutinfo(extra):
            if shouldrecordmutation[0]:
                return mutation.record(
                    repo,
                    extra,
                    [ctx.node()],
                    "split",
                    splitting=[c.node() for c in newcommits],
                )

        def recordfilter(ui, originalchunks, operation=None):
            chunks, newopts = cmdutil.recordfilter(ui, originalchunks,
                                                   operation)
            if cmdutil.comparechunks(chunks, originalchunks):
                shouldrecordmutation[0] = True
            return chunks, newopts

        msg = ("HG: This is the original pre-split commit message. "
               "Edit it as appropriate.\n\n")
        msg += ctx.description()
        opts["message"] = msg
        opts["edit"] = True
        opts["_commitmutinfofunc"] = mutinfo
        try:
            while haschanges():
                pats = ()
                with repo.transaction("split"):
                    cmdutil.dorecord(ui, repo, commands.commit, "commit",
                                     False, recordfilter, *pats, **opts)
                # TODO: Does no seem like the best way to do this
                # We should make dorecord return the newly created commit
                newcommits.append(repo["."])
                if haschanges():
                    if ui.prompt("Done splitting? [yN]", default="n") == "y":
                        shouldrecordmutation[0] = True
                        with repo.transaction("split"):
                            commands.commit(ui, repo, **opts)
                        newcommits.append(repo["."])
                        break
                else:
                    ui.status(_("no more change to split\n"))
        except Exception:
            # Rollback everything
            hg.updaterepo(repo, r, True)  # overwrite=True
            if newcommits:
                visibility.remove(repo, [c.node() for c in newcommits])

            if bookactive is not None:
                bookmarks.activate(repo, bookactive)
            raise

        if newcommits:
            phabdiffs = {}
            for c in newcommits:
                phabdiff = diffprops.parserevfromcommitmsg(
                    repo[c].description())
                if phabdiff:
                    phabdiffs.setdefault(phabdiff, []).append(c)
            if any(len(commits) > 1 for commits in phabdiffs.values()):
                hintutil.trigger("split-phabricator",
                                 ui.config("split", "phabricatoradvice"))

            tip = repo[newcommits[-1]]
            with repo.transaction("post-split"):
                bmupdate(tip.node())
                if bookactive is not None:
                    bookmarks.activate(repo, bookactive)
                if obsolete.isenabled(repo, obsolete.createmarkersopt):
                    obsolete.createmarkers(repo, [(repo[r], newcommits)],
                                           operation="split")
            if torebase:
                rebaseopts = {"dest": "_destrestack(SRC)", "rev": torebase}
                rebase.rebase(ui, repo, **rebaseopts)
            unfi = repo
            with repo.transaction("post-split-hide"):
                visibility.remove(repo, [unfi[r].node()])
コード例 #20
0
ファイル: remotenames.py プロジェクト: mitrandir77/eden
def debugremotebookmark(ui, repo, name, rev):
    """Change a remote bookmark under the 'debugremote' namespace."""
    node = scmutil.revsingle(repo, rev).node()
    setremotebookmark(repo, "debugremote/%s" % name, node)
コード例 #21
0
def _memcommit(repo, params):
    """create a new commit in the repo based on the params

    Isolating this method allows easy wrapping by other extensions like hgsql.
    """

    with repo.wlock(), repo.lock(), repo.transaction("memcommit"):

        def resolvetargetctx(repo, originalparentnode, targetparents):
            numparents = len(targetparents)

            if numparents > 1:
                raise error.Abort(_("merge commits are not supported"))

            if numparents == 0:
                raise error.Abort(_("parent commit must be specified"))

            targetctx = repo[targetparents[0]]
            targetnode = targetctx.node()

            if originalparentnode != targetnode:
                raise error.Abort(_("commit with new parents not supported"))

            if (not repo.ui.configbool("memcommit", "allowunrelatedroots")
                    and targetnode == nullid):
                raise error.Abort(_("commit without parents are not allowed"))

            return targetctx

        request = pushrequest.frommemcommit(repo, params)
        originalparentnode = request.stackparentnode
        targetctx = resolvetargetctx(repo, originalparentnode,
                                     params.metadata.parents)
        targetnode = targetctx.node()

        destination = params.destination
        pushrebase = destination.pushrebase
        ontobookmark = destination.bookmark

        if ontobookmark:
            bookmarkctx = scmutil.revsingle(repo, ontobookmark)
            if not pushrebase and bookmarkctx.node() != targetnode:
                raise error.Abort(
                    _("destination parent does not match destination bookmark")
                )
        elif pushrebase:
            raise error.Abort(
                _("must specify destination bookmark for pushrebase"))

        if pushrebase:
            ontonode = bookmarkctx.node()
            cl = repo.changelog
            if cl.isancestor(originalparentnode, ontonode):
                targetctx = bookmarkctx
            elif cl.isancestor(ontonode, originalparentnode):
                targetctx = repo[originalparentnode]
            else:
                raise error.Abort(
                    _("destination bookmark is not ancestor or descendant of commit parent"
                      ))

        added, replacements = request.pushonto(targetctx)

        if len(added) > 1:
            # We always create a single commit.
            error.Abort(_("more than one commit was created"))

        if replacements:
            # We always create a new commit and therefore, cannot have any
            # replacements.
            error.Abort(_("new commit cannot replace any commit"))

        node = added[0]

        if ontobookmark:
            bookmarks.pushbookmark(repo, ontobookmark, bookmarkctx.hex(), node)

        return node