text: import contextlib
  import subprocess
  import tempfile
  from typing import Generator
  from typing import List
  from typing import NamedTuple
  from typing import Optional
  
  from git_code_debt.repo_parser import Commit
  from git_code_debt.repo_parser import RepoParser
  from git_code_debt.util.iter import chunk_iter
  from git_code_debt.util.subprocess import cmd_output
  from git_code_debt.util.subprocess import cmd_output_b
  
  
  COMMIT_FORMAT = '--format=%H%n%ct'
  
  
  class GitRepoParser(RepoParser):
  
      def __init__(self, git_repo: str) -> None:
          self.git_repo = git_repo
          self.tempdir: Optional[str] = None
  
      @contextlib.contextmanager
      def repo_checked_out(self) -> Generator[None, None, None]:
          assert not self.tempdir
          with tempfile.TemporaryDirectory(suffix='temp-repo') as self.tempdir:
              try:
                  subprocess.check_call((
                      'git', 'clone',
                      '--no-checkout', '--quiet', '--shared',
                      self.git_repo, self.tempdir,
                  ))
                  yield
              finally:
                  self.tempdir = None
  
      def get_commit(self, sha: str) -> Commit:
          output = cmd_output(
              'git', 'show', COMMIT_FORMAT, sha, cwd=self.tempdir,
          )
          sha, date = output.splitlines()[:2]
  
          return Commit(sha, int(date))
  
      def get_commits(self, since_sha: Optional[str] = None) -> List[Commit]:
          """Returns a list of Commit objects.
  
          Args:
             since_sha - (optional) A sha to search from
          """
          assert self.tempdir
  
          cmd = ['git', 'log', '--first-parent', '--reverse', COMMIT_FORMAT]
          if since_sha:
              commits = [self.get_commit(since_sha)]
              cmd.append(f'{since_sha}..HEAD')
          else:
              commits = []
              cmd.append('HEAD')
  
          output = cmd_output(*cmd, cwd=self.tempdir)
  
          for sha, date in chunk_iter(output.splitlines(), 2):
              commits.append(Commit(sha, int(date)))
  
          return commits
  
      def get_original_commit(self, sha: str) -> bytes:
          assert self.tempdir
          return cmd_output_b('git', 'show', sha, cwd=self.tempdir)
  
      def get_commit_diff(self, previous_sha: str, sha: str) -> bytes:
          assert self.tempdir
          return cmd_output_b(
              'git', 'diff', previous_sha, sha, '--no-renames',
              cwd=self.tempdir,
          )
  