git_wrapper.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. # -*- coding: utf-8 -*-
  2. """
  3. Wrap python git interface for compatibility with older/newer version
  4. """
  5. import logging
  6. import os
  7. from time import mktime, altzone
  8. from datetime import datetime
  9. from pelican.utils import set_date_tzinfo
  10. from git import Git, Repo
  11. DEV_LOGGER = logging.getLogger(__name__)
  12. class _GitWrapperCommon(object):
  13. '''
  14. Wrap git module to provide a more stable interface across versions
  15. '''
  16. def __init__(self, repo_path):
  17. self.git = Git()
  18. self.repo = Repo(os.path.abspath('.'))
  19. def is_file_managed_by_git(self, path):
  20. '''
  21. :param path: Path to check
  22. :returns: True if path is managed by git
  23. '''
  24. status, _stdout, _stderr = self.git.execute(
  25. ['git', 'ls-files', path, '--error-unmatch'],
  26. with_extended_output=True,
  27. with_exceptions=False)
  28. return status == 0
  29. def is_file_modified(self, path):
  30. '''
  31. Does a file have local changes not yet committed
  32. :returns: True if file has local changes
  33. '''
  34. status, _stdout, _stderr = self.git.execute(
  35. ['git', 'diff', '--quiet', 'HEAD', path],
  36. with_extended_output=True,
  37. with_exceptions=False)
  38. return status != 0
  39. def get_commits_following(self, path):
  40. '''
  41. Get all commits including path following the file through
  42. renames
  43. :param path: Path which we will find commits for
  44. :returns: Sequence of commit objects. Newest to oldest
  45. '''
  46. commit_shas = self.git.log(
  47. '--pretty=%H', '--follow', '--', path).splitlines()
  48. return map(self.repo.commit, commit_shas)
  49. def get_commits(self, path, follow=False):
  50. '''
  51. Get all commits including path
  52. :param path: Path which we will find commits for
  53. :param bool follow: If True we will follow path through renames
  54. :returns: Sequence of commit objects. Newest to oldest
  55. '''
  56. if follow:
  57. return self.get_commits_following(path)
  58. else:
  59. return self._get_commits(path)
  60. class _GitWrapperLegacy(_GitWrapperCommon):
  61. def _get_commits(self, path):
  62. '''
  63. Get all commits including path without following renames
  64. :param path: Path which we will find commits for
  65. :returns: Sequence of commit objects. Newest to oldest
  66. '''
  67. return self.repo.commits(path=path)
  68. @staticmethod
  69. def get_commit_date(commit, tz_name):
  70. '''
  71. Get datetime of commit comitted_date
  72. '''
  73. return set_date_tzinfo(
  74. datetime.fromtimestamp(mktime(commit.committed_date) - altzone),
  75. tz_name=tz_name)
  76. class _GitWrapper(_GitWrapperCommon):
  77. def _get_commits(self, path):
  78. '''
  79. Get all commits including path without following renames
  80. :param path: Path which we will find commits for
  81. :returns: Sequence of commit objects. Newest to oldest
  82. .. NOTE ::
  83. If this fails it could be that your gitpython version is out of sync with the git
  84. binary on your distro. Make sure you use the correct gitpython version.
  85. Alternatively enabling GIT_FILETIME_FOLLOW may also make your problem go away.
  86. '''
  87. return list(self.repo.iter_commits(paths=path))
  88. @staticmethod
  89. def get_commit_date(commit, tz_name):
  90. '''
  91. Get datetime of commit comitted_date
  92. '''
  93. return set_date_tzinfo(
  94. datetime.fromtimestamp(commit.committed_date),
  95. tz_name=tz_name)
  96. _wrapper_cache = {}
  97. def git_wrapper(path):
  98. '''
  99. Get appropriate wrapper factory and cache instance for path
  100. '''
  101. path = os.path.abspath(path)
  102. if path not in _wrapper_cache:
  103. if hasattr(Repo, 'commits'):
  104. _wrapper_cache[path] = _GitWrapperLegacy(path)
  105. else:
  106. _wrapper_cache[path] = _GitWrapper(path)
  107. return _wrapper_cache[path]