Example #1
0
File: rage.py Project: jsoref/eden
def rage(ui, repo, *pats, **opts):
    """collect troubleshooting diagnostics

    The rage command collects useful diagnostic information.

    By default, the information will be uploaded to Phabricator and
    instructions about how to ask for help will be printed.

    After submitting to Phabricator, it prints configerable advice::

        [rage]
        advice = Please see our FAQ guide: https://...

    """
    with progress.spinner(ui, "collecting information"):
        msg = _makerage(ui, repo, **opts)

    if opts.get("preview"):
        ui.pager("rage")
        ui.write("%s\n" % msg)
        return

    with progress.spinner(ui, "saving paste"):
        try:
            p = subprocess.Popen(
                ["pastry", "--lang", "hgrage", "--title", "hgrage"],
                stdout=subprocess.PIPE,
                stdin=subprocess.PIPE,
                stderr=subprocess.PIPE,
                shell=pycompat.iswindows,
            )
            out, err = p.communicate(input=msg + "\n")
            ret = p.returncode
        except OSError:
            ui.write(_("Failed calling pastry. (is it in your PATH?)\n"))
            ret = 1

    if ret:
        fd, tmpname = tempfile.mkstemp(prefix="hg-rage-")
        with util.fdopen(fd, r"w") as tmpfp:
            tmpfp.write(msg)
            ui.write(
                _(
                    "Failed to post the diagnostic paste to Phabricator, "
                    "but its contents have been written to:\n\n"
                )
            )
            ui.write(_("  %s\n") % tmpname, label="rage.link")
            ui.write(
                _("\nPlease include this file in the %s.\n")
                % ui.config("ui", "supportcontact")
            )
    else:
        ui.write(
            _("Please post in %s with the following link:\n\n")
            % (ui.config("ui", "supportcontact"))
        )
        ui.write("  " + out + "\n", label="rage.link")
    ui.write(ui.config("rage", "advice", "") + "\n")
Example #2
0
        def render(self):
            ui = self.ui
            ui.pushbuffer()
            ui.status(_("Interactive Smartlog History\n\n"))
            if opts.get("all"):
                limit = 0
            else:
                limit = 2 * 604800  # two weeks
            if self.index == len(self.versions):
                self.index = -1
            if self.index == -2:
                self.index = len(self.versions) - 1
            if self.index == -1:
                with progress.spinner(ui, _("fetching")):
                    firstpublic, revdag = serv.getsmartlog(
                        reponame, workspacename, repo, limit)
                ui.status(_("Current Smartlog:\n\n"))
            else:
                with progress.spinner(ui, _("fetching")):
                    firstpublic, revdag, slversion, sltimestamp = serv.getsmartlogbyversion(
                        reponame,
                        workspacename,
                        repo,
                        None,
                        self.versions[self.index]["version_number"],
                        limit,
                    )
                formatteddate = time.strftime("%Y-%m-%d %H:%M:%S",
                                              time.localtime(sltimestamp))
                ui.status(
                    _("Smartlog version %d \nsynced at %s\n\n") %
                    (slversion, formatteddate))
            template = "sl_cloud"
            smartlogstyle = ui.config("templatealias", template)
            if smartlogstyle:
                opts["template"] = "{%s}" % smartlogstyle
            else:
                ui.debug(
                    _("style %s is not defined, skipping") % smartlogstyle,
                    component="commitcloud",
                )

            displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
            if ui.config("experimental", "graph.renderer") == "legacy":
                cmdutil.displaygraph(ui, repo, revdag, displayer,
                                     graphmod.asciiedges)
            else:
                cmdutil.rustdisplaygraph(ui,
                                         repo,
                                         revdag,
                                         displayer,
                                         reserved=firstpublic)
            repo.ui.status(
                _("<-: newer  "
                  "->: older  "
                  "q: abort  \n"
                  "a: 1 day forward  d: 1 day back \n"))
            return ui.popbuffer()
Example #3
0
    def pendingchanges(self, match=None, listignored=False):
        # type: (Optional[Callable[[str], bool]], bool) -> Iterable[Tuple[str, bool]]
        def bail(reason):
            self._ui.debug("fsmonitor: fallback to core status, %s\n" % reason)
            return super(fsmonitorfilesystem, self).pendingchanges(
                match, listignored=listignored
            )

        if self._fsmonitordisable:
            return bail("fsmonitor disabled")
        if listignored:
            return bail("listing ignored files")
        if not self._watchmanclient.available():
            return bail("client unavailable")

        with progress.spinner(self._ui, "scanning working copy"), self._detectrace(
            match
        ):
            try:
                # Ideally we'd return the result incrementally, but we need to
                # be able to fall back if watchman fails. So let's consume the
                # whole pendingchanges list upfront.
                return list(self._fspendingchanges(match))
            except fsmonitorfallback as ex:
                return bail(str(ex))
Example #4
0
 def command(self, *args, **kwargs):
     ignoreerrors = kwargs.get("ignoreerrors", False)
     with progress.spinner(self._ui, "querying watchman"):
         try:
             try:
                 return self._command(*args)
             except pywatchman.UseAfterFork:
                 # Ideally we wouldn't let this happen, but if it does happen,
                 # record it in the log and retry the command.
                 blackbox.log(
                     {
                         "debug": {
                             "value": "fork detected. re-connect to watchman socket"
                         }
                     }
                 )
                 self._watchmanclient = None
                 return self._command(*args)
             except WatchmanNoRoot:
                 # this 'watch' command can also raise a WatchmanNoRoot if
                 # watchman refuses to accept this root
                 self._command("watch")
                 return self._command(*args)
         except Unavailable:
             # this is in an outer scope to catch Unavailable form any of the
             # above _command calls
             if not ignoreerrors:
                 self._watchmanclient = None
             raise
Example #5
0
 def loadoldversion(self, versionindex):
     versionnumber = self.versions[versionindex]["version_number"]
     with self.servlock, progress.spinner(
             self.ui,
             _("fetching version %s") % versionnumber):
         limit = self.limit
         if limit > 0:
             # Increase the limit by how long ago the smartlog was
             # backed-up.  This gives a rolling window, so viewing
             # versions more than the limit in age will still show
             # commits.
             timestamp = self.versions[versionindex]["timestamp"]
             limit += max(0, int(time.time() - timestamp))
         slinfo = self.serv.getsmartlogbyversion(
             self.reponame,
             self.workspacename,
             self.repo,
             None,
             versionnumber,
             limit,
             self.flags,
         )
         formatteddate = time.strftime("%Y-%m-%d %H:%M:%S",
                                       time.localtime(slinfo.timestamp))
         title = "Smartlog version %d synced at %s:" % (
             slinfo.version,
             formatteddate,
         )
         return (title, slinfo)
Example #6
0
        def __init__(self, ui, repo, reponame, workspacename, **opts):
            self.ui = ui
            self.repo = repo
            self.reponame = reponame
            self.workspacename = workspacename
            self.opts = opts
            self.serv = service.get(ui, tokenmod.TokenLocator(ui).token)
            self.servlock = threading.Lock()
            self.renderevent = threading.Event()
            self.running = True
            self.cache = {}
            with progress.spinner(ui, _("fetching cloud smartlog history")):
                self.versions = sorted(
                    self.serv.gethistoricalversions(reponame, workspacename),
                    key=lambda version: version["version_number"],
                )

            smartlogstyle = ui.config("templatealias", template)
            if smartlogstyle:
                self.opts["template"] = "{%s}" % smartlogstyle

            self.cur_index = len(self.versions)
            if opts.get("all"):
                self.limit = 0
            else:
                self.limit = 2 * 7 * 24 * 60 * 60  # two weeks
            self.flags = []
Example #7
0
def _cleanupoldpacks(ui, packpath, limit):
    """Enforce a size limit on the cache. Packfiles will be removed oldest
    first, with the asumption that old packfiles contains less useful data than new ones.
    """
    with progress.spinner(ui, _("cleaning old packs")):

        def _mtime(f):
            stat = util.lstat(f)
            return stat.st_mtime

        def _listpackfiles(path):
            packs = []
            try:
                for f in os.listdir(path):
                    _, ext = os.path.splitext(f)
                    if ext.endswith("pack"):
                        packs.append(os.path.join(packpath, f))
            except OSError as ex:
                if ex.errno != errno.ENOENT:
                    raise

            return packs

        files = sorted(_listpackfiles(packpath), key=_mtime, reverse=True)

        cachesize = 0
        for f in files:
            stat = os.lstat(f)
            cachesize += stat.st_size

        while cachesize > limit:
            f = files.pop()
            stat = util.lstat(f)

            # Dont't remove files that are newer than 10 minutes. This will
            # avoid a race condition where mercurial downloads files from the
            # network and expect these to be present on disk. If the 'limit' is
            # properly set, we should have removed enough files that this
            # condition won't matter.
            if time.gmtime(stat.st_mtime + 10 * 60) > time.gmtime():
                return

            root, ext = os.path.splitext(f)
            try:
                if ext == datapack.PACKSUFFIX:
                    util.unlink(root + datapack.INDEXSUFFIX)
                else:
                    util.unlink(root + historypack.INDEXSUFFIX)
            except OSError as ex:
                if ex.errno != errno.ENOENT:
                    raise

            try:
                util.unlink(f)
            except OSError as ex:
                if ex.errno != errno.ENOENT:
                    raise

            cachesize -= stat.st_size
Example #8
0
 def loadcurrentversion(self):
     with self.servlock:
         with progress.spinner(self.ui, _("fetching latest version")):
             slinfo = self.serv.getsmartlog(
                 self.reponame,
                 self.workspacename,
                 self.repo,
                 self.limit,
                 self.flags,
             )
             title = "Current cloud smartlog"
             return (title, slinfo)
Example #9
0
def _cleanuptemppacks(ui, packpath):
    """In some situations, temporary pack files are left around unecessarily
    using disk space. We've even seen cases where some users had 170GB+ worth
    of these. Let's remove these.
    """
    extensions = [
        datapack.PACKSUFFIX,
        datapack.INDEXSUFFIX,
        historypack.PACKSUFFIX,
        historypack.INDEXSUFFIX,
    ]

    def _shouldhold(f):
        """Newish files shouldn't be removed as they could be used by another
        running command.
        """
        if os.path.isdir(f) or os.path.basename(f) == "repacklock":
            return True

        try:
            stat = os.lstat(f)
        except OSError:
            # If we can't access the file, it's either being removed, or we
            # don't have access to it, either way there is nothing we can do
            # about it, ignore them.
            return True
        return time.gmtime(stat.st_atime + 24 * 3600) > time.gmtime()

    with progress.spinner(ui, _("cleaning old temporary files")):
        try:
            for f in os.listdir(packpath):
                f = os.path.join(packpath, f)
                if _shouldhold(f):
                    continue

                __, ext = os.path.splitext(f)

                if ext not in extensions:
                    try:
                        util.unlink(f)
                    except Exception:
                        pass

        except OSError as ex:
            if ex.errno != errno.ENOENT:
                raise
Example #10
0
        def rendercontents(self, versionindex):
            if versionindex in self.cache:
                contents = self.cache[versionindex]
            else:
                if versionindex == len(self.versions):
                    (title, slinfo) = self.loadcurrentversion()
                else:
                    (title, slinfo) = self.loadoldversion(versionindex)

                contents = [
                    ui.label("Commit Cloud Smartlog History",
                             "bold cyan underline").encode(),
                    ui.label(
                        "Use [ and ] to navigate to earlier or later versions",
                        "cyan").encode(),
                    ui.label(
                        "Note: version dates may be off by one due to a server bug",
                        "cyan",
                    ).encode(),
                    b"",
                    title.encode(),
                    b"",
                ]
                firstpublic, revdag = self.serv.makedagwalker(
                    slinfo, self.repo)
                displayer = cmdutil.show_changeset(self.ui,
                                                   self.repo,
                                                   self.opts,
                                                   buffered=True)

                def out(row):
                    contents.extend(row.rstrip().encode().split(b"\n"))

                with progress.spinner(ui, _("loading commit information")):
                    cmdutil.displaygraph(
                        self.ui,
                        self.repo,
                        revdag,
                        displayer,
                        reserved=firstpublic,
                        out=out,
                    )
                self.cache[versionindex] = contents

            return contents
Example #11
0
 def latencytest(count):
     # Use the upload endpoint for the latency test.  We will time how long it
     # takes for the server to return the "upload complete" response for a
     # single byte upload.
     latencies = []
     with progress.spinner(ui, "testing connection latency"):
         for i in range(count):
             pipeo.write(b"upload 1\n")
             pipeo.flush()
             l = pipei.readline()
             if l != b"upload bytes 1\n":
                 raise error.Abort("invalid response from server: %r" % l)
             starttime = util.timer()
             pipeo.write(b"\n")
             pipeo.flush()
             l = pipei.readline()
             endtime = util.timer()
             if l != b"upload complete\n":
                 raise error.Abort("invalid response from server: %r" % l)
             latencies.append(endtime - starttime)
     return latencies
Example #12
0
def cloudhide(ui, repo, *revs, **opts):
    """remove commits or bookmarks from the cloud workspace"""
    reponame = ccutil.getreponame(repo)
    workspacename = workspace.parseworkspace(ui, opts)
    if workspacename is None:
        workspacename = workspace.currentworkspace(repo)
    if workspacename is None:
        workspacename = workspace.defaultworkspace(ui)

    with progress.spinner(ui, _("fetching commit cloud workspace")):
        serv = service.get(ui, tokenmod.TokenLocator(ui).token)
        slinfo = serv.getsmartlog(reponame, workspacename, repo, 0)
        firstpublic, revdag = serv.makedagwalker(slinfo, repo)
        cloudrefs = serv.getreferences(reponame, workspacename, 0)

    nodeinfos = slinfo.nodeinfos
    dag = slinfo.dag
    drafts = set(slinfo.draft)

    removenodes = set()

    for rev in list(revs) + opts.get("rev", []):
        rev = pycompat.encodeutf8(rev)
        if rev in drafts:
            removenodes.add(rev)
        else:
            candidate = None
            for draft in drafts:
                if draft.startswith(rev):
                    if candidate is None:
                        candidate = draft
                    else:
                        raise error.Abort(
                            _("ambiguous commit hash prefix: %s") % rev)
            if candidate is None:
                raise error.Abort(_("commit not in workspace: %s") % rev)
            removenodes.add(candidate)

    # Find the bookmarks we need to remove
    removebookmarks = set()
    for bookmark in opts.get("bookmark", []):
        kind, pattern, matcher = util.stringmatcher(bookmark)
        if kind == "literal":
            if pattern not in cloudrefs.bookmarks:
                raise error.Abort(_("bookmark not in workspace: %s") % pattern)
            removebookmarks.add(pattern)
        else:
            for bookmark in cloudrefs.bookmarks:
                if matcher(bookmark):
                    removebookmarks.add(bookmark)

    # Find the remote bookmarks we need to remove
    removeremotes = set()
    for remote in opts.get("remotebookmark", []):
        kind, pattern, matcher = util.stringmatcher(remote)
        if kind == "literal":
            if pattern not in cloudrefs.remotebookmarks:
                raise error.Abort(
                    _("remote bookmark not in workspace: %s") % pattern)
            removeremotes.add(remote)
        else:
            for remote in cloudrefs.remotebookmarks:
                if matcher(remote):
                    removeremotes.add(remote)

    # Find the heads and bookmarks we need to remove
    allremovenodes = dag.descendants(removenodes)
    removeheads = set(allremovenodes
                      & map(pycompat.encodeutf8, cloudrefs.heads))
    for node in allremovenodes:
        removebookmarks.update(nodeinfos[node].bookmarks)

    # Find the heads we need to remove because we are removing the last bookmark
    # to it.
    remainingheads = set(map(pycompat.encodeutf8,
                             cloudrefs.heads)) - removeheads
    for bookmark in removebookmarks:
        nodeutf8 = cloudrefs.bookmarks[bookmark]
        node = pycompat.encodeutf8(nodeutf8)
        info = nodeinfos.get(node)
        if node in remainingheads and info:
            if removebookmarks.issuperset(set(info.bookmarks)):
                remainingheads.discard(node)
                removeheads.add(node)

    # Find the heads we need to add to keep other commits visible
    addheads = (dag.parents(removenodes) - allremovenodes -
                dag.ancestors(remainingheads)) & drafts

    if removeheads:
        ui.status(_("removing heads:\n"))
        for head in sorted(removeheads):
            headutf8 = pycompat.decodeutf8(head)
            ui.status("    %s  %s\n" %
                      (headutf8[:12],
                       templatefilters.firstline(nodeinfos[head].message)))
    if addheads:
        ui.status(_("adding heads:\n"))
        for head in sorted(addheads):
            headutf8 = pycompat.decodeutf8(head)
            ui.status("    %s  %s\n" %
                      (headutf8[:12],
                       templatefilters.firstline(nodeinfos[head].message)))
    if removebookmarks:
        ui.status(_("removing bookmarks:\n"))
        for bookmark in sorted(removebookmarks):
            ui.status("    %s: %s\n" %
                      (bookmark, cloudrefs.bookmarks[bookmark][:12]))
    if removeremotes:
        ui.status(_("removing remote bookmarks:\n"))
        for remote in sorted(removeremotes):
            ui.status("    %s: %s\n" %
                      (remote, cloudrefs.remotebookmarks[remote][:12]))

    # Normalize back to strings. (The DAG wants bytes, the cloudrefs wants str)
    removeheads = list(map(pycompat.decodeutf8, removeheads))
    addheads = list(map(pycompat.decodeutf8, addheads))

    if removeheads or addheads or removebookmarks or removeremotes:
        if opts.get("dry_run"):
            ui.status(_("not updating cloud workspace: --dry-run specified\n"))
            return 0
        with progress.spinner(ui, _("updating commit cloud workspace")):
            serv.updatereferences(
                reponame,
                workspacename,
                cloudrefs.version,
                oldheads=list(removeheads),
                newheads=list(addheads),
                oldbookmarks=list(removebookmarks),
                oldremotebookmarks=list(removeremotes),
            )
    else:
        ui.status(_("nothing to change\n"))
Example #13
0
def showhistory(ui, repo, reponame, workspacename, **opts):
    """Shows an interactive view for historical versions of smartlogs"""
    serv = service.get(ui, tokenmod.TokenLocator(ui).token)
    with progress.spinner(ui, _("fetching")):
        versions = sorted(
            serv.gethistoricalversions(reponame, workspacename),
            key=lambda version: version["version_number"],
            reverse=True,
        )

    class smartlogview(interactiveui.viewframe):
        def __init__(self, ui, repo, versions):
            interactiveui.viewframe.__init__(self, ui, repo, -1)
            self.versions = versions
            self.flags = []
            if opts.get("force_original_backend"):
                self.flags.append("USE_ORIGINAL_BACKEND")

        def render(self):
            ui = self.ui
            ui.pushbuffer()
            ui.status(_("Interactive Smartlog History\n\n"))
            if opts.get("all"):
                limit = 0
            else:
                limit = 2 * 604800  # two weeks
            if self.index == len(self.versions):
                self.index = -1
            if self.index == -2:
                self.index = len(self.versions) - 1
            if self.index == -1:
                with progress.spinner(ui, _("fetching")):
                    slinfo = serv.getsmartlog(reponame, workspacename, repo,
                                              limit, self.flags)
                ui.status(_("Current Smartlog:\n\n"))
            else:
                with progress.spinner(ui, _("fetching")):
                    slinfo = serv.getsmartlogbyversion(
                        reponame,
                        workspacename,
                        repo,
                        None,
                        self.versions[self.index]["version_number"],
                        limit,
                        self.flags,
                    )
                formatteddate = time.strftime("%Y-%m-%d %H:%M:%S",
                                              time.localtime(slinfo.timestamp))
                ui.status(
                    _("Smartlog version %d \nsynced at %s\n\n") %
                    (slinfo.version, formatteddate))
            template = "sl_cloud"
            smartlogstyle = ui.config("templatealias", template)
            if smartlogstyle:
                opts["template"] = "{%s}" % smartlogstyle
            else:
                ui.debug(
                    _("style %s is not defined, skipping") % smartlogstyle,
                    component="commitcloud",
                )

            firstpublic, revdag = serv.makedagwalker(slinfo, repo)
            displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
            cmdutil.rustdisplaygraph(ui,
                                     repo,
                                     revdag,
                                     displayer,
                                     reserved=firstpublic)
            repo.ui.status(
                _("<-: newer  "
                  "->: older  "
                  "q: abort  \n"
                  "a: 1 day forward  d: 1 day back \n"))
            return ui.popbuffer()

        def rightarrow(self):
            self.index += 1

        def leftarrow(self):
            self.index -= 1

        def apress(self):
            if self.index == -1:
                return
            else:
                mintimestamp = self.versions[self.index]["timestamp"] + 86400
            while True:
                self.index -= 1
                if self.index <= -1:
                    break
                if self.versions[self.index]["timestamp"] >= mintimestamp:
                    break

        def dpress(self):
            if self.index == -1:
                maxtimestamp = int(time.time()) - 86400
            else:
                maxtimestamp = self.versions[self.index]["timestamp"] - 86400
            while True:
                self.index += 1
                if (self.index == len(self.versions)
                        or self.versions[self.index]["timestamp"] <=
                        maxtimestamp):
                    break

        def enter(self):
            return

    viewobj = smartlogview(ui, repo, versions)
    interactiveui.view(viewobj)
Example #14
0
        def __init__(self, ui, repo, reponame, workspacename, **opts):
            self.ui = ui
            self.repo = repo
            self.reponame = reponame
            self.workspacename = workspacename
            self.opts = opts
            self.serv = service.get(ui, tokenmod.TokenLocator(ui).token)
            self.servlock = threading.Lock()
            self.renderevent = threading.Event()
            self.running = True
            self.cache = {}
            with progress.spinner(ui, _("fetching cloud smartlog history")):
                self.versions = sorted(
                    self.serv.gethistoricalversions(reponame, workspacename),
                    key=lambda version: version["version_number"],
                )

            initversion = opts.get("workspace_version")
            date = opts.get("date")
            inittime = int(util.parsedate(date)[0]) if date else None

            if initversion and inittime:
                raise error.Abort(
                    "'--workspace-version' and '--date' options can't be both provided"
                )

            if inittime:
                timestamps = sorted(
                    self.versions,
                    key=lambda version: version["timestamp"],
                )
                for index, version in enumerate(timestamps):
                    if version["timestamp"] >= inittime:
                        initversion = version["version_number"]
                        break
                    if index == len(timestamps) - 1:
                        raise error.Abort(
                            "You have no recorded history at or after this date"
                        )

            smartlogstyle = ui.config("templatealias", template)
            if smartlogstyle:
                self.opts["template"] = "{%s}" % smartlogstyle

            if initversion:
                initversion = int(initversion)
                for index, version in enumerate(self.versions):
                    if version["version_number"] == initversion:
                        self.cur_index = index
                        break
                else:
                    versionrange = [
                        version["version_number"] for version in self.versions
                    ]
                    raise error.Abort(
                        "workspace version %s is not available (%s to %s are available)"
                        % (initversion, min(versionrange), max(versionrange)))
            else:
                self.cur_index = len(self.versions)
            if opts.get("all"):
                self.limit = 0
            else:
                self.limit = 12 * 7 * 24 * 60 * 60  # 12 weeks
            self.flags = []
            if ui.configbool("commitcloud", "sl_showremotebookmarks"):
                self.flags.append("ADD_REMOTE_BOOKMARKS")
            if ui.configbool("commitcloud", "sl_showallbookmarks"):
                self.flags.append("ADD_ALL_BOOKMARKS")
            if opts.get("force_original_backend"):
                self.flags.append("USE_ORIGINAL_BACKEND")
Example #15
0
File: rage.py Project: jsoref/eden
def _makerage(ui, repo, **opts):
    # Make graphlog shorter.
    configoverrides = {("experimental", "graphshorten"): "1"}

    def hgcmd(cmdname, *args, **additional_opts):
        cmd, opts = cmdutil.getcmdanddefaultopts(cmdname, commands.table)
        opts.update(additional_opts)

        _repo = repo
        if "_repo" in opts:
            _repo = opts["_repo"]
            del opts["_repo"]
        # If we failed to popbuffer for some reason, do not mess up with the
        # main `ui` object.
        newui = ui.copy()
        newui.pushbuffer(error=True)
        try:
            with ui.configoverride(configoverrides, "rage"):
                if cmd.norepo:
                    cmd(newui, *args, **opts)
                else:
                    cmd(newui, _repo, *args, **opts)
        finally:
            return newui.popbuffer()

    basic = [
        ("date", lambda: time.ctime()),
        ("unixname", lambda: encoding.environ.get("LOGNAME")),
        ("hostname", lambda: socket.gethostname()),
        ("repo location", lambda: repo.root),
        ("cwd", lambda: pycompat.getcwd()),
        ("fstype", lambda: util.getfstype(repo.root)),
        ("active bookmark", lambda: bookmarks._readactive(repo, repo._bookmarks)),
        (
            "hg version",
            lambda: __import__(
                "edenscm.mercurial.__version__"
            ).mercurial.__version__.version,
        ),
        ("obsstore size", lambda: str(repo.svfs.stat("obsstore").st_size)),
    ]

    oldcolormode = ui._colormode
    ui._colormode = None

    detailed = [
        ("df -h", lambda: shcmd("df -h", check=False)),
        # smartlog as the user sees it
        ("hg sl", lambda: hgcmd("smartlog", template="{sl_debug}")),
        # unfiltered smartlog for recent hidden changesets, including full
        # node identity
        (
            "hg sl --master='interestingmaster()' -r 'predecessors(draft())'",
            lambda: hgcmd(
                "smartlog",
                master="interestingmaster()",
                rev=["predecessors(draft())"],
                _repo=repo.unfiltered(),
                template='{sub("\\n", " ", "{node} {sl_debug}")}',
            ),
        ),
        (
            'first 20 lines of "hg status"',
            lambda: "\n".join(hgcmd("status").splitlines()[:20]),
        ),
        (
            "hg blackbox",
            lambda: "\n".join(
                hgcmd("blackbox", pattern=BLACKBOX_PATTERN).splitlines()[-500:]
            ),
        ),
        ("hg summary", lambda: hgcmd("summary")),
        ("hg cloud status", lambda: hgcmd("cloud status")),
        ("hg debugprocesstree", lambda: hgcmd("debugprocesstree")),
        ("hg config (local)", lambda: "\n".join(localconfig(ui))),
        ("hg sparse show", lambda: hgcmd("sparse show")),
        ("hg debuginstall", lambda: hgcmd("debuginstall")),
        ("usechg", (usechginfo)),
        (
            "uptime",
            lambda: shcmd(
                "wmic path Win32_OperatingSystem get LastBootUpTime"
                if pycompat.iswindows
                else "uptime"
            ),
        ),
        ("rpm info", (partial(rpminfo, ui))),
        ("klist", lambda: shcmd("klist", check=False)),
        ("ifconfig", lambda: shcmd("ipconfig" if pycompat.iswindows else "ifconfig")),
        (
            "airport",
            lambda: shcmd(
                "/System/Library/PrivateFrameworks/Apple80211."
                + "framework/Versions/Current/Resources/airport "
                + "--getinfo",
                check=False,
            ),
        ),
        (
            'last 100 lines of "hg debugobsolete"',
            lambda: "\n".join(hgcmd("debugobsolete").splitlines()[-100:]),
        ),
        ("infinitepush backup state", lambda: readinfinitepushbackupstate(repo)),
        ("commit cloud workspace sync state", lambda: readcommitcloudstate(repo)),
        (
            "infinitepush / commitcloud backup logs",
            lambda: infinitepushbackuplogs(ui, repo),
        ),
        ("scm daemon logs", lambda: scmdaemonlog(ui, repo)),
        ("debugstatus", lambda: hgcmd("debugstatus")),
        ("debugtree", lambda: hgcmd("debugtree")),
        ("hg config (overrides)", lambda: "\n".join(overriddenconfig(ui))),
        ("edenfs rage", lambda: shcmd("edenfsctl rage --stdout")),
        (
            "environment variables",
            lambda: "\n".join(
                sorted(["{}={}".format(k, v) for k, v in encoding.environ.items()])
            ),
        ),
        ("ssh config", lambda: shcmd("ssh -G hg.vip.facebook.com", check=False)),
    ]

    msg = ""

    if util.safehasattr(repo, "name"):
        # Add the contents of both local and shared pack directories.
        packlocs = {
            "local": lambda category: shallowutil.getlocalpackpath(
                repo.svfs.vfs.base, category
            ),
            "shared": lambda category: shallowutil.getcachepackpath(repo, category),
        }

        for loc, getpath in packlocs.iteritems():
            for category in constants.ALL_CATEGORIES:
                path = getpath(category)
                detailed.append(
                    (
                        "%s packs (%s)" % (loc, constants.getunits(category)),
                        lambda path=path: "%s:\n%s"
                        % (
                            path,
                            shcmd(
                                "dir /o-s %s" % os.path.normpath(path)
                                if pycompat.iswindows
                                else "ls -lhS %s" % path
                            ),
                        ),
                    )
                )

    footnotes = []
    timeout = opts.get("timeout") or 20

    def _failsafe(gen, timeout=timeout):
        class TimedOut(RuntimeError):
            pass

        def target(result, gen):
            try:
                result.append(gen())
            except TimedOut:
                return
            except Exception as ex:
                index = len(footnotes) + 1
                footnotes.append(
                    "[%d]: %s\n%s\n\n" % (index, str(ex), traceback.format_exc())
                )
                result.append("(Failed. See footnote [%d])" % index)

        result = []
        thread = threading.Thread(target=target, args=(result, gen))
        thread.daemon = True
        thread.start()
        thread.join(timeout)
        if result:
            value = result[0]
            return value
        else:
            if thread.is_alive():
                # Attempt to stop the thread, since hg is not thread safe.
                # There is no pure Python API to interrupt a thread.
                # But CPython C API can do that.
                ctypes.pythonapi.PyThreadState_SetAsyncExc(
                    ctypes.c_long(thread.ident), ctypes.py_object(TimedOut)
                )
            return (
                "(Did not complete in %s seconds, rerun with a larger --timeout to collect this)"
                % timeout
            )

    msg = []
    profile = []
    allstart = time.time()
    for name, gen in basic:
        msg.append("%s: %s\n\n" % (name, _failsafe(gen)))
    profile.append((time.time() - allstart, "basic info", None))
    for name, gen in detailed:
        start = time.time()
        with progress.spinner(ui, "collecting %r" % name):
            value = _failsafe(gen)
        finish = time.time()
        msg.append(
            "%s: (%.2f s)\n---------------------------\n%s\n\n"
            % (name, finish - start, value)
        )
        profile.append((finish - start, name, value.count("\n")))
    allfinish = time.time()
    profile.append((allfinish - allstart, "total time", None))

    msg.append("hg rage profile:\n")
    width = max([len(name) for _t, name, _l in profile])
    for timetaken, name, lines in reversed(sorted(profile)):
        m = "  %-*s  %8.2f s" % (width + 1, name + ":", timetaken)
        if lines is not None:
            msg.append("%s for %4d lines\n" % (m, lines))
        else:
            msg.append("%s\n" % m)
    msg.append("\n")

    msg.extend(footnotes)
    msg = "".join(msg)

    ui._colormode = oldcolormode
    return msg
Example #16
0
        def _prefetch(self,
                      revs,
                      base=None,
                      pats=None,
                      opts=None,
                      matcher=None):
            fallbackpath = self.fallbackpath
            if fallbackpath:
                # If we know a rev is on the server, we should fetch the server
                # version of those files, since our local file versions might
                # become obsolete if the local commits are stripped.
                with progress.spinner(self.ui,
                                      _("finding outgoing revisions")):
                    localrevs = self.revs("outgoing(%s)", fallbackpath)
                if base is not None and base != nullrev:
                    serverbase = list(
                        self.revs("first(reverse(::%s) - %ld)", base,
                                  localrevs))
                    if serverbase:
                        base = serverbase[0]
            else:
                localrevs = self

            mfl = self.manifestlog
            if base is not None:
                mfdict = mfl[self[base].manifestnode()].read()
                skip = set(mfdict.iteritems())
            else:
                skip = set()

            # Copy the skip set to start large and avoid constant resizing,
            # and since it's likely to be very similar to the prefetch set.
            files = skip.copy()
            serverfiles = skip.copy()
            visited = set()
            visited.add(nullid)
            with progress.bar(self.ui, _("prefetching"),
                              total=len(revs)) as prog:
                for rev in sorted(revs):
                    ctx = self[rev]
                    if pats:
                        m = scmutil.match(ctx, pats, opts)
                    if matcher is None:
                        matcher = self.maybesparsematch(rev)

                    mfnode = ctx.manifestnode()
                    mfctx = mfl[mfnode]

                    # Decompressing manifests is expensive.
                    # When possible, only read the deltas.
                    p1, p2 = mfctx.parents
                    if p1 in visited and p2 in visited:
                        mfdict = mfctx.readnew()
                    else:
                        mfdict = mfctx.read()

                    diff = mfdict.iteritems()
                    if pats:
                        diff = (pf for pf in diff if m(pf[0]))
                    if matcher:
                        diff = (pf for pf in diff if matcher(pf[0]))
                    if rev not in localrevs:
                        serverfiles.update(diff)
                    else:
                        files.update(diff)

                    visited.add(mfctx.node())
                    prog.value += 1

            files.difference_update(skip)
            serverfiles.difference_update(skip)

            # Fetch files known to be on the server
            if serverfiles:
                results = [(path, hex(fnode)) for (path, fnode) in serverfiles]
                self.fileservice.prefetch(results, force=True)

            # Fetch files that may or may not be on the server
            if files:
                results = [(path, hex(fnode)) for (path, fnode) in files]
                self.fileservice.prefetch(results)
Example #17
0
def cloudsmartlog(ui, repo, template="sl_cloud", **opts):
    """get smartlog view for the default workspace of the given user

    If the requested template is not defined in the config
    the command provides a simple view as a list of draft commits.
    """

    reponame = ccutil.getreponame(repo)
    workspacename = workspace.parseworkspace(ui, opts)
    if workspacename is None:
        workspacename = workspace.currentworkspace(repo)
    if workspacename is None:
        workspacename = workspace.defaultworkspace(ui)

    if opts.get("history"):
        interactivehistory.showhistory(ui, repo, reponame, workspacename,
                                       **opts)
        return

    date = opts.get("date")
    version = opts.get("workspace_version")
    if date:
        parseddate = util.parsedate(date)
    else:
        parseddate = None

    ui.status(
        _("searching draft commits for the '%s' workspace for the '%s' repo\n")
        % (workspacename, reponame),
        component="commitcloud",
    )
    serv = service.get(ui, tokenmod.TokenLocator(ui).token)
    if parseddate is None and not version:
        with progress.spinner(ui, _("fetching")):
            firstpublic, revdag = serv.getsmartlog(reponame, workspacename,
                                                   repo, 0)
    else:
        with progress.spinner(ui, _("fetching")):
            firstpublic, revdag, slversion, sltimestamp = serv.getsmartlogbyversion(
                reponame, workspacename, repo, parseddate, version, 0)
    if parseddate or version:
        formatteddate = time.strftime("%Y-%m-%d %H:%M:%S",
                                      time.localtime(sltimestamp))
        ui.status(
            _("Smartlog version %d \nsynced at %s\n\n") %
            (slversion, formatteddate))
    else:
        ui.status(_("Smartlog:\n\n"))
    # set up pager
    ui.pager("smartlog")

    smartlogstyle = ui.config("templatealias", template)
    # if style is defined in templatealias section of config apply that style
    if smartlogstyle:
        opts["template"] = "{%s}" % smartlogstyle
    else:
        ui.debug(
            _("style %s is not defined, skipping") % smartlogstyle,
            component="commitcloud",
        )

    # show all the nodes
    displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
    if ui.config("experimental", "graph.renderer") == "legacy":
        cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
    else:
        cmdutil.rustdisplaygraph(ui,
                                 repo,
                                 revdag,
                                 displayer,
                                 reserved=firstpublic)
Example #18
0
def _makerage(ui, repo, **opts):
    configoverrides = {
        # Make graphlog shorter.
        ("experimental", "graphshorten"):
        "1",
        # Force use of lines-square renderer, as the user's configuration may
        # not render properly in a text file.
        ("experimental", "graph.renderer"):
        "lines-square",
        # Reduce the amount of data used for debugnetwork speed tests to
        # increase the chance they complete within 20s.
        ("debugnetwork", "speed-test-download-size"):
        "4M",
        ("debugnetwork", "speed-test-upload-size"):
        "1M",
    }

    # Override the encoding to "UTF-8" to generate the rage in UTF-8.
    oldencoding = encoding.encoding
    oldencodingmode = encoding.encodingmode
    encoding.encoding = "UTF-8"
    encoding.encodingmode = "replace"

    def hgcmd(cmdname, *args, **additional_opts):
        cmd, opts = cmdutil.getcmdanddefaultopts(cmdname, commands.table)
        opts.update(additional_opts)

        _repo = repo
        if "_repo" in opts:
            _repo = opts["_repo"]
            del opts["_repo"]
        # If we failed to popbuffer for some reason, do not mess up with the
        # main `ui` object.
        newui = ui.copy()
        newui.pushbuffer(error=True, subproc=True)
        newui._colormode = None

        def remoteui(orig, src, opts):
            rui = orig(src, opts)
            rui._outputui = newui
            return rui

        try:
            with newui.configoverride(configoverrides,
                                      "rage"), extensions.wrappedfunction(
                                          hg, "remoteui", remoteui):
                if cmd.norepo:
                    cmd(newui, *args, **opts)
                else:
                    cmd(newui, _repo, *args, **opts)
        finally:
            return newui.popbuffer()

    basic = [
        ("date", lambda: time.ctime()),
        ("unixname", lambda: encoding.environ.get("LOGNAME")),
        ("hostname", lambda: socket.gethostname()),
        ("repo location", lambda: repo.root),
        ("cwd", lambda: pycompat.getcwd()),
        ("fstype", lambda: util.getfstype(repo.root)),
        ("active bookmark",
         lambda: bookmarks._readactive(repo, repo._bookmarks)),
        (
            "hg version",
            lambda: __import__("edenscm.mercurial.__version__").mercurial.
            __version__.version,
        ),
    ]

    def _edenfs_rage():
        ragecmd = "edenfsctl rage --stdout"
        if opts.get("preview"):
            return shcmd(ragecmd + " --dry-run")
        return shcmd(ragecmd)

    detailed = [
        (
            "disk space usage",
            lambda: shcmd(
                "wmic LogicalDisk Where DriveType=3 Get DeviceId,FileSystem,FreeSpace,Size"
                if pycompat.iswindows else "df -h",
                check=False,
            ),
        ),
        # smartlog as the user sees it
        ("hg sl", lambda: hgcmd("smartlog", template="{sl_debug}")),
        (
            "hg debugmetalog -t 'since 2d ago'",
            lambda: hgcmd("debugmetalog", time_range=["since 2d ago"]),
        ),
        (
            'first 20 lines of "hg status"',
            lambda: "\n".join(hgcmd("status").splitlines()[:20]),
        ),
        (
            "hg debugmutation -r 'draft() & date(-4)' -t 'since 4d ago'",
            lambda: hgcmd("debugmutation",
                          rev=["draft() & date(-4)"],
                          time_range=["since 4d ago"]),
        ),
        (
            "hg bookmarks --list-subscriptions",
            lambda: hgcmd("bookmarks", list_subscriptions=True),
        ),
        ("sigtrace", lambda: readsigtraces(repo)),
        (
            "hg blackbox",
            lambda: "\n".join(
                hgcmd("blackbox", pattern=BLACKBOX_PATTERN).splitlines()[-500:]
            ),
        ),
        ("hg summary", lambda: hgcmd("summary")),
        ("hg cloud status", lambda: hgcmd("cloud status")),
        ("hg debugprocesstree", lambda: hgcmd("debugprocesstree")),
        ("hg config (local)", lambda: "\n".join(localconfig(ui))),
        ("hg sparse", lambda: hgcmd("sparse")),
        ("hg debugchangelog", lambda: hgcmd("debugchangelog")),
        ("hg debugexpandpaths", lambda: hgcmd("debugexpandpaths")),
        ("hg debuginstall", lambda: hgcmd("debuginstall")),
        ("hg debugdetectissues", lambda: hgcmd("debugdetectissues")),
        ("usechg", usechginfo),
        (
            "uptime",
            lambda: shcmd("wmic path Win32_OperatingSystem get LastBootUpTime"
                          if pycompat.iswindows else "uptime"),
        ),
        ("rpm info", (partial(rpminfo, ui))),
        ("klist", lambda: shcmd("klist", check=False)),
        ("ifconfig", lambda: shcmd("ipconfig"
                                   if pycompat.iswindows else "ifconfig")),
        (
            "airport",
            lambda: shcmd(
                "/System/Library/PrivateFrameworks/Apple80211." +
                "framework/Versions/Current/Resources/airport " + "--getinfo",
                check=False,
            ),
        ),
        ("hg debugnetwork", lambda: hgcmd("debugnetwork")),
        ("infinitepush backup state",
         lambda: readinfinitepushbackupstate(repo)),
        ("commit cloud workspace sync state",
         lambda: readcommitcloudstate(repo)),
        (
            "infinitepush / commitcloud backup logs",
            lambda: infinitepushbackuplogs(ui, repo),
        ),
        ("scm daemon logs", lambda: scmdaemonlog(ui, repo)),
        ("debugstatus", lambda: hgcmd("debugstatus")),
        ("debugtree", lambda: hgcmd("debugtree")),
        ("hg config (all)", lambda: "\n".join(allconfig(ui))),
        ("edenfs rage", _edenfs_rage),
        (
            "environment variables",
            lambda: "\n".join(
                sorted([
                    "{}={}".format(k, v) for k, v in encoding.environ.items()
                ])),
        ),
        ("ssh config",
         lambda: shcmd("ssh -G hg.vip.facebook.com", check=False)),
        ("debuglocks", lambda: hgcmd("debuglocks")),
        ("x2pagentd info", lambda: checkproxyagentstate(ui)),
    ]

    msg = ""

    if util.safehasattr(repo, "name"):
        # Add the contents of both local and shared pack directories.
        packlocs = {
            "local":
            lambda category: shallowutil.getlocalpackpath(
                repo.svfs.vfs.base, category),
            "shared":
            lambda category: shallowutil.getcachepackpath(repo, category),
        }

        for loc, getpath in pycompat.iteritems(packlocs):
            for category in constants.ALL_CATEGORIES:
                path = getpath(category)
                detailed.append((
                    "%s packs (%s)" % (loc, constants.getunits(category)),
                    lambda path=path: "%s:\n%s" % (
                        path,
                        shcmd("dir /o-s %s" % os.path.normpath(path)
                              if pycompat.iswindows else "ls -lhS %s" % path),
                    ),
                ))

    footnotes = []
    timeout = opts.get("timeout") or 20

    def _failsafe(gen, timeout=timeout):
        class TimedOut(RuntimeError):
            pass

        def target(result, gen):
            try:
                result.append(gen())
            except TimedOut:
                return
            except Exception as ex:
                index = len(footnotes) + 1
                footnotes.append("[%d]: %s\n%s\n\n" %
                                 (index, str(ex), traceback.format_exc()))
                result.append("(Failed. See footnote [%d])" % index)

        result = []
        thread = threading.Thread(target=target, args=(result, gen))
        thread.daemon = True
        thread.start()
        thread.join(timeout)
        if result:
            value = result[0]
            return value
        else:
            if thread.is_alive():
                # Attempt to stop the thread, since hg is not thread safe.
                # There is no pure Python API to interrupt a thread.
                # But CPython C API can do that.
                ctypes.pythonapi.PyThreadState_SetAsyncExc(
                    ctypes.c_long(thread.ident), ctypes.py_object(TimedOut))
            return (
                "(Did not complete in %s seconds, rerun with a larger --timeout to collect this)"
                % timeout)

    msg = []
    profile = []
    allstart = time.time()
    for name, gen in basic:
        msg.append("%s: %s\n\n" % (name, _failsafe(gen)))
    profile.append((time.time() - allstart, "basic info", None))
    for name, gen in detailed:
        start = time.time()
        with progress.spinner(ui, "collecting %r" % name):
            value = _failsafe(gen)
        finish = time.time()
        msg.append("%s: (%.2f s)\n---------------------------\n%s\n\n" %
                   (name, finish - start, value))
        profile.append((finish - start, name, value.count("\n")))
    allfinish = time.time()
    profile.append((allfinish - allstart, "total time", None))

    msg.append("hg rage profile:\n")
    width = max([len(name) for _t, name, _l in profile])
    for timetaken, name, lines in reversed(sorted(profile)):
        m = "  %-*s  %8.2f s" % (width + 1, name + ":", timetaken)
        if lines is not None:
            msg.append("%s for %4d lines\n" % (m, lines))
        else:
            msg.append("%s\n" % m)
    msg.append("\n")

    msg.extend(footnotes)
    msg = "".join(msg)

    encoding.encoding = oldencoding
    encoding.encodingmode = oldencodingmode
    return msg
Example #19
0
def _innerwalk(self, match, event, span):
    state = self._fsmonitorstate
    clock, ignorehash, notefiles = state.get()
    if not clock:
        if state.walk_on_invalidate:
            raise fsmonitorfallback("no clock")
        # Initial NULL clock value, see
        # https://facebook.github.io/watchman/docs/clockspec.html
        clock = "c:0:0"
        notefiles = []

    ignore = self.dirstate._ignore

    # experimental config: experimental.fsmonitor.skipignore
    if not self._ui.configbool("experimental", "fsmonitor.skipignore"):
        if ignorehash and _hashignore(
                ignore) != ignorehash and clock != "c:0:0":
            # ignore list changed -- can't rely on Watchman state any more
            if state.walk_on_invalidate:
                raise fsmonitorfallback("ignore rules changed")
            notefiles = []
            clock = "c:0:0"

    matchfn = match.matchfn
    matchalways = match.always()
    dmap = self.dirstate._map
    if util.safehasattr(dmap, "_map"):
        # for better performance, directly access the inner dirstate map if the
        # standard dirstate implementation is in use.
        dmap = dmap._map
    if "treestate" in self._repo.requirements:
        # treestate has a fast path to filter out ignored directories.
        ignorevisitdir = self.dirstate._ignore.visitdir

        def dirfilter(path):
            result = ignorevisitdir(path.rstrip("/"))
            return result == "all"

        nonnormalset = self.dirstate._map.nonnormalsetfiltered(dirfilter)
    else:
        nonnormalset = self.dirstate._map.nonnormalset

    event["old_clock"] = clock
    event["old_files"] = blackbox.shortlist(sorted(nonnormalset))
    span.record(oldclock=clock, oldfileslen=len(nonnormalset))
    state.setlastnonnormalfilecount(len(nonnormalset))

    copymap = self.dirstate._map.copymap
    getkind = stat.S_IFMT
    dirkind = stat.S_IFDIR
    regkind = stat.S_IFREG
    lnkkind = stat.S_IFLNK
    join = self.dirstate._join
    normcase = util.normcase
    fresh_instance = False

    exact = False
    if match.isexact():  # match.exact
        exact = True

    if not exact and self.dirstate._checkcase:
        # note that even though we could receive directory entries, we're only
        # interested in checking if a file with the same name exists. So only
        # normalize files if possible.
        normalize = self.dirstate._normalizefile
    else:
        normalize = None

    # step 2: query Watchman
    with progress.spinner(self._ui, "watchman query"):
        try:
            # Use the user-configured timeout for the query.
            # Add a little slack over the top of the user query to allow for
            # overheads while transferring the data
            excludes = [
                "anyof", ["dirname", ".hg"], ["name", ".hg", "wholename"]
            ]
            # Exclude submodules.
            if git.isgitformat(self._repo):
                submods = git.parsesubmodules(self._repo[None])
                excludes += [["dirname", s.path] for s in submods]
            self._watchmanclient.settimeout(state.timeout + 0.1)
            result = self._watchmanclient.command(
                "query",
                {
                    "fields": ["mode", "mtime", "size", "exists", "name"],
                    "since": clock,
                    "expression": ["not", excludes],
                    "sync_timeout": int(state.timeout * 1000),
                    "empty_on_fresh_instance": state.walk_on_invalidate,
                },
            )
        except Exception as ex:
            event["is_error"] = True
            span.record(error=ex)
            _handleunavailable(self._ui, state, ex)
            self._watchmanclient.clearconnection()
            # XXX: Legacy scuba logging. Remove this once the source of truth
            # is moved to the Rust Event.
            self._ui.log("fsmonitor_status", fsmonitor_status="exception")
            if self._ui.configbool("fsmonitor",
                                   "fallback-on-watchman-exception"):
                raise fsmonitorfallback("exception during run")
            else:
                raise ex
        else:
            # We need to propagate the last observed clock up so that we
            # can use it for our next query
            event["new_clock"] = result["clock"]
            event["is_fresh"] = result["is_fresh_instance"]
            span.record(newclock=result["clock"],
                        isfresh=result["is_fresh_instance"])
            state.setlastclock(result["clock"])
            state.setlastisfresh(result["is_fresh_instance"])

            files = list(
                filter(lambda x: _isutf8(self._ui, x["name"]),
                       result["files"]))

            self._ui.metrics.gauge("watchmanfilecount", len(files))
            # Ideally we'd just track a bool for fresh_instance or not, but there
            # could be multiple queries during a command, so let's use a counter.
            self._ui.metrics.gauge(
                "watchmanfreshinstances",
                1 if result["is_fresh_instance"] else 0,
            )

            if result["is_fresh_instance"]:
                if not self._ui.plain() and self._ui.configbool(
                        "fsmonitor", "warn-fresh-instance"):
                    oldpid = _watchmanpid(event["old_clock"])
                    newpid = _watchmanpid(event["new_clock"])
                    if oldpid is not None and newpid is not None and oldpid != newpid:
                        self._ui.warn(
                            _("warning: watchman has recently restarted (old pid %s, new pid %s) - operation will be slower than usual\n"
                              ) % (oldpid, newpid))
                    elif oldpid is None and newpid is not None:
                        self._ui.warn(
                            _("warning: watchman has recently started (pid %s) - operation will be slower than usual\n"
                              ) % (newpid, ))
                    else:
                        self._ui.warn(
                            _("warning: watchman failed to catch up with file change events and requires a full scan - operation will be slower than usual\n"
                              ))

                if state.walk_on_invalidate:
                    state.invalidate(reason="fresh_instance")
                    raise fsmonitorfallback("fresh instance")
                fresh_instance = True
                # Ignore any prior noteable files from the state info
                notefiles = []
            else:
                count = len(files)
                state.setwatchmanchangedfilecount(count)
                event["new_files"] = blackbox.shortlist(
                    sorted(e["name"] for e in files), count)
                span.record(newfileslen=len(files))
            # XXX: Legacy scuba logging. Remove this once the source of truth
            # is moved to the Rust Event.
            if event["is_fresh"]:
                self._ui.log("fsmonitor_status", fsmonitor_status="fresh")
            else:
                self._ui.log("fsmonitor_status", fsmonitor_status="normal")

    results = {}

    # for file paths which require normalization and we encounter a case
    # collision, we store our own foldmap
    if normalize:
        foldmap = dict((normcase(k), k) for k in results)

    switch_slashes = pycompat.ossep == "\\"
    # The order of the results is, strictly speaking, undefined.
    # For case changes on a case insensitive filesystem we may receive
    # two entries, one with exists=True and another with exists=False.
    # The exists=True entries in the same response should be interpreted
    # as being happens-after the exists=False entries due to the way that
    # Watchman tracks files.  We use this property to reconcile deletes
    # for name case changes.
    ignorelist = []
    ignorelistappend = ignorelist.append
    with progress.bar(self.ui, _("Watchman results"), _("files"),
                      len(files)) as prog:
        for entry in files:
            prog.value += 1
            fname = entry["name"]

            if _fixencoding:
                fname = _watchmantofsencoding(fname)
            if switch_slashes:
                fname = fname.replace("\\", "/")
            if normalize:
                normed = normcase(fname)
                fname = normalize(fname, True, True)
                foldmap[normed] = fname
            fmode = entry["mode"]
            fexists = entry["exists"]
            kind = getkind(fmode)

            if not fexists:
                # if marked as deleted and we don't already have a change
                # record, mark it as deleted.  If we already have an entry
                # for fname then it was either part of walkexplicit or was
                # an earlier result that was a case change
                if (fname not in results and fname in dmap
                        and (matchalways or matchfn(fname))):
                    results[fname] = None
            elif kind == dirkind:
                if fname in dmap and (matchalways or matchfn(fname)):
                    results[fname] = None
            elif kind == regkind or kind == lnkkind:
                if fname in dmap:
                    if matchalways or matchfn(fname):
                        results[fname] = entry
                else:
                    ignored = ignore(fname)
                    if ignored:
                        ignorelistappend(fname)
                    if (matchalways or matchfn(fname)) and not ignored:
                        results[fname] = entry
            elif fname in dmap and (matchalways or matchfn(fname)):
                results[fname] = None
            elif fname in match.files():
                match.bad(fname, filesystem.badtype(kind))

    # step 3: query notable files we don't already know about
    # XXX try not to iterate over the entire dmap
    if normalize:
        # any notable files that have changed case will already be handled
        # above, so just check membership in the foldmap
        notefiles = set((normalize(f, True, True) for f in notefiles
                         if normcase(f) not in foldmap))
    visit = set((f for f in notefiles if (
        f not in results and matchfn(f) and (f in dmap or not ignore(f)))))

    if not fresh_instance:
        if matchalways:
            visit.update(f for f in nonnormalset if f not in results)
            visit.update(f for f in copymap if f not in results)
        else:
            visit.update(f for f in nonnormalset
                         if f not in results and matchfn(f))
            visit.update(f for f in copymap if f not in results and matchfn(f))
    else:
        if matchalways:
            visit.update(f for f in dmap if f not in results)
            visit.update(f for f in copymap if f not in results)
        else:
            visit.update(f for f in dmap if f not in results and matchfn(f))
            visit.update(f for f in copymap if f not in results and matchfn(f))

    # audit returns False for paths with one of its parent directories being a
    # symlink.
    audit = pathutil.pathauditor(self.dirstate._root, cached=True).check
    with progress.bar(self.ui, _("Auditing paths"), _("files"),
                      len(visit)) as prog:
        auditpass = []
        for f in visit:
            prog.value += 1
            if audit(f):
                auditpass.append(f)
        auditpass.sort()

    auditfail = visit.difference(auditpass)
    droplist = []
    droplistappend = droplist.append
    for f in auditfail:
        # For auditfail paths, they should be treated as not existed in working
        # copy.
        filestate = dmap.get(f, ("?", 0, 0, 0))[0]
        if filestate in ("?", ):
            # do not exist in working parents, remove them from treestate and
            # avoid walking through them.
            droplistappend(f)
            results.pop(f, None)
        else:
            # tracked, mark as deleted
            results[f] = None

    auditpassiter = iter(auditpass)

    def nf():
        return next(auditpassiter)

    with progress.bar(self.ui, _("Getting metadata"), _("files"),
                      len(auditpass)) as prog:
        # Break it into chunks so we get some progress information
        for i in range(0, len(auditpass), 5000):
            chunk = auditpass[i:i + 5000]
            for st in util.statfiles([join(f) for f in chunk]):
                prog.value += 1
                f = nf()
                if (st and not ignore(f)) or f in dmap:
                    results[f] = st
                elif not st:
                    # '?' (untracked) file was deleted from the filesystem - remove it
                    # from treestate.
                    #
                    # We can only update the dirstate (and treestate) while holding the
                    # wlock. That happens inside poststatus.__call__ -> state.set. So
                    # buffer what files to "drop" so state.set can clean them up.
                    entry = dmap.get(f, None)
                    if entry and entry[0] == "?":
                        droplistappend(f)

    # The droplist and ignorelist need to match setlastclock()
    state.setdroplist(droplist)
    state.setignorelist(ignorelist)

    results.pop(".hg", None)
    return pycompat.iteritems(results)
Example #20
0
def _makerage(ui, repo, **opts):
    configoverrides = {
        # Make graphlog shorter.
        ("experimental", "graphshorten"): "1",
        # Force use of lines-square renderer, as the user's configuration may
        # not render properly in a text file.
        ("experimental", "graph.renderer"): "lines-square",
        # Reduce the amount of data used for debugnetwork speed tests to
        # increase the chance they complete within 20s.
        ("debugnetwork", "speed-test-download-size"): "4M",
        ("debugnetwork", "speed-test-upload-size"): "1M",
    }

    # Override the encoding to "UTF-8" to generate the rage in UTF-8.
    oldencoding = encoding.encoding
    oldencodingmode = encoding.encodingmode
    encoding.encoding = "UTF-8"
    encoding.encodingmode = "replace"

    def hgcmd(cmdname, *args, **additional_opts):
        cmdargs = ["hg", *cmdname.split(), *args]
        for flagname, flagvalue in additional_opts.items():
            flagname = flagname.replace("_", "-")
            if isinstance(flagvalue, list):
                cmdargs += [f"--{flagname}={v}" for v in flagvalue]
            else:
                cmdargs += [f"--{flagname}={flagvalue}"]
        fin = util.stringio()
        fout = ferr = util.stringio()
        status = bindings.commands.run(cmdargs, fin, fout, ferr)

        output = fout.getvalue().decode()
        if status != 0:
            output += f"[{status}]\n"
        return output

    basic = [
        ("date", lambda: time.ctime()),
        ("unixname", lambda: encoding.environ.get("LOGNAME")),
        ("hostname", lambda: socket.gethostname()),
        ("repo location", lambda: repo.root),
        ("cwd", lambda: pycompat.getcwd()),
        ("fstype", lambda: util.getfstype(repo.root)),
        ("active bookmark", lambda: bookmarks._readactive(repo, repo._bookmarks)),
        (
            "hg version",
            lambda: __import__(
                "edenscm.mercurial.__version__"
            ).mercurial.__version__.version,
        ),
    ]

    def _edenfs_rage():
        ragecmd = "edenfsctl rage --stdout"
        if opts.get("preview"):
            return shcmd(ragecmd + " --dry-run")
        return shcmd(ragecmd)

    detailed = [
        (
            "disk space usage",
            lambda: shcmd(
                "wmic LogicalDisk Where DriveType=3 Get DeviceId,FileSystem,FreeSpace,Size"
                if pycompat.iswindows
                else "df -h",
                check=False,
            ),
        ),
        # smartlog as the user sees it
        ("hg sl", lambda: hgcmd("smartlog", template="{sl_debug}")),
        (
            "hg debugmetalog -t 'since 2d ago'",
            lambda: hgcmd("debugmetalog", time_range=["since 2d ago"]),
        ),
        (
            'first 20 lines of "hg status"',
            lambda: "\n".join(hgcmd("status").splitlines()[:20]),
        ),
        (
            "hg debugmutation -r 'draft() & date(-4)' -t 'since 4d ago'",
            lambda: hgcmd(
                "debugmutation", rev=["draft() & date(-4)"], time_range=["since 4d ago"]
            ),
        ),
        (
            "hg bookmarks --list-subscriptions",
            lambda: hgcmd("bookmarks", list_subscriptions=True),
        ),
        ("sigtrace", lambda: readsigtraces(repo)),
        (
            "hg blackbox",
            lambda: "\n".join(
                hgcmd("blackbox", pattern=BLACKBOX_PATTERN).splitlines()[-500:]
            ),
        ),
        ("hg summary", lambda: hgcmd("summary")),
        ("hg cloud status", lambda: hgcmd("cloud status")),
        ("hg debugprocesstree", lambda: hgcmd("debugprocesstree")),
        ("hg config (local)", lambda: "\n".join(localconfig(ui))),
        ("hg sparse", lambda: hgcmd("sparse")),
        ("hg debugchangelog", lambda: hgcmd("debugchangelog")),
        ("hg debugexpandpaths", lambda: hgcmd("debugexpandpaths")),
        ("hg debuginstall", lambda: hgcmd("debuginstall")),
        ("hg debugdetectissues", lambda: hgcmd("debugdetectissues")),
        ("usechg", usechginfo),
        (
            "uptime",
            lambda: shcmd(
                "wmic path Win32_OperatingSystem get LastBootUpTime"
                if pycompat.iswindows
                else "uptime"
            ),
        ),
        ("rpm info", (partial(rpminfo, ui))),
        ("klist", lambda: shcmd("klist", check=False)),
        ("ifconfig", lambda: shcmd("ipconfig" if pycompat.iswindows else "ifconfig")),
        (
            "airport",
            lambda: shcmd(
                "/System/Library/PrivateFrameworks/Apple80211."
                + "framework/Versions/Current/Resources/airport "
                + "--getinfo",
                check=False,
            ),
        ),
        ("hg debugnetwork", lambda: hgcmd("debugnetwork")),
        ("hg debugnetworkdoctor", lambda: hgcmd("debugnetworkdoctor")),
        ("infinitepush backup state", lambda: readinfinitepushbackupstate(repo)),
        ("commit cloud workspace sync state", lambda: readcommitcloudstate(repo)),
        (
            "infinitepush / commitcloud backup logs",
            lambda: infinitepushbackuplogs(ui, repo),
        ),
        ("scm daemon logs", lambda: scmdaemonlog(ui, repo)),
        ("debugstatus", lambda: hgcmd("debugstatus")),
        ("debugtree", lambda: hgcmd("debugtree")),
        ("hg config (all)", lambda: "\n".join(allconfig(ui))),
        ("edenfs rage", _edenfs_rage),
        (
            "environment variables",
            lambda: "\n".join(
                sorted(["{}={}".format(k, v) for k, v in encoding.environ.items()])
            ),
        ),
        ("ssh config", lambda: shcmd("ssh -G hg.vip.facebook.com", check=False)),
        ("debuglocks", lambda: hgcmd("debuglocks")),
        ("x2pagentd info", lambda: checkproxyagentstate(ui)),
        ("sks-agent rage", lambda: sksagentrage(ui)),
    ]

    msg = ""

    footnotes = []
    timeout = opts.get("timeout") or 20

    def _failsafe(gen, timeout=timeout):
        class TimedOut(RuntimeError):
            pass

        def target(result, gen):
            try:
                result.append(gen())
            except TimedOut:
                return
            except Exception as ex:
                index = len(footnotes) + 1
                footnotes.append(
                    "[%d]: %s\n%s\n\n" % (index, str(ex), traceback.format_exc())
                )
                result.append("(Failed. See footnote [%d])" % index)

        result = []
        thread = threading.Thread(target=target, args=(result, gen))
        thread.daemon = True
        thread.start()
        thread.join(timeout)
        if result:
            value = result[0]
            return value
        else:
            if thread.is_alive():
                # Attempt to stop the thread, since hg is not thread safe.
                # There is no pure Python API to interrupt a thread.
                # But CPython C API can do that.
                ctypes.pythonapi.PyThreadState_SetAsyncExc(
                    ctypes.c_long(thread.ident), ctypes.py_object(TimedOut)
                )
            return (
                "(Did not complete in %s seconds, rerun with a larger --timeout to collect this)"
                % timeout
            )

    msg = []
    profile = []
    allstart = time.time()
    for name, gen in basic:
        msg.append("%s: %s\n\n" % (name, _failsafe(gen)))
    profile.append((time.time() - allstart, "basic info", None))
    for name, gen in detailed:
        start = time.time()
        with progress.spinner(ui, name):
            value = _failsafe(gen)
        finish = time.time()
        msg.append(
            "%s: (%.2f s)\n---------------------------\n%s\n\n"
            % (name, finish - start, value)
        )
        profile.append((finish - start, name, value.count("\n")))
    allfinish = time.time()
    profile.append((allfinish - allstart, "total time", None))

    msg.append("hg rage profile:\n")
    width = max([len(name) for _t, name, _l in profile])
    for timetaken, name, lines in reversed(sorted(profile)):
        m = "  %-*s  %8.2f s" % (width + 1, name + ":", timetaken)
        if lines is not None:
            msg.append("%s for %4d lines\n" % (m, lines))
        else:
            msg.append("%s\n" % m)
    msg.append("\n")

    msg.extend(footnotes)
    msg = "".join(msg)

    encoding.encoding = oldencoding
    encoding.encodingmode = oldencodingmode
    return msg