gitlib.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. # Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug
  2. # Copyright: (c) <spug.dev@gmail.com>
  3. # Released under the AGPL-3.0 License.
  4. from git import Repo, RemoteReference, TagReference, InvalidGitRepositoryError, GitCommandError
  5. from tempfile import NamedTemporaryFile
  6. from datetime import datetime
  7. import shutil
  8. import os
  9. class Git:
  10. def __init__(self, git_repo, repo_dir, pkey=None):
  11. self.git_repo = git_repo
  12. self.repo_dir = repo_dir
  13. self.repo = None
  14. self.pkey = pkey
  15. self.fd = None
  16. self.env = {}
  17. def archive(self, filepath, commit):
  18. with open(filepath, 'wb') as f:
  19. self.repo.archive(f, commit)
  20. def fetch_branches_tags(self):
  21. self.fetch()
  22. branches, tags = {}, {}
  23. for ref in self.repo.references:
  24. if isinstance(ref, RemoteReference):
  25. if ref.remote_head != 'HEAD':
  26. branches[ref.remote_head] = self._get_commits(f'origin/{ref.remote_head}', 30)
  27. elif isinstance(ref, TagReference):
  28. tags[ref.name] = {
  29. 'id': ref.tag.hexsha,
  30. 'author': ref.tag.tagger.name,
  31. 'date': self._format_date(ref.tag.tagged_date),
  32. 'message': ref.tag.message.strip()
  33. } if ref.tag else {
  34. 'id': ref.commit.binsha.hex(),
  35. 'author': ref.commit.author.name,
  36. 'date': self._format_date(ref.commit.authored_date),
  37. 'message': ref.commit.message.strip()
  38. }
  39. tags = sorted(tags.items(), key=lambda x: x[1]['date'], reverse=True)
  40. return branches, dict(tags)
  41. def fetch(self):
  42. try:
  43. self.repo.remotes.origin.fetch(f=True, p=True, P=True)
  44. except GitCommandError as e:
  45. if self.env:
  46. self.repo.remotes.origin.fetch(env=self.env, f=True, p=True, P=True)
  47. else:
  48. raise e
  49. def _get_repo(self):
  50. if os.path.exists(self.repo_dir):
  51. try:
  52. return Repo(self.repo_dir)
  53. except InvalidGitRepositoryError:
  54. if os.path.isdir(self.repo_dir):
  55. shutil.rmtree(self.repo_dir)
  56. else:
  57. os.remove(self.repo_dir)
  58. try:
  59. repo = Repo.clone_from(self.git_repo, self.repo_dir)
  60. except GitCommandError as e:
  61. if self.env:
  62. repo = Repo.clone_from(self.git_repo, self.repo_dir, env=self.env)
  63. else:
  64. raise e
  65. return repo
  66. def _get_commits(self, branch, count=10):
  67. commits = []
  68. for commit in self.repo.iter_commits(branch):
  69. if len(commits) == count:
  70. break
  71. commits.append({
  72. 'id': commit.hexsha,
  73. 'author': commit.author.name,
  74. 'date': self._format_date(commit.committed_date),
  75. 'message': commit.message.strip()
  76. })
  77. return commits
  78. def _format_date(self, timestamp):
  79. if isinstance(timestamp, int):
  80. date = datetime.fromtimestamp(timestamp)
  81. return date.strftime('%Y-%m-%d %H:%M')
  82. return timestamp
  83. def __enter__(self):
  84. if self.pkey:
  85. self.fd = NamedTemporaryFile()
  86. self.fd.write(self.pkey.encode())
  87. self.fd.flush()
  88. self.env = {'GIT_SSH_COMMAND': f'ssh -i {self.fd.name}'}
  89. self.repo = self._get_repo()
  90. return self
  91. def __exit__(self, exc_type, exc_val, exc_tb):
  92. if self.fd:
  93. self.fd.close()