Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2016 The LUCI Authors. All rights reserved. | 1 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import base64 | 5 import base64 |
| 6 import httplib | 6 import httplib |
| 7 import json | 7 import json |
| 8 import logging | 8 import logging |
| 9 import os | 9 import os |
| 10 import re | |
| 10 import shutil | 11 import shutil |
| 11 import sys | 12 import sys |
| 12 import tarfile | 13 import tarfile |
| 13 import tempfile | 14 import tempfile |
| 14 | 15 |
| 15 # Add third party paths. | 16 # Add third party paths. |
| 16 from . import env | 17 from . import env |
| 17 from . import requests_ssl | 18 from . import requests_ssl |
| 18 from . import util | 19 from . import util |
| 19 from .requests_ssl import requests | 20 from .requests_ssl import requests |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 112 subcommand = (args[0]) if args else ('') | 113 subcommand = (args[0]) if args else ('') |
| 113 is_remote = subcommand in self._REMOTE_SUBCOMMANDS | 114 is_remote = subcommand in self._REMOTE_SUBCOMMANDS |
| 114 raise GitError(is_remote, 'Git "%s" failed: %s' % ( | 115 raise GitError(is_remote, 'Git "%s" failed: %s' % ( |
| 115 subcommand, e.message,)) | 116 subcommand, e.message,)) |
| 116 | 117 |
| 117 def _execute(self, *args): | 118 def _execute(self, *args): |
| 118 """Runs a raw command. Separate so it's easily mockable.""" | 119 """Runs a raw command. Separate so it's easily mockable.""" |
| 119 logging.info('Running: %s', args) | 120 logging.info('Running: %s', args) |
| 120 return subprocess42.check_output(args) | 121 return subprocess42.check_output(args) |
| 121 | 122 |
| 122 | |
| 123 @property | 123 @property |
| 124 def repo_type(self): | 124 def repo_type(self): |
| 125 return package_pb2.DepSpec.GIT | 125 return package_pb2.DepSpec.GIT |
| 126 | 126 |
| 127 @staticmethod | 127 @staticmethod |
| 128 def branch_spec(branch): | 128 def branch_spec(branch): |
| 129 return 'origin/%s' % branch | 129 return 'origin/%s' % branch |
| 130 | 130 |
| 131 @util.exponential_retry(condition=GitError.is_remote_error) | 131 @util.exponential_retry(condition=GitError.is_remote_error) |
| 132 def checkout(self, repo, revision, checkout_dir, allow_fetch): | 132 def checkout(self, repo_url, revision, checkout_dir, allow_fetch): |
| 133 logging.info('Freshening repository %s in %s', repo, checkout_dir) | 133 logging.info('Freshening repository %s in %s', repo_url, checkout_dir) |
| 134 | 134 |
| 135 git = self.Git() | 135 if revision.startswith('origin/'): |
| 136 if not os.path.isdir(checkout_dir): | 136 # Some clients pass "origin/master" when they actually mean "master". |
| 137 if not allow_fetch: | 137 # Strip "origin/" |
| 138 raise FetchNotAllowedError( | 138 # TODO(nodir): raise an exception http://crbug.com/696704 |
|
iannucci
2017/02/27 20:42:03
Let's use syntax `BUG(http://crbug.com/696704): ..
nodir
2017/02/27 21:23:31
Done
| |
| 139 'need to clone %s but fetch not allowed' % repo) | 139 revision = revision[len('origin/'):] |
| 140 git('clone', '-q', repo, checkout_dir) | 140 |
| 141 elif not os.path.isdir(os.path.join(checkout_dir, '.git')): | 141 is_commit = re.match('^[a-z0-9]{40}$', revision) |
| 142 raise UncleanFilesystemError( | |
| 143 '%s exists but is not a git repo' % checkout_dir) | |
| 144 | 142 |
| 145 git = self.Git(checkout_dir=checkout_dir) | 143 git = self.Git(checkout_dir=checkout_dir) |
| 146 git('config', 'remote.origin.url', repo) | 144 if not os.path.isdir(checkout_dir): |
| 147 try: | 145 os.makedirs(checkout_dir) |
| 148 git('rev-parse', '-q', '--verify', '%s^{commit}' % revision) | 146 else: |
| 149 except GitError as e: | 147 if not os.path.isdir(os.path.join(checkout_dir, '.git')): |
| 150 logging.warning('Revision %s is not available: %s', revision, e) | 148 raise UncleanFilesystemError( |
| 149 '%s exists but is not a git repo' % checkout_dir) | |
| 151 | 150 |
| 152 # Revision does not exist. If we can't fetch, then we fail here. | 151 if is_commit: |
| 153 if not allow_fetch: | 152 # Avoid doing sever roundtrips if we already have the commit. |
| 154 raise FetchNotAllowedError( | 153 # Note: rev-parse is only safe if revision is a commit. If the revision |
| 155 'need to fetch %s but fetch not allowed' % repo) | 154 # is a ref, they may differ locally and remotely. |
| 156 git('fetch') | 155 try: |
| 156 git('rev-parse', '-q', '--verify', '%s^{commit}' % revision) | |
| 157 except GitError as e: | |
| 158 logging.warning('Revision %s is not available: %s', revision, e) | |
|
iannucci
2017/02/27 20:42:03
Revision %s needs to be fetched: %s
So that way t
nodir
2017/02/27 21:23:31
done
| |
| 159 else: | |
| 160 git('checkout', '-q', '-f', revision) | |
| 161 return | |
| 157 | 162 |
| 158 git('reset', '-q', '--hard', revision) | 163 if not allow_fetch: |
| 164 # If revision is a commit hash, it is not present in the existing repo, so | |
| 165 # we have to fetch. | |
| 166 # If revision is a ref, what we have locally may be stale, so we have to | |
| 167 # fetch. | |
| 168 # We have to fetch anyway. | |
| 169 raise FetchNotAllowedError( | |
| 170 'need to fetch %s but fetch not allowed' % repo_url) | |
| 171 | |
| 172 git('init') # Safe to call on an existing git repo. | |
| 173 | |
| 174 # Typically we cannot fetch a commit, so we assume that remote master branch | |
| 175 # contains the requested commit. We will checkout the commit afterwards. | |
| 176 # Do not name the git remote to minimize repo state and to avoid making it | |
| 177 # work with invalid value of "origin/master". | |
| 178 # Do not map the fetched commit to anything to avoid checking out possibly | |
| 179 # stale refs. | |
| 180 git('fetch', repo_url, 'refs/heads/master' if is_commit else revision) | |
| 181 | |
| 182 # FETCH_HEAD points to the fetched commit. | |
| 183 | |
| 184 git('checkout', '-q', '-f', revision if is_commit else 'FETCH_HEAD') | |
| 159 | 185 |
| 160 @util.exponential_retry(condition=GitError.is_remote_error) | 186 @util.exponential_retry(condition=GitError.is_remote_error) |
| 161 def updates(self, repo, revision, checkout_dir, allow_fetch, | 187 def updates(self, repo, revision, checkout_dir, allow_fetch, |
| 162 other_revision, paths): | 188 other_revision, paths): |
| 163 self.checkout(repo, revision, checkout_dir, allow_fetch) | 189 self.checkout(repo, revision, checkout_dir, allow_fetch) |
| 164 | 190 |
| 165 git = self.Git(checkout_dir=checkout_dir) | 191 git = self.Git(checkout_dir=checkout_dir) |
| 166 if allow_fetch: | 192 if allow_fetch: |
| 167 git('fetch') | 193 git('fetch') |
| 168 | 194 |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 331 logging.info('fetching %s', url) | 357 logging.info('fetching %s', url) |
| 332 | 358 |
| 333 resp = requests.get(url) | 359 resp = requests.get(url) |
| 334 if resp.status_code != httplib.OK: | 360 if resp.status_code != httplib.OK: |
| 335 raise GitilesFetchError(resp.status_code, resp.text) | 361 raise GitilesFetchError(resp.status_code, resp.text) |
| 336 | 362 |
| 337 if not resp.text.startswith(cls._GERRIT_XSRF_HEADER): | 363 if not resp.text.startswith(cls._GERRIT_XSRF_HEADER): |
| 338 raise GitilesFetchError(resp.status_code, 'Missing XSRF header') | 364 raise GitilesFetchError(resp.status_code, 'Missing XSRF header') |
| 339 | 365 |
| 340 return json.loads(resp.text[len(cls._GERRIT_XSRF_HEADER):]) | 366 return json.loads(resp.text[len(cls._GERRIT_XSRF_HEADER):]) |
| OLD | NEW |