git.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. # vim: set fileencoding=utf-8 :
  2. #
  3. # (C) 2011,2014 Guido Günther <agx@sigxcpu.org>
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, please see
  16. # <http://www.gnu.org/licenses/>
  17. """A Git Repository that keeps a Debian Package"""
  18. import re
  19. from gbp.git import GitRepository, GitRepositoryError
  20. from gbp.deb.pristinetar import DebianPristineTar
  21. from gbp.format import format_str
  22. class DebianGitRepository(GitRepository):
  23. """A git repository that holds the source of a Debian package"""
  24. def __init__(self, path):
  25. super(DebianGitRepository, self).__init__(path)
  26. self.pristine_tar = DebianPristineTar(self)
  27. def find_version(self, format, version):
  28. """
  29. Check if a certain version is stored in this repo and return the SHA1
  30. of the related commit. That is, an annotated tag is dereferenced to the
  31. commit object it points to.
  32. For legacy tags don't only check the tag itself but also the commit
  33. message, since the former wasn't injective until release 0.5.5. You
  34. only need to use this function if you also need to check for legacy
  35. tags.
  36. @param format: tag pattern
  37. @type format: C{str}
  38. @param version: debian version number
  39. @type version: C{str}
  40. @return: sha1 of the commit the tag references to
  41. @rtype: C{str}
  42. """
  43. tag = self.version_to_tag(format, version)
  44. legacy_tag = self._build_legacy_tag(format, version)
  45. if self.has_tag(tag): # new tags are injective
  46. # dereference to a commit object
  47. return self.rev_parse("%s^0" % tag)
  48. elif self.has_tag(legacy_tag):
  49. out, ret = self._git_getoutput('cat-file', args=['-p', legacy_tag])
  50. if ret:
  51. return None
  52. for line in out:
  53. if line.endswith(" %s\n" % version):
  54. # dereference to a commit object
  55. return self.rev_parse("%s^0" % legacy_tag)
  56. elif line.startswith('---'): # GPG signature start
  57. return None
  58. return None
  59. def debian_version_from_upstream(self, upstream_tag_format,
  60. upstream_branch, commit='HEAD',
  61. epoch=None, debian_release=True):
  62. """
  63. Build the Debian version that a package based on upstream commit
  64. I{commit} would carry taking into account a possible epoch.
  65. @param upstream_tag_format: the tag format on the upstream branch
  66. @type upstream_tag_format: C{str}
  67. @param upstream_branch: the upstream branch
  68. @type upstream_branch: C{str}
  69. @param commit: the commit to search for the latest upstream version
  70. @param epoch: an epoch to use
  71. @param debian_release: If set to C{False} don't append a Debian release
  72. number to the version number
  73. @returns: a new debian version
  74. @raises GitRepositoryError: if no upstream tag was found
  75. """
  76. pattern = upstream_tag_format % dict(version='*')
  77. tag = self.find_branch_tag(commit, upstream_branch, pattern=pattern)
  78. version = self.tag_to_version(tag, upstream_tag_format)
  79. if debian_release:
  80. version += "-1"
  81. if epoch:
  82. version = "%s:%s" % (epoch, version)
  83. return version
  84. @staticmethod
  85. def _build_legacy_tag(format, version):
  86. """
  87. Legacy tags (prior to 0.5.5) dropped epochs and didn't honor the '~'
  88. >>> DebianGitRepository._build_legacy_tag('upstream/%(version)s', '1:2.0~3')
  89. 'upstream/2.0.3'
  90. """
  91. if ':' in version: # strip of any epochs
  92. version = version.split(':', 1)[1]
  93. version = version.replace('~', '.')
  94. return format % dict(version=version)
  95. @staticmethod
  96. def version_to_tag(format, version):
  97. """Generate a tag from a given format and a version
  98. %(version)s provides a clean version that works as a git tag.
  99. %(hversion)s provides the same thing, but with '.' replaced with '-'.
  100. hversion is useful for upstreams with tagging policies that prohibit .
  101. characters.
  102. >>> DebianGitRepository.version_to_tag("debian/%(version)s", "0:0~0")
  103. 'debian/0%0_0'
  104. >>> DebianGitRepository.version_to_tag("libfoo-%(hversion)s", "1.8.1")
  105. 'libfoo-1-8-1'
  106. """
  107. return format_str(format, dict(version=DebianGitRepository._sanitize_version(version),
  108. hversion=DebianGitRepository._sanitize_version(version).replace('.','-')))
  109. @staticmethod
  110. def _sanitize_version(version):
  111. """sanitize a version so git accepts it as a tag
  112. >>> DebianGitRepository._sanitize_version("0.0.0")
  113. '0.0.0'
  114. >>> DebianGitRepository._sanitize_version("0.0~0")
  115. '0.0_0'
  116. >>> DebianGitRepository._sanitize_version("0:0.0")
  117. '0%0.0'
  118. >>> DebianGitRepository._sanitize_version("0%0~0")
  119. '0%0_0'
  120. """
  121. return version.replace('~', '_').replace(':', '%')
  122. @staticmethod
  123. def tag_to_version(tag, format):
  124. """Extract the version from a tag
  125. >>> DebianGitRepository.tag_to_version("upstream/1%2_3-4", "upstream/%(version)s")
  126. '1:2~3-4'
  127. >>> DebianGitRepository.tag_to_version("foo/2.3.4", "foo/%(version)s")
  128. '2.3.4'
  129. >>> DebianGitRepository.tag_to_version("foo/2.3.4", "upstream/%(version)s")
  130. """
  131. version_re = format.replace('%(version)s',
  132. '(?P<version>[\w_%+-.]+)')
  133. r = re.match(version_re, tag)
  134. if r:
  135. version = r.group('version').replace('_', '~').replace('%', ':')
  136. return version
  137. return None
  138. @property
  139. def pristine_tar_branch(self):
  140. """
  141. The name of the pristine-tar branch, whether it already exists or
  142. not.
  143. """
  144. return DebianPristineTar.branch
  145. def has_pristine_tar_branch(self):
  146. """
  147. Wheter the repo has a I{pristine-tar} branch.
  148. @return: C{True} if the repo has pristine-tar commits already, C{False}
  149. otherwise
  150. @rtype: C{Bool}
  151. """
  152. return True if self.has_branch(self.pristine_tar_branch) else False
  153. # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: