/bangkokhotel/lib/python2.5/site-packages/pip-1.2.1-py2.5.egg/pip/vcs/git.py

https://bitbucket.org/luisrodriguez/bangkokhotel · Python · 221 lines · 182 code · 21 blank · 18 comment · 31 complexity · ff9a8a150d5c3cd3517c7ce7b6f10125 MD5 · raw file

  1. import tempfile
  2. import re
  3. import os.path
  4. from pip.util import call_subprocess
  5. from pip.util import display_path, rmtree
  6. from pip.vcs import vcs, VersionControl
  7. from pip.log import logger
  8. from pip.backwardcompat import url2pathname, urlparse
  9. urlsplit = urlparse.urlsplit
  10. urlunsplit = urlparse.urlunsplit
  11. class Git(VersionControl):
  12. name = 'git'
  13. dirname = '.git'
  14. repo_name = 'clone'
  15. schemes = ('git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file')
  16. bundle_file = 'git-clone.txt'
  17. guide = ('# This was a Git repo; to make it a repo again run:\n'
  18. 'git init\ngit remote add origin %(url)s -f\ngit checkout %(rev)s\n')
  19. def __init__(self, url=None, *args, **kwargs):
  20. # Works around an apparent Git bug
  21. # (see http://article.gmane.org/gmane.comp.version-control.git/146500)
  22. if url:
  23. scheme, netloc, path, query, fragment = urlsplit(url)
  24. if scheme.endswith('file'):
  25. initial_slashes = path[:-len(path.lstrip('/'))]
  26. newpath = initial_slashes + url2pathname(path).replace('\\', '/').lstrip('/')
  27. url = urlunsplit((scheme, netloc, newpath, query, fragment))
  28. after_plus = scheme.find('+') + 1
  29. url = scheme[:after_plus] + urlunsplit((scheme[after_plus:], netloc, newpath, query, fragment))
  30. super(Git, self).__init__(url, *args, **kwargs)
  31. def parse_vcs_bundle_file(self, content):
  32. url = rev = None
  33. for line in content.splitlines():
  34. if not line.strip() or line.strip().startswith('#'):
  35. continue
  36. url_match = re.search(r'git\s*remote\s*add\s*origin(.*)\s*-f', line)
  37. if url_match:
  38. url = url_match.group(1).strip()
  39. rev_match = re.search(r'^git\s*checkout\s*-q\s*(.*)\s*', line)
  40. if rev_match:
  41. rev = rev_match.group(1).strip()
  42. if url and rev:
  43. return url, rev
  44. return None, None
  45. def export(self, location):
  46. """Export the Git repository at the url to the destination location"""
  47. temp_dir = tempfile.mkdtemp('-export', 'pip-')
  48. self.unpack(temp_dir)
  49. try:
  50. if not location.endswith('/'):
  51. location = location + '/'
  52. call_subprocess(
  53. [self.cmd, 'checkout-index', '-a', '-f', '--prefix', location],
  54. filter_stdout=self._filter, show_stdout=False, cwd=temp_dir)
  55. finally:
  56. rmtree(temp_dir)
  57. def check_rev_options(self, rev, dest, rev_options):
  58. """Check the revision options before checkout to compensate that tags
  59. and branches may need origin/ as a prefix.
  60. Returns the SHA1 of the branch or tag if found.
  61. """
  62. revisions = self.get_tag_revs(dest)
  63. revisions.update(self.get_branch_revs(dest))
  64. origin_rev = 'origin/%s' % rev
  65. if origin_rev in revisions:
  66. # remote branch
  67. return [revisions[origin_rev]]
  68. elif rev in revisions:
  69. # a local tag or branch name
  70. return [revisions[rev]]
  71. else:
  72. logger.warn("Could not find a tag or branch '%s', assuming commit." % rev)
  73. return rev_options
  74. def switch(self, dest, url, rev_options):
  75. call_subprocess(
  76. [self.cmd, 'config', 'remote.origin.url', url], cwd=dest)
  77. call_subprocess(
  78. [self.cmd, 'checkout', '-q'] + rev_options, cwd=dest)
  79. self.update_submodules(dest)
  80. def update(self, dest, rev_options):
  81. # First fetch changes from the default remote
  82. call_subprocess([self.cmd, 'fetch', '-q'], cwd=dest)
  83. # Then reset to wanted revision (maby even origin/master)
  84. if rev_options:
  85. rev_options = self.check_rev_options(rev_options[0], dest, rev_options)
  86. call_subprocess([self.cmd, 'reset', '--hard', '-q'] + rev_options, cwd=dest)
  87. #: update submodules
  88. self.update_submodules(dest)
  89. def obtain(self, dest):
  90. url, rev = self.get_url_rev()
  91. if rev:
  92. rev_options = [rev]
  93. rev_display = ' (to %s)' % rev
  94. else:
  95. rev_options = ['origin/master']
  96. rev_display = ''
  97. if self.check_destination(dest, url, rev_options, rev_display):
  98. logger.notify('Cloning %s%s to %s' % (url, rev_display, display_path(dest)))
  99. call_subprocess([self.cmd, 'clone', '-q', url, dest])
  100. #: repo may contain submodules
  101. self.update_submodules(dest)
  102. if rev:
  103. rev_options = self.check_rev_options(rev, dest, rev_options)
  104. # Only do a checkout if rev_options differs from HEAD
  105. if not self.get_revision(dest).startswith(rev_options[0]):
  106. call_subprocess([self.cmd, 'checkout', '-q'] + rev_options, cwd=dest)
  107. def get_url(self, location):
  108. url = call_subprocess(
  109. [self.cmd, 'config', 'remote.origin.url'],
  110. show_stdout=False, cwd=location)
  111. return url.strip()
  112. def get_revision(self, location):
  113. current_rev = call_subprocess(
  114. [self.cmd, 'rev-parse', 'HEAD'], show_stdout=False, cwd=location)
  115. return current_rev.strip()
  116. def get_tag_revs(self, location):
  117. tags = self._get_all_tag_names(location)
  118. tag_revs = {}
  119. for line in tags.splitlines():
  120. tag = line.strip()
  121. rev = self._get_revision_from_rev_parse(tag, location)
  122. tag_revs[tag] = rev.strip()
  123. return tag_revs
  124. def get_branch_revs(self, location):
  125. branches = self._get_all_branch_names(location)
  126. branch_revs = {}
  127. for line in branches.splitlines():
  128. if '(no branch)' in line:
  129. continue
  130. line = line.split('->')[0].strip()
  131. # actual branch case
  132. branch = "".join(b for b in line.split() if b != '*')
  133. rev = self._get_revision_from_rev_parse(branch, location)
  134. branch_revs[branch] = rev.strip()
  135. return branch_revs
  136. def get_src_requirement(self, dist, location, find_tags):
  137. repo = self.get_url(location)
  138. if not repo.lower().startswith('git:'):
  139. repo = 'git+' + repo
  140. egg_project_name = dist.egg_name().split('-', 1)[0]
  141. if not repo:
  142. return None
  143. current_rev = self.get_revision(location)
  144. tag_revs = self.get_tag_revs(location)
  145. branch_revs = self.get_branch_revs(location)
  146. if current_rev in tag_revs:
  147. # It's a tag
  148. full_egg_name = '%s-%s' % (egg_project_name, tag_revs[current_rev])
  149. elif (current_rev in branch_revs and
  150. branch_revs[current_rev] != 'origin/master'):
  151. # It's the head of a branch
  152. full_egg_name = '%s-%s' % (
  153. egg_project_name,
  154. branch_revs[current_rev].replace('origin/', '')
  155. )
  156. else:
  157. full_egg_name = '%s-dev' % egg_project_name
  158. return '%s@%s#egg=%s' % (repo, current_rev, full_egg_name)
  159. def get_url_rev(self):
  160. """
  161. Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'.
  162. That's required because although they use SSH they sometimes doesn't
  163. work with a ssh:// scheme (e.g. Github). But we need a scheme for
  164. parsing. Hence we remove it again afterwards and return it as a stub.
  165. """
  166. if not '://' in self.url:
  167. assert not 'file:' in self.url
  168. self.url = self.url.replace('git+', 'git+ssh://')
  169. url, rev = super(Git, self).get_url_rev()
  170. url = url.replace('ssh://', '')
  171. else:
  172. url, rev = super(Git, self).get_url_rev()
  173. return url, rev
  174. def _get_all_tag_names(self, location):
  175. return call_subprocess([self.cmd, 'tag', '-l'],
  176. show_stdout=False,
  177. raise_on_returncode=False,
  178. cwd=location)
  179. def _get_all_branch_names(self, location):
  180. remote_branches = call_subprocess([self.cmd, 'branch', '-r'],
  181. show_stdout=False, cwd=location)
  182. local_branches = call_subprocess([self.cmd, 'branch', '-l'],
  183. show_stdout=False, cwd=location)
  184. return remote_branches + local_branches
  185. def _get_revision_from_rev_parse(self, name, location):
  186. return call_subprocess([self.cmd, 'rev-parse', name],
  187. show_stdout=False, cwd=location)
  188. def update_submodules(self, location):
  189. if not os.path.exists(os.path.join(location, '.gitmodules')):
  190. return
  191. call_subprocess([self.cmd, 'submodule', 'init', '-q'], cwd=location)
  192. call_subprocess([self.cmd, 'submodule', 'update', '--recursive', '-q'],
  193. cwd=location)
  194. vcs.register(Git)