PageRenderTime 29ms CodeModel.GetById 10ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

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