OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2013 The Native Client Authors. All rights reserved. | 2 # Copyright (c) 2013 The Native Client Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import logging | 6 import logging |
7 import os | 7 import os |
| 8 import posixpath |
8 import subprocess | 9 import subprocess |
9 import sys | 10 import sys |
10 import urlparse | 11 import urlparse |
11 | 12 |
12 import file_tools | 13 import file_tools |
13 import log_tools | 14 import log_tools |
14 import platform | 15 import platform |
15 | 16 |
| 17 GIT_ALTERNATES_PATH = os.path.join('.git', 'objects', 'info', 'alternates') |
| 18 |
| 19 |
16 class InvalidRepoException(Exception): | 20 class InvalidRepoException(Exception): |
17 def __init__(self, expected_repo, msg, *args): | 21 def __init__(self, expected_repo, msg, *args): |
18 Exception.__init__(self, msg % args) | 22 Exception.__init__(self, msg % args) |
19 self.expected_repo = expected_repo | 23 self.expected_repo = expected_repo |
20 | 24 |
21 | 25 |
22 def GitCmd(): | 26 def GitCmd(): |
23 """Return the git command to execute for the host platform.""" | 27 """Return the git command to execute for the host platform.""" |
24 if platform.IsWindows(): | 28 if platform.IsWindows(): |
25 # On windows, we want to use the depot_tools version of git, which has | 29 # On windows, we want to use the depot_tools version of git, which has |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
104 branches. | 108 branches. |
105 git_cache: If set, assumes URL has been populated within the git cache | 109 git_cache: If set, assumes URL has been populated within the git cache |
106 directory specified and sets the fetch URL to be from the | 110 directory specified and sets the fetch URL to be from the |
107 git_cache. | 111 git_cache. |
108 """ | 112 """ |
109 if reclone: | 113 if reclone: |
110 logging.debug('Clobbering source directory %s' % destination) | 114 logging.debug('Clobbering source directory %s' % destination) |
111 file_tools.RemoveDirectoryIfPresent(destination) | 115 file_tools.RemoveDirectoryIfPresent(destination) |
112 | 116 |
113 if git_cache: | 117 if git_cache: |
114 fetch_url = GetGitCacheURL(git_cache, url) | 118 git_cache_url = GetGitCacheURL(git_cache, url) |
115 else: | 119 else: |
116 fetch_url = url | 120 git_cache_url = None |
117 | 121 |
118 # If the destination is a git repository, validate the tracked origin. | 122 # If the destination is a git repository, validate the tracked origin. |
119 git_dir = os.path.join(destination, '.git') | 123 git_dir = os.path.join(destination, '.git') |
120 if os.path.exists(git_dir): | 124 if os.path.exists(git_dir): |
121 if not IsURLInRemoteRepoList(fetch_url, destination, include_fetch=True, | 125 if not IsURLInRemoteRepoList(url, destination, include_fetch=True, |
122 include_push=False): | 126 include_push=False): |
123 # If the original URL is being tracked instead of the fetch URL, we | 127 # If the git cache URL is being tracked instead of the fetch URL, we |
124 # can safely redirect it to the fetch URL instead. | 128 # can safely redirect it to the fetch URL instead. |
125 if (fetch_url != url and IsURLInRemoteRepoList(url, destination, | 129 if git_cache_url and IsURLInRemoteRepoList(git_cache_url, destination, |
126 include_fetch=True, | 130 include_fetch=True, |
127 include_push=False)): | 131 include_push=False): |
128 GitSetRemoteRepo(fetch_url, destination, push_url=push_url) | 132 GitSetRemoteRepo(url, destination, push_url=push_url) |
129 else: | 133 else: |
130 logging.error('Git Repo (%s) does not track URL: %s', | 134 logging.error('Git Repo (%s) does not track URL: %s', |
131 destination, fetch_url) | 135 destination, url) |
132 raise InvalidRepoException(fetch_url, 'Could not sync git repo: %s', | 136 raise InvalidRepoException(url, 'Could not sync git repo: %s', |
133 destination) | 137 destination) |
134 | 138 |
| 139 # Make sure the push URL is set correctly as well. |
| 140 if not IsURLInRemoteRepoList(push_url, destination, include_fetch=False, |
| 141 include_push=True): |
| 142 GitSetRemoteRepo(url, destination, push_url=push_url) |
| 143 |
135 git = GitCmd() | 144 git = GitCmd() |
136 if not os.path.exists(git_dir): | 145 if not os.path.exists(git_dir): |
137 logging.info('Cloning %s...' % url) | 146 logging.info('Cloning %s...' % url) |
138 clone_args = ['clone', '-n'] | |
139 if git_cache: | |
140 clone_args.append('-s') | |
141 | 147 |
142 file_tools.MakeDirectoryIfAbsent(destination) | 148 file_tools.MakeDirectoryIfAbsent(destination) |
143 log_tools.CheckCall(git + clone_args + [fetch_url, '.'], cwd=destination) | 149 clone_args = ['clone', '-n'] |
| 150 if git_cache_url: |
| 151 clone_args.extend(['--reference', git_cache_url]) |
144 | 152 |
145 if fetch_url != url: | 153 log_tools.CheckCall(git + clone_args + [url, '.'], cwd=destination) |
146 GitSetRemoteRepo(fetch_url, destination, push_url=push_url) | |
147 | 154 |
| 155 if url != push_url: |
| 156 GitSetRemoteRepo(url, destination, push_url=push_url) |
148 elif clean: | 157 elif clean: |
149 log_tools.CheckCall(git + ['clean', '-dffx'], cwd=destination) | 158 log_tools.CheckCall(git + ['clean', '-dffx'], cwd=destination) |
150 log_tools.CheckCall(git + ['reset', '--hard', 'HEAD'], cwd=destination) | 159 log_tools.CheckCall(git + ['reset', '--hard', 'HEAD'], cwd=destination) |
151 | 160 |
| 161 # If a git cache URL is supplied, make sure it is setup as a git alternate. |
| 162 if git_cache_url: |
| 163 git_alternates = [git_cache_url] |
| 164 else: |
| 165 git_alternates = [] |
| 166 |
| 167 GitSetRepoAlternates(destination, git_alternates, append=False) |
| 168 |
152 if revision is not None: | 169 if revision is not None: |
153 logging.info('Checking out pinned revision...') | 170 logging.info('Checking out pinned revision...') |
154 log_tools.CheckCall(git + ['fetch', '--all'], cwd=destination) | 171 log_tools.CheckCall(git + ['fetch', '--all'], cwd=destination) |
155 checkout_flags = ['-f'] if clean else [] | 172 checkout_flags = ['-f'] if clean else [] |
156 path = [pathspec] if pathspec else [] | 173 path = [pathspec] if pathspec else [] |
157 log_tools.CheckCall( | 174 log_tools.CheckCall( |
158 git + ['checkout'] + checkout_flags + [revision] + path, | 175 git + ['checkout'] + checkout_flags + [revision] + path, |
159 cwd=destination) | 176 cwd=destination) |
160 | 177 |
161 | 178 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
206 cygwin_path = False | 223 cygwin_path = False |
207 if platform.IsCygWin() and cache_dir.startswith('/cygdrive/'): | 224 if platform.IsCygWin() and cache_dir.startswith('/cygdrive/'): |
208 cygwin_path = True | 225 cygwin_path = True |
209 drive, file_path = cache_dir[len('/cygdrive/'):].split('/', 1) | 226 drive, file_path = cache_dir[len('/cygdrive/'):].split('/', 1) |
210 cache_dir = drive + ':\\' + file_path.replace('/', '\\') | 227 cache_dir = drive + ':\\' + file_path.replace('/', '\\') |
211 | 228 |
212 git_url = log_tools.CheckOutput(GitCmd() + ['cache', 'exists', | 229 git_url = log_tools.CheckOutput(GitCmd() + ['cache', 'exists', |
213 '-c', cache_dir, | 230 '-c', cache_dir, |
214 url]).strip() | 231 url]).strip() |
215 | 232 |
216 # For cygwin paths, convert forward slashes to backslashes to mimic URLs. | 233 # For windows, make sure the git cache URL is a posix path. |
217 if cygwin_path: | 234 if platform.IsWindows(): |
218 git_url = git_url.replace('\\', '/') | 235 git_url = git_url.replace('\\', '/') |
219 return git_url | 236 return git_url |
220 | 237 |
221 | 238 |
222 def GitRevInfo(directory): | 239 def GitRevInfo(directory): |
223 """Get the git revision information of a git checkout. | 240 """Get the git revision information of a git checkout. |
224 | 241 |
225 Args: | 242 Args: |
226 directory: Existing git working directory. | 243 directory: Existing git working directory. |
227 """ | 244 """ |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
328 valid_urls = (url, GetAuthenticatedGitURL(url)) | 345 valid_urls = (url, GetAuthenticatedGitURL(url)) |
329 else: | 346 else: |
330 valid_urls = (url,) | 347 valid_urls = (url,) |
331 | 348 |
332 remote_repo_list = GitRemoteRepoList(directory, | 349 remote_repo_list = GitRemoteRepoList(directory, |
333 include_fetch=include_fetch, | 350 include_fetch=include_fetch, |
334 include_push=include_push) | 351 include_push=include_push) |
335 return len([repo_name for | 352 return len([repo_name for |
336 repo_name, repo_url in remote_repo_list | 353 repo_name, repo_url in remote_repo_list |
337 if repo_url in valid_urls]) > 0 | 354 if repo_url in valid_urls]) > 0 |
| 355 |
| 356 |
| 357 def GitGetRepoAlternates(directory): |
| 358 """Gets the list of git alternates for a local git repo. |
| 359 |
| 360 Args: |
| 361 directory: Local git repository to get the git alternate for. |
| 362 |
| 363 Returns: |
| 364 List of git alternates set for the local git repository. |
| 365 """ |
| 366 git_alternates_file = os.path.join(directory, GIT_ALTERNATES_PATH) |
| 367 if os.path.isfile(git_alternates_file): |
| 368 with open(git_alternates_file, 'rt') as f: |
| 369 alternates_list = [] |
| 370 for line in f.readlines(): |
| 371 line = line.strip() |
| 372 if line: |
| 373 if posixpath.basename(line) == 'objects': |
| 374 line = posixpath.dirname(line) |
| 375 alternates_list.append(line) |
| 376 |
| 377 return alternates_list |
| 378 |
| 379 return [] |
| 380 |
| 381 |
| 382 def GitSetRepoAlternates(directory, alternates_list, append=True): |
| 383 """Sets the list of git alternates for a local git repo. |
| 384 |
| 385 Args: |
| 386 directory: Local git repository. |
| 387 alternates_list: List of local git repositories for the git alternates. |
| 388 append: If True, will append the list to currently set list of alternates. |
| 389 """ |
| 390 git_alternates_file = os.path.join(directory, GIT_ALTERNATES_PATH) |
| 391 git_alternates_dir = os.path.dirname(git_alternates_file) |
| 392 if not os.path.isdir(git_alternates_dir): |
| 393 raise InvalidRepoException(directory, |
| 394 'Invalid local git repo: %s', directory) |
| 395 |
| 396 original_alternates_list = GitGetRepoAlternates(directory) |
| 397 if append: |
| 398 alternates_list.extend(original_alternates_list) |
| 399 alternates_list = sorted(set(alternates_list)) |
| 400 |
| 401 if set(original_alternates_list) != set(alternates_list): |
| 402 lines = [posixpath.join(line, 'objects') + '\n' for line in alternates_list] |
| 403 logging.info('Setting git alternates:\n\t%s', '\t'.join(lines)) |
| 404 |
| 405 with open(git_alternates_file, 'wb') as f: |
| 406 f.writelines(lines) |
OLD | NEW |