Exemple #1
0
 async def export(self, sha, update_if_exists=False) -> str:
     cmd = ["diff", "%s^..%s" % (sha, sha)]
     with sentry.Span("vcs.export", self.remote_url) as par_span:
         par_span.set_tag("sha", sha)
         par_span.set_tag("backend", "git")
         await self.ensure(update_if_exists=update_if_exists)
         result = await self.run(cmd)
     return result
Exemple #2
0
 async def show(self, sha, filename, update_if_exists=False) -> str:
     cmd = ["show", "{}:{}".format(sha, filename)]
     with sentry.Span("vcs.show", self.remote_url) as par_span:
         par_span.set_tag("sha", sha)
         par_span.set_tag("filename", filename)
         par_span.set_tag("backend", "git")
         await self.ensure(update_if_exists=update_if_exists)
         result = await self.run(cmd)
     return result
Exemple #3
0
    async def update(self, allow_cleanup=False):
        with sentry.Span("vcs.update",
                         description=self.remote_url) as par_span:
            par_span.set_tag("backend", "git")
            par_span.set_tag("allow_cleanup", allow_cleanup)

            if allow_cleanup:
                await self.run(["fetch", "--all", "--force", "-p"])
            else:
                await self.run(["fetch", "--all", "--force"])
Exemple #4
0
    async def run(self, cmd, **kwargs) -> str:
        with sentry.Span("vcs.run-command",
                         description=self.remote_url) as span:
            span.set_data("command", " ".join(cmd))
            span.set_data("backend", "git")

            cmd = [self.binary_path] + cmd
            try:
                output = await super().run(cmd, **kwargs)
                span.set_data("output_bytes", len(output))
                return output

            except CommandError as e:
                stderr = e.stderr.decode("utf-8") if e.stderr else ""
                if "unknown revision or path" in stderr:
                    # fatal: ambiguous argument '82f750e7a3b692e049b95ed66bf9149f56218733^..82f750e7a3b692e049b95ed66bf9149f56218733': unknown revision or path not in the working tree.\nUse '--' to separate paths from revisions, like this:\n'git <command> [<revision>...] -- [<file>...]'\n
                    raise UnknownRevision(
                        ref=stderr.split("fatal: ambiguous argument ")
                        [-1].split("^", 1)[0],
                        cmd=e.cmd,
                        retcode=e.retcode,
                        stdout=e.stdout,
                        stderr=e.stderr,
                    ) from e
                elif "fatal: bad object" in stderr:
                    # bad object 5d953e751835a52472ca2e1906023435a71cb5e4\n
                    raise UnknownRevision(
                        ref=stderr.split("\n")[0].split("bad object ", 1)[-1],
                        cmd=e.cmd,
                        retcode=e.retcode,
                        stdout=e.stdout,
                        stderr=e.stderr,
                    ) from e

                if "Permission denied (publickey)" in stderr:
                    raise InvalidPublicKey(cmd=e.cmd,
                                           retcode=e.retcode,
                                           stdout=e.stdout,
                                           stderr=e.stderr) from e

                raise
Exemple #5
0
    async def log(
        self,
        parent=None,
        branch=None,
        author=None,
        offset=0,
        limit=100,
        timeout=None,
        update_if_exists=False,
    ) -> List[RevisionResult]:
        """ Gets the commit log for the repository.

        Each revision returned includes all the branches with which this commit
        is associated. There will always be at least one associated branch.

        See documentation for the base for general information on this function.
        """
        # TODO(dcramer): we should make this streaming
        cmd = ["log", "--date-order", "--pretty=format:%s" % (LOG_FORMAT, )]

        if author:
            cmd.append("--author=%s" % (author, ))
        if offset:
            cmd.append("--skip=%d" % (offset, ))
        if limit:
            cmd.append("--max-count=%d" % (limit, ))

        if parent and branch:
            raise ValueError("Both parent and branch cannot be set")

        if branch:
            if branch == "!default":
                branch = self.get_default_branch()
            cmd.append(branch)

        # TODO(dcramer): determine correct way to paginate results in git as
        # combining --all with --parent causes issues
        elif not parent:
            cmd.append("--all")
        if parent:
            cmd.append(parent)

        with sentry.Span("vcs.log", description=self.remote_url) as par_span:
            par_span.set_tag("branch", branch)
            par_span.set_tag("parent", parent)
            par_span.set_tag("backend", "git")
            for n in range(2):
                try:
                    await self.ensure(update_if_exists=update_if_exists)
                    result = await self.run(cmd, timeout=timeout)
                    break
                except CommandError as cmd_error:
                    err_msg = cmd_error.stderr
                    if err_msg and branch and branch in err_msg.decode(
                            "utf-8"):
                        # TODO: https://stackoverflow.com/questions/45096755/fatal-ambiguous-argument-origin-unknown-revision-or-path-not-in-the-working
                        default_error = ValueError(
                            'Unable to fetch commit log for branch "{0}".'.
                            format(branch))
                        if not await self.run(["remote"]):
                            # assume we're in a broken state, and try to repair
                            # XXX: theory is this might happen when OOMKiller axes clone?
                            result = await self.run([
                                "symbolic-ref",
                                "refs/remotes/origin/HEAD",
                                "refs/remotes/origin/{}".format(
                                    self.get_default_branch()),
                            ])
                            continue

                        import traceback
                        import logging

                        msg = traceback.format_exception(
                            CommandError, cmd_error, None)
                        logging.warning(msg)
                        raise default_error from cmd_error
                    raise

        # we use a list instead of a generator as we were always
        # needing to coerce to a list anyways
        results = []
        for chunk in BufferParser(result, "\x02"):
            (
                sha,
                author,
                author_date,
                committer,
                committer_date,
                parents,
                message,
            ) = chunk.split("\x01")

            # sha may have a trailing newline due to git log adding it
            sha = sha.lstrip("\n")

            parents: List[str] = [p for p in parents.split(" ") if p]

            author_date = timezone.fromtimestamp(float(author_date))
            committer_date = timezone.fromtimestamp(float(committer_date))

            results.append(
                RevisionResult(
                    sha=sha,
                    author=author,
                    committer=committer,
                    author_date=author_date,
                    committer_date=committer_date,
                    parents=parents,
                    message=message,
                ))
        return results
Exemple #6
0
 async def clone(self):
     with sentry.Span("vcs.clone", description=self.remote_url) as par_span:
         par_span.set_tag("backend", "git")
         await self.run(["clone", "--mirror", self.remote_url, self.path])