_git.py 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. """Fetch information about any current git repo."""
  2. import functools
  3. import logging
  4. import subprocess
  5. from typing import List, Optional, TypeVar
  6. from typing_extensions import TypedDict
  7. logger = logging.getLogger(__name__)
  8. T = TypeVar("T")
  9. def exec_git(command: List[str]) -> Optional[str]:
  10. try:
  11. return subprocess.check_output(
  12. ["git"] + command, encoding="utf-8", stderr=subprocess.DEVNULL
  13. ).strip()
  14. except BaseException:
  15. return None
  16. class GitInfo(TypedDict, total=False):
  17. repo_name: Optional[str]
  18. remote_url: Optional[str]
  19. commit: Optional[str]
  20. branch: Optional[str]
  21. author_name: Optional[str]
  22. author_email: Optional[str]
  23. commit_time: Optional[str]
  24. dirty: Optional[bool]
  25. tags: Optional[str]
  26. @functools.lru_cache(maxsize=1)
  27. def get_git_info(remote: str = "origin") -> GitInfo:
  28. """Get information about the git repository."""
  29. if not exec_git(["rev-parse", "--is-inside-work-tree"]):
  30. return GitInfo(
  31. remote_url=None,
  32. commit=None,
  33. branch=None,
  34. author_name=None,
  35. author_email=None,
  36. commit_time=None,
  37. dirty=None,
  38. tags=None,
  39. repo_name=None,
  40. )
  41. return {
  42. "remote_url": exec_git(["remote", "get-url", remote]),
  43. "commit": exec_git(["rev-parse", "HEAD"]),
  44. "commit_time": exec_git(["log", "-1", "--format=%ct"]),
  45. "branch": exec_git(["rev-parse", "--abbrev-ref", "HEAD"]),
  46. "tags": exec_git(
  47. ["describe", "--tags", "--exact-match", "--always", "--dirty"]
  48. ),
  49. "dirty": exec_git(["status", "--porcelain"]) != "",
  50. "author_name": exec_git(["log", "-1", "--format=%an"]),
  51. "author_email": exec_git(["log", "-1", "--format=%ae"]),
  52. "repo_name": (exec_git(["rev-parse", "--show-toplevel"]) or "").split("/")[-1],
  53. }