OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Gclient-specific SCM-specific operations.""" | 5 """Gclient-specific SCM-specific operations.""" |
6 | 6 |
7 from __future__ import print_function | 7 from __future__ import print_function |
8 | 8 |
9 import collections | |
10 import errno | 9 import errno |
11 import logging | 10 import logging |
12 import os | 11 import os |
13 import posixpath | 12 import posixpath |
14 import re | 13 import re |
15 import shlex | 14 import shlex |
16 import sys | 15 import sys |
17 import tempfile | 16 import tempfile |
18 import traceback | 17 import traceback |
19 import urlparse | 18 import urlparse |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 | 152 |
154 if not command in commands: | 153 if not command in commands: |
155 raise gclient_utils.Error('Unknown command %s' % command) | 154 raise gclient_utils.Error('Unknown command %s' % command) |
156 | 155 |
157 if not command in dir(self): | 156 if not command in dir(self): |
158 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( | 157 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( |
159 command, self.__class__.__name__)) | 158 command, self.__class__.__name__)) |
160 | 159 |
161 return getattr(self, command)(options, args, file_list) | 160 return getattr(self, command)(options, args, file_list) |
162 | 161 |
163 def GetActualRemoteURL(self): | 162 def GetActualRemoteURL(self, options): |
164 """Attempt to determine the remote URL for this SCMWrapper.""" | 163 """Attempt to determine the remote URL for this SCMWrapper.""" |
| 164 # Git |
165 if os.path.exists(os.path.join(self.checkout_path, '.git')): | 165 if os.path.exists(os.path.join(self.checkout_path, '.git')): |
166 actual_remote_url = shlex.split(scm.GIT.Capture( | 166 actual_remote_url = shlex.split(scm.GIT.Capture( |
167 ['config', '--local', '--get-regexp', r'remote.*.url'], | 167 ['config', '--local', '--get-regexp', r'remote.*.url'], |
168 cwd=self.checkout_path))[1] | 168 cwd=self.checkout_path))[1] |
169 | 169 |
170 # If a cache_dir is used, obtain the actual remote URL from the cache. | 170 # If a cache_dir is used, obtain the actual remote URL from the cache. |
171 if getattr(self, 'cache_dir', None): | 171 if getattr(self, 'cache_dir', None): |
172 url, _ = gclient_utils.SplitUrlRevision(self.url) | 172 url, _ = gclient_utils.SplitUrlRevision(self.url) |
173 mirror = git_cache.Mirror(url) | 173 mirror = git_cache.Mirror(url) |
174 if (mirror.exists() and mirror.mirror_path.replace('\\', '/') == | 174 if (mirror.exists() and mirror.mirror_path.replace('\\', '/') == |
175 actual_remote_url.replace('\\', '/')): | 175 actual_remote_url.replace('\\', '/')): |
176 actual_remote_url = shlex.split(scm.GIT.Capture( | 176 actual_remote_url = shlex.split(scm.GIT.Capture( |
177 ['config', '--local', '--get-regexp', r'remote.*.url'], | 177 ['config', '--local', '--get-regexp', r'remote.*.url'], |
178 cwd=mirror.mirror_path))[1] | 178 cwd=mirror.mirror_path))[1] |
179 return actual_remote_url | 179 return actual_remote_url |
180 | 180 |
181 # Svn | 181 # Svn |
182 if os.path.exists(os.path.join(self.checkout_path, '.svn')): | 182 if os.path.exists(os.path.join(self.checkout_path, '.svn')): |
183 return scm.SVN.CaptureLocalInfo([], self.checkout_path)['URL'] | 183 return scm.SVN.CaptureLocalInfo([], self.checkout_path)['URL'] |
184 return None | 184 return None |
185 | 185 |
186 def DoesRemoteURLMatch(self, options): | 186 def DoesRemoteURLMatch(self, options): |
187 """Determine whether the remote URL of this checkout is the expected URL.""" | 187 """Determine whether the remote URL of this checkout is the expected URL.""" |
188 if not os.path.exists(self.checkout_path): | 188 if not os.path.exists(self.checkout_path): |
189 # A checkout which doesn't exist can't be broken. | 189 # A checkout which doesn't exist can't be broken. |
190 return True | 190 return True |
191 | 191 |
192 actual_remote_url = self.GetActualRemoteURL() | 192 actual_remote_url = self.GetActualRemoteURL(options) |
193 if actual_remote_url: | 193 if actual_remote_url: |
194 return (gclient_utils.SplitUrlRevision(actual_remote_url)[0].rstrip('/') | 194 return (gclient_utils.SplitUrlRevision(actual_remote_url)[0].rstrip('/') |
195 == gclient_utils.SplitUrlRevision(self.url)[0].rstrip('/')) | 195 == gclient_utils.SplitUrlRevision(self.url)[0].rstrip('/')) |
196 else: | 196 else: |
197 # This may occur if the self.checkout_path exists but does not contain a | 197 # This may occur if the self.checkout_path exists but does not contain a |
198 # valid git or svn checkout. | 198 # valid git or svn checkout. |
199 return False | 199 return False |
200 | 200 |
201 def _DeleteOrMove(self, force): | 201 def _DeleteOrMove(self, force): |
202 """Delete the checkout directory or move it out of the way. | 202 """Delete the checkout directory or move it out of the way. |
(...skipping 20 matching lines...) Expand all Loading... |
223 dest_path = tempfile.mkdtemp( | 223 dest_path = tempfile.mkdtemp( |
224 prefix=os.path.basename(self.relpath), | 224 prefix=os.path.basename(self.relpath), |
225 dir=bad_scm_dir) | 225 dir=bad_scm_dir) |
226 self.Print('_____ Conflicting directory found in %s. Moving to %s.' | 226 self.Print('_____ Conflicting directory found in %s. Moving to %s.' |
227 % (self.checkout_path, dest_path)) | 227 % (self.checkout_path, dest_path)) |
228 gclient_utils.AddWarning('Conflicting directory %s moved to %s.' | 228 gclient_utils.AddWarning('Conflicting directory %s moved to %s.' |
229 % (self.checkout_path, dest_path)) | 229 % (self.checkout_path, dest_path)) |
230 shutil.move(self.checkout_path, dest_path) | 230 shutil.move(self.checkout_path, dest_path) |
231 | 231 |
232 | 232 |
233 _GitRefishBase = collections.namedtuple('GitRef', ( | |
234 # The initial string that the parsed Refish came from | |
235 'source', | |
236 # Is this ref a branch? | |
237 'is_branch', | |
238 # The name of this ref when accessed through the local repository | |
239 'local_ref', | |
240 # The name of the remote that this refish resides on | |
241 'remote', | |
242 # The path of this refish on the remote (e.g., 'master') | |
243 'remote_ref', | |
244 # The remote-qualified path to this refish (e.g., 'origin/master') | |
245 'remote_refspec', | |
246 # If a branch, the expected name of the upstream branch that it should | |
247 # track; otherwise, 'None' | |
248 'upstream_branch', | |
249 )) | |
250 | |
251 | |
252 class GitRefish(_GitRefishBase): | |
253 # Disable 'no __init__' warning | pylint: disable=W0232 | |
254 """Implements refish parsing and properties. | |
255 | |
256 This class is designed to concentrate and codify the assumptions made by | |
257 'gclient' code about refs and revisions. | |
258 """ | |
259 | |
260 _DEFAULT_REMOTE = 'origin' | |
261 | |
262 _GIT_SHA1_RE = re.compile('[0-9a-f]{40}', re.IGNORECASE) | |
263 _GIT_SHA1_SHORT_RE = re.compile('[0-9a-f]{4,40}', re.IGNORECASE) | |
264 | |
265 def __str__(self): | |
266 return self.source | |
267 | |
268 @classmethod | |
269 def Parse(cls, ref, remote=None, other_remotes=None): | |
270 """Parses a ref into a 'GitRefish' instance. | |
271 | |
272 Args: | |
273 ref: (str) The refish (branch, tag, hash, etc.) to parse | |
274 remote: (None/str) If supplied, the name of the primary remote. In | |
275 addtion to being recognized as a remote string, the primary remote | |
276 will also be used (as needed) when generating the remote refspec. If | |
277 omitted, the default remote name ('origin') will be used. | |
278 other_remotes: (None/iterable) If supplied, a list of strings to | |
279 recognize as remotes in addition to 'remote'. | |
280 """ | |
281 assert ref == ref.strip() | |
282 | |
283 # Use default set of remotes | |
284 remote = remote or cls._DEFAULT_REMOTE | |
285 remotes = {remote} | |
286 if other_remotes: | |
287 remotes.update(other_remotes) | |
288 | |
289 # Treat 'ref' as a '/'-delimited set of items; analyze their contents | |
290 ref_split = local_ref = remote_ref = tuple(ref.split('/')) | |
291 is_branch = True | |
292 if len(ref_split) > 1: | |
293 if ref_split[0] == 'refs': | |
294 if len(ref_split) >= 3 and ref_split[1] == 'heads': | |
295 # refs/heads/foo/bar | |
296 # | |
297 # Local Ref: foo/bar | |
298 # Remote Ref: foo/bar | |
299 local_ref = remote_ref = ref_split[2:] | |
300 elif len(ref_split) >= 4 and ref_split[1] == 'remotes': | |
301 # refs/remotes/<REMOTE>/foo/bar | |
302 # This is a bad assumption, and this logic can be improved, but it | |
303 # is consistent with traditional 'gclient' logic. | |
304 # | |
305 # Local Ref: refs/remotes/<REMOTE>/foo/bar | |
306 # Remote: <REMOTE> | |
307 # Remote Ref: foo/bar | |
308 remote = ref_split[2] | |
309 remote_ref = ref_split[3:] | |
310 elif len(ref_split) >= 2 and ref_split[0] in remotes: | |
311 # origin/master | |
312 # | |
313 # Local Ref: refs/remotes/origin/master | |
314 # Remote Ref: master | |
315 remote = ref_split[0] | |
316 remote_ref = ref_split[1:] | |
317 local_ref = ('refs', 'remotes') + ref_split | |
318 | |
319 # (Default) The refish has multiple paths. Assume at least that it's a | |
320 # branch name. | |
321 # | |
322 # foo/bar | |
323 # refs/foo/bar | |
324 # | |
325 # Local Ref: foo/bar | |
326 # Remote ref: foo/bar | |
327 else: | |
328 # It could be a hash, a short-hash, or a tag | |
329 is_branch = False | |
330 | |
331 # Determine how the ref should be referenced remotely | |
332 if is_branch: | |
333 # foo/bar/baz => origin/foo/bar/baz | |
334 remote_refspec = (remote,) + remote_ref | |
335 else: | |
336 # Refer to the hash/tag directly. This is actually not allowed in | |
337 # 'fetch', although it oftentimes works regardless. | |
338 # | |
339 # <HASH/TAG> => <HASH/TAG> | |
340 remote_refspec = (ref,) | |
341 | |
342 # Calculate the upstream branch | |
343 if is_branch: | |
344 # Convert '/refs/heads/...' to 'refs/remotes/REMOTE/...' | |
345 if ref_split[:2] == ('refs', 'heads'): | |
346 upstream_branch = ('refs', 'remotes', remote) + ref_split[2:] | |
347 else: | |
348 upstream_branch = ref_split | |
349 else: | |
350 upstream_branch = None | |
351 | |
352 def compose(ref_tuple): | |
353 return '/'.join(ref_tuple) if ref_tuple else ref_tuple | |
354 | |
355 return cls( | |
356 source=ref, | |
357 is_branch=is_branch, | |
358 local_ref=compose(local_ref), | |
359 remote=remote, | |
360 remote_ref=compose(remote_ref), | |
361 remote_refspec=compose(remote_refspec), | |
362 upstream_branch=compose(upstream_branch), | |
363 ) | |
364 | |
365 | |
366 class GitWrapper(SCMWrapper): | 233 class GitWrapper(SCMWrapper): |
367 """Wrapper for Git""" | 234 """Wrapper for Git""" |
368 name = 'git' | 235 name = 'git' |
369 remote = 'origin' | 236 remote = 'origin' |
370 | 237 |
371 cache_dir = None | 238 cache_dir = None |
372 | 239 |
373 def __init__(self, url=None, *args): | 240 def __init__(self, url=None, *args): |
374 """Removes 'git+' fake prefix from git URL.""" | 241 """Removes 'git+' fake prefix from git URL.""" |
375 if url.startswith('git+http://') or url.startswith('git+https://'): | 242 if url.startswith('git+http://') or url.startswith('git+https://'): |
376 url = url[4:] | 243 url = url[4:] |
377 SCMWrapper.__init__(self, url, *args) | 244 SCMWrapper.__init__(self, url, *args) |
378 filter_kwargs = { 'time_throttle': 1, 'out_fh': self.out_fh } | 245 filter_kwargs = { 'time_throttle': 1, 'out_fh': self.out_fh } |
379 if self.out_cb: | 246 if self.out_cb: |
380 filter_kwargs['predicate'] = self.out_cb | 247 filter_kwargs['predicate'] = self.out_cb |
381 self.filter = gclient_utils.GitFilter(**filter_kwargs) | 248 self.filter = gclient_utils.GitFilter(**filter_kwargs) |
382 | 249 |
383 @staticmethod | 250 @staticmethod |
384 def BinaryExists(): | 251 def BinaryExists(): |
385 """Returns true if the command exists.""" | 252 """Returns true if the command exists.""" |
386 try: | 253 try: |
387 # We assume git is newer than 1.7. See: crbug.com/114483 | 254 # We assume git is newer than 1.7. See: crbug.com/114483 |
388 result, version = scm.GIT.AssertVersion('1.7') | 255 result, version = scm.GIT.AssertVersion('1.7') |
389 if not result: | 256 if not result: |
390 raise gclient_utils.Error('Git version is older than 1.7: %s' % version) | 257 raise gclient_utils.Error('Git version is older than 1.7: %s' % version) |
391 return result | 258 return result |
392 except OSError: | 259 except OSError: |
393 return False | 260 return False |
394 | 261 |
395 def ParseRefish(self, value, **kwargs): | |
396 kwargs.setdefault('remote', self.remote) | |
397 return GitRefish.Parse(value, **kwargs) | |
398 | |
399 def GetCheckoutRoot(self): | 262 def GetCheckoutRoot(self): |
400 return scm.GIT.GetCheckoutRoot(self.checkout_path) | 263 return scm.GIT.GetCheckoutRoot(self.checkout_path) |
401 | 264 |
402 def GetRevisionDate(self, _revision): | 265 def GetRevisionDate(self, _revision): |
403 """Returns the given revision's date in ISO-8601 format (which contains the | 266 """Returns the given revision's date in ISO-8601 format (which contains the |
404 time zone).""" | 267 time zone).""" |
405 # TODO(floitsch): get the time-stamp of the given revision and not just the | 268 # TODO(floitsch): get the time-stamp of the given revision and not just the |
406 # time-stamp of the currently checked out revision. | 269 # time-stamp of the currently checked out revision. |
407 return self._Capture(['log', '-n', '1', '--format=%ai']) | 270 return self._Capture(['log', '-n', '1', '--format=%ai']) |
408 | 271 |
(...skipping 14 matching lines...) Expand all Loading... |
423 | 286 |
424 The patch file is generated from a diff of the merge base of HEAD and | 287 The patch file is generated from a diff of the merge base of HEAD and |
425 its upstream branch. | 288 its upstream branch. |
426 """ | 289 """ |
427 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) | 290 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) |
428 gclient_utils.CheckCallAndFilter( | 291 gclient_utils.CheckCallAndFilter( |
429 ['git', 'diff', merge_base], | 292 ['git', 'diff', merge_base], |
430 cwd=self.checkout_path, | 293 cwd=self.checkout_path, |
431 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print) | 294 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print) |
432 | 295 |
433 def _FetchAndReset(self, refish, file_list, options): | 296 def _FetchAndReset(self, revision, file_list, options): |
434 """Equivalent to git fetch; git reset.""" | 297 """Equivalent to git fetch; git reset.""" |
435 quiet = [] | 298 quiet = [] |
436 if not options.verbose: | 299 if not options.verbose: |
437 quiet = ['--quiet'] | 300 quiet = ['--quiet'] |
438 self._UpdateBranchHeads(options, fetch=False) | 301 self._UpdateBranchHeads(options, fetch=False) |
439 | 302 |
440 self._Fetch(options, prune=True, quiet=options.verbose) | 303 self._Fetch(options, prune=True, quiet=options.verbose) |
441 self._Run(['reset', '--hard', refish.local_ref] + quiet, options) | 304 self._Run(['reset', '--hard', revision] + quiet, options) |
442 if file_list is not None: | 305 if file_list is not None: |
443 files = self._Capture(['ls-files']).splitlines() | 306 files = self._Capture(['ls-files']).splitlines() |
444 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 307 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
445 | 308 |
446 def _DisableHooks(self): | 309 def _DisableHooks(self): |
447 hook_dir = os.path.join(self.checkout_path, '.git', 'hooks') | 310 hook_dir = os.path.join(self.checkout_path, '.git', 'hooks') |
448 if not os.path.isdir(hook_dir): | 311 if not os.path.isdir(hook_dir): |
449 return | 312 return |
450 for f in os.listdir(hook_dir): | 313 for f in os.listdir(hook_dir): |
451 if not f.endswith('.sample') and not f.endswith('.disabled'): | 314 if not f.endswith('.sample') and not f.endswith('.disabled'): |
452 os.rename(os.path.join(hook_dir, f), | 315 os.rename(os.path.join(hook_dir, f), |
453 os.path.join(hook_dir, f + '.disabled')) | 316 os.path.join(hook_dir, f + '.disabled')) |
454 | 317 |
455 def update(self, options, args, file_list): | 318 def update(self, options, args, file_list): |
456 """Runs git to update or transparently checkout the working copy. | 319 """Runs git to update or transparently checkout the working copy. |
457 | 320 |
458 All updated files will be appended to file_list. | 321 All updated files will be appended to file_list. |
459 | 322 |
460 Raises: | 323 Raises: |
461 Error: if can't get URL for relative path. | 324 Error: if can't get URL for relative path. |
462 """ | 325 """ |
463 if args: | 326 if args: |
464 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) | 327 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) |
465 | 328 |
466 self._CheckMinVersion("1.6.6") | 329 self._CheckMinVersion("1.6.6") |
467 | 330 |
468 # If a dependency is not pinned, track the default remote branch. | 331 # If a dependency is not pinned, track the default remote branch. |
469 default_rev = 'refs/remotes/%s/master' % (self.remote,) | 332 default_rev = 'refs/remotes/%s/master' % self.remote |
470 url, deps_revision = gclient_utils.SplitUrlRevision(self.url) | 333 url, deps_revision = gclient_utils.SplitUrlRevision(self.url) |
471 rev_str = "" | 334 rev_str = "" |
472 revision = deps_revision | 335 revision = deps_revision |
473 managed = True | 336 managed = True |
474 if options.revision: | 337 if options.revision: |
475 # Override the revision number. | 338 # Override the revision number. |
476 revision = str(options.revision) | 339 revision = str(options.revision) |
477 if revision == 'unmanaged': | 340 if revision == 'unmanaged': |
478 # Check again for a revision in case an initial ref was specified | 341 # Check again for a revision in case an initial ref was specified |
479 # in the url, for example bla.git@refs/heads/custombranch | 342 # in the url, for example bla.git@refs/heads/custombranch |
(...skipping 16 matching lines...) Expand all Loading... |
496 rev_str = ' at %s' % revision | 359 rev_str = ' at %s' % revision |
497 files = [] if file_list is not None else None | 360 files = [] if file_list is not None else None |
498 | 361 |
499 printed_path = False | 362 printed_path = False |
500 verbose = [] | 363 verbose = [] |
501 if options.verbose: | 364 if options.verbose: |
502 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) | 365 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
503 verbose = ['--verbose'] | 366 verbose = ['--verbose'] |
504 printed_path = True | 367 printed_path = True |
505 | 368 |
506 refish = self.ParseRefish(revision) | 369 if revision.startswith('refs/'): |
| 370 rev_type = "branch" |
| 371 elif revision.startswith(self.remote + '/'): |
| 372 # Rewrite remote refs to their local equivalents. |
| 373 revision = 'refs/remotes/' + revision |
| 374 rev_type = "branch" |
| 375 else: |
| 376 # hash is also a tag, only make a distinction at checkout |
| 377 rev_type = "hash" |
| 378 |
507 mirror = self._GetMirror(url, options) | 379 mirror = self._GetMirror(url, options) |
508 if mirror: | 380 if mirror: |
509 url = mirror.mirror_path | 381 url = mirror.mirror_path |
510 | 382 |
511 if (not os.path.exists(self.checkout_path) or | 383 if (not os.path.exists(self.checkout_path) or |
512 (os.path.isdir(self.checkout_path) and | 384 (os.path.isdir(self.checkout_path) and |
513 not os.path.exists(os.path.join(self.checkout_path, '.git')))): | 385 not os.path.exists(os.path.join(self.checkout_path, '.git')))): |
514 if mirror: | 386 if mirror: |
515 self._UpdateMirror(mirror, options) | 387 self._UpdateMirror(mirror, options) |
516 try: | 388 try: |
517 self._Clone(refish.local_ref, url, options) | 389 self._Clone(revision, url, options) |
518 except subprocess2.CalledProcessError: | 390 except subprocess2.CalledProcessError: |
519 self._DeleteOrMove(options.force) | 391 self._DeleteOrMove(options.force) |
520 self._Clone(refish.local_ref, url, options) | 392 self._Clone(revision, url, options) |
521 if deps_revision and deps_revision.startswith('branch-heads/'): | 393 if deps_revision and deps_revision.startswith('branch-heads/'): |
522 deps_branch = deps_revision.replace('branch-heads/', '') | 394 deps_branch = deps_revision.replace('branch-heads/', '') |
523 self._Capture(['branch', deps_branch, deps_revision]) | 395 self._Capture(['branch', deps_branch, deps_revision]) |
524 self._Checkout(options, deps_branch, quiet=True) | 396 self._Checkout(options, deps_branch, quiet=True) |
525 if file_list is not None: | 397 if file_list is not None: |
526 files = self._Capture(['ls-files']).splitlines() | 398 files = self._Capture(['ls-files']).splitlines() |
527 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 399 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
528 if not verbose: | 400 if not verbose: |
529 # Make the output a little prettier. It's nice to have some whitespace | 401 # Make the output a little prettier. It's nice to have some whitespace |
530 # between projects when cloning. | 402 # between projects when cloning. |
(...skipping 20 matching lines...) Expand all Loading... |
551 if (current_url.rstrip('/') != url.rstrip('/') and | 423 if (current_url.rstrip('/') != url.rstrip('/') and |
552 url != 'git://foo' and | 424 url != 'git://foo' and |
553 subprocess2.capture( | 425 subprocess2.capture( |
554 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], | 426 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], |
555 cwd=self.checkout_path).strip() != 'False'): | 427 cwd=self.checkout_path).strip() != 'False'): |
556 self.Print('_____ switching %s to a new upstream' % self.relpath) | 428 self.Print('_____ switching %s to a new upstream' % self.relpath) |
557 # Make sure it's clean | 429 # Make sure it's clean |
558 self._CheckClean(rev_str) | 430 self._CheckClean(rev_str) |
559 # Switch over to the new upstream | 431 # Switch over to the new upstream |
560 self._Run(['remote', 'set-url', self.remote, url], options) | 432 self._Run(['remote', 'set-url', self.remote, url], options) |
561 self._FetchAndReset(refish, file_list, options) | 433 self._FetchAndReset(revision, file_list, options) |
562 return_early = True | 434 return_early = True |
563 | 435 |
564 if return_early: | 436 if return_early: |
565 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 437 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
566 | 438 |
567 cur_branch = self._GetCurrentBranch() | 439 cur_branch = self._GetCurrentBranch() |
568 | 440 |
569 # Cases: | 441 # Cases: |
570 # 0) HEAD is detached. Probably from our initial clone. | 442 # 0) HEAD is detached. Probably from our initial clone. |
571 # - make sure HEAD is contained by a named ref, then update. | 443 # - make sure HEAD is contained by a named ref, then update. |
(...skipping 22 matching lines...) Expand all Loading... |
594 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) | 466 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) |
595 if not upstream_branch or not upstream_branch.startswith('refs/remotes'): | 467 if not upstream_branch or not upstream_branch.startswith('refs/remotes'): |
596 current_type = "hash" | 468 current_type = "hash" |
597 logging.debug("Current branch is not tracking an upstream (remote)" | 469 logging.debug("Current branch is not tracking an upstream (remote)" |
598 " branch.") | 470 " branch.") |
599 elif upstream_branch.startswith('refs/remotes'): | 471 elif upstream_branch.startswith('refs/remotes'): |
600 current_type = "branch" | 472 current_type = "branch" |
601 else: | 473 else: |
602 raise gclient_utils.Error('Invalid Upstream: %s' % upstream_branch) | 474 raise gclient_utils.Error('Invalid Upstream: %s' % upstream_branch) |
603 | 475 |
604 if not scm.GIT.IsValidRevision(self.checkout_path, refish.local_ref, | 476 if not scm.GIT.IsValidRevision(self.checkout_path, revision, sha_only=True): |
605 sha_only=True): | |
606 # Update the remotes first so we have all the refs. | 477 # Update the remotes first so we have all the refs. |
607 remote_output = scm.GIT.Capture(['remote'] + verbose + ['update'], | 478 remote_output = scm.GIT.Capture(['remote'] + verbose + ['update'], |
608 cwd=self.checkout_path) | 479 cwd=self.checkout_path) |
609 if verbose: | 480 if verbose: |
610 self.Print(remote_output) | 481 self.Print(remote_output) |
611 | 482 |
612 self._UpdateBranchHeads(options, fetch=True) | 483 self._UpdateBranchHeads(options, fetch=True) |
613 | 484 |
614 # This is a big hammer, debatable if it should even be here... | 485 # This is a big hammer, debatable if it should even be here... |
615 if options.force or options.reset: | 486 if options.force or options.reset: |
616 target = 'HEAD' | 487 target = 'HEAD' |
617 if options.upstream and upstream_branch: | 488 if options.upstream and upstream_branch: |
618 target = upstream_branch | 489 target = upstream_branch |
619 self._Run(['reset', '--hard', target], options) | 490 self._Run(['reset', '--hard', target], options) |
620 | 491 |
621 if current_type == 'detached': | 492 if current_type == 'detached': |
622 # case 0 | 493 # case 0 |
623 self._CheckClean(rev_str) | 494 self._CheckClean(rev_str) |
624 self._CheckDetachedHead(rev_str, options) | 495 self._CheckDetachedHead(rev_str, options) |
625 if self._Capture(['rev-list', '-n', '1', 'HEAD']) == refish.local_ref: | 496 if self._Capture(['rev-list', '-n', '1', 'HEAD']) == revision: |
626 self.Print('Up-to-date; skipping checkout.') | 497 self.Print('Up-to-date; skipping checkout.') |
627 else: | 498 else: |
628 # 'git checkout' may need to overwrite existing untracked files. Allow | 499 # 'git checkout' may need to overwrite existing untracked files. Allow |
629 # it only when nuclear options are enabled. | 500 # it only when nuclear options are enabled. |
630 self._Checkout( | 501 self._Checkout( |
631 options, | 502 options, |
632 revision, | 503 revision, |
633 force=(options.force and options.delete_unversioned_trees), | 504 force=(options.force and options.delete_unversioned_trees), |
634 quiet=True, | 505 quiet=True, |
635 ) | 506 ) |
636 if not printed_path: | 507 if not printed_path: |
637 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) | 508 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
638 elif current_type == 'hash': | 509 elif current_type == 'hash': |
639 # case 1 | 510 # case 1 |
640 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: | 511 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: |
641 # Our git-svn branch (upstream_branch) is our upstream | 512 # Our git-svn branch (upstream_branch) is our upstream |
642 self._AttemptRebase(upstream_branch, files, options, | 513 self._AttemptRebase(upstream_branch, files, options, |
643 newbase=refish.local_ref, | 514 newbase=revision, printed_path=printed_path, |
644 printed_path=printed_path, merge=options.merge) | 515 merge=options.merge) |
645 printed_path = True | 516 printed_path = True |
646 else: | 517 else: |
647 # Can't find a merge-base since we don't know our upstream. That makes | 518 # Can't find a merge-base since we don't know our upstream. That makes |
648 # this command VERY likely to produce a rebase failure. For now we | 519 # this command VERY likely to produce a rebase failure. For now we |
649 # assume origin is our upstream since that's what the old behavior was. | 520 # assume origin is our upstream since that's what the old behavior was. |
650 upstream_branch = self.remote | 521 upstream_branch = self.remote |
651 if options.revision or deps_revision: | 522 if options.revision or deps_revision: |
652 upstream_branch = refish.local_ref | 523 upstream_branch = revision |
653 self._AttemptRebase(upstream_branch, files, options, | 524 self._AttemptRebase(upstream_branch, files, options, |
654 printed_path=printed_path, merge=options.merge) | 525 printed_path=printed_path, merge=options.merge) |
655 printed_path = True | 526 printed_path = True |
656 elif not refish.is_branch: | 527 elif rev_type == 'hash': |
657 # case 2 | 528 # case 2 |
658 self._AttemptRebase(upstream_branch, files, options, | 529 self._AttemptRebase(upstream_branch, files, options, |
659 newbase=refish.local_ref, printed_path=printed_path, | 530 newbase=revision, printed_path=printed_path, |
660 merge=options.merge) | 531 merge=options.merge) |
661 printed_path = True | 532 printed_path = True |
662 elif refish.upstream_branch != upstream_branch: | 533 elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch: |
663 # case 4 | 534 # case 4 |
664 new_base = refish.upstream_branch | 535 new_base = revision.replace('heads', 'remotes/' + self.remote) |
665 if not printed_path: | 536 if not printed_path: |
666 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) | 537 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
667 switch_error = ("Switching upstream branch from %s to %s\n" | 538 switch_error = ("Switching upstream branch from %s to %s\n" |
668 % (upstream_branch, new_base) + | 539 % (upstream_branch, new_base) + |
669 "Please merge or rebase manually:\n" + | 540 "Please merge or rebase manually:\n" + |
670 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + | 541 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + |
671 "OR git checkout -b <some new branch> %s" % new_base) | 542 "OR git checkout -b <some new branch> %s" % new_base) |
672 raise gclient_utils.Error(switch_error) | 543 raise gclient_utils.Error(switch_error) |
673 else: | 544 else: |
674 # case 3 - the default case | 545 # case 3 - the default case |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
785 return self.update(options, [], file_list) | 656 return self.update(options, [], file_list) |
786 | 657 |
787 default_rev = "refs/heads/master" | 658 default_rev = "refs/heads/master" |
788 if options.upstream: | 659 if options.upstream: |
789 if self._GetCurrentBranch(): | 660 if self._GetCurrentBranch(): |
790 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) | 661 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) |
791 default_rev = upstream_branch or default_rev | 662 default_rev = upstream_branch or default_rev |
792 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) | 663 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) |
793 if not deps_revision: | 664 if not deps_revision: |
794 deps_revision = default_rev | 665 deps_revision = default_rev |
795 refish = self.ParseRefish(deps_revision) | 666 if deps_revision.startswith('refs/heads/'): |
796 deps_revision = self.GetUsableRev(refish.remote_refspec, options) | 667 deps_revision = deps_revision.replace('refs/heads/', self.remote + '/') |
| 668 deps_revision = self.GetUsableRev(deps_revision, options) |
797 | 669 |
798 if file_list is not None: | 670 if file_list is not None: |
799 files = self._Capture(['diff', deps_revision, '--name-only']).split() | 671 files = self._Capture(['diff', deps_revision, '--name-only']).split() |
800 | 672 |
801 self._Run(['reset', '--hard', deps_revision], options) | 673 self._Run(['reset', '--hard', deps_revision], options) |
802 self._Run(['clean', '-f', '-d'], options) | 674 self._Run(['clean', '-f', '-d'], options) |
803 | 675 |
804 if file_list is not None: | 676 if file_list is not None: |
805 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 677 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
806 | 678 |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
963 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'), | 835 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'), |
964 os.path.join(self.checkout_path, '.git')) | 836 os.path.join(self.checkout_path, '.git')) |
965 except: | 837 except: |
966 traceback.print_exc(file=self.out_fh) | 838 traceback.print_exc(file=self.out_fh) |
967 raise | 839 raise |
968 finally: | 840 finally: |
969 if os.listdir(tmp_dir): | 841 if os.listdir(tmp_dir): |
970 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir) | 842 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir) |
971 gclient_utils.rmtree(tmp_dir) | 843 gclient_utils.rmtree(tmp_dir) |
972 self._UpdateBranchHeads(options, fetch=True) | 844 self._UpdateBranchHeads(options, fetch=True) |
973 | 845 self._Checkout(options, revision.replace('refs/heads/', ''), quiet=True) |
974 refish = self.ParseRefish(revision) | |
975 self._Checkout(options, refish.local_ref, quiet=True) | |
976 if self._GetCurrentBranch() is None: | 846 if self._GetCurrentBranch() is None: |
977 # Squelch git's very verbose detached HEAD warning and use our own | 847 # Squelch git's very verbose detached HEAD warning and use our own |
978 self.Print( | 848 self.Print( |
979 "Checked out %s to a detached HEAD. Before making any commits\n" | 849 ('Checked out %s to a detached HEAD. Before making any commits\n' |
980 "in this repo, you should use 'git checkout <branch>' to switch to\n" | 850 'in this repo, you should use \'git checkout <branch>\' to switch to\n' |
981 "an existing branch or use 'git checkout %s -b <branch>' to\n" | 851 'an existing branch or use \'git checkout %s -b <branch>\' to\n' |
982 "create a new branch for your work." % (revision, self.remote)) | 852 'create a new branch for your work.') % (revision, self.remote)) |
983 | 853 |
984 def _AskForData(self, prompt, options): | 854 def _AskForData(self, prompt, options): |
985 if options.jobs > 1: | 855 if options.jobs > 1: |
986 self.Print(prompt) | 856 self.Print(prompt) |
987 raise gclient_utils.Error("Background task requires input. Rerun " | 857 raise gclient_utils.Error("Background task requires input. Rerun " |
988 "gclient with --jobs=1 so that\n" | 858 "gclient with --jobs=1 so that\n" |
989 "interaction is possible.") | 859 "interaction is possible.") |
990 try: | 860 try: |
991 return raw_input(prompt) | 861 return raw_input(prompt) |
992 except KeyboardInterrupt: | 862 except KeyboardInterrupt: |
(...skipping 676 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1669 new_command.append('--force') | 1539 new_command.append('--force') |
1670 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1540 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1671 new_command.extend(('--accept', 'theirs-conflict')) | 1541 new_command.extend(('--accept', 'theirs-conflict')) |
1672 elif options.manually_grab_svn_rev: | 1542 elif options.manually_grab_svn_rev: |
1673 new_command.append('--force') | 1543 new_command.append('--force') |
1674 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1544 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1675 new_command.extend(('--accept', 'postpone')) | 1545 new_command.extend(('--accept', 'postpone')) |
1676 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1546 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1677 new_command.extend(('--accept', 'postpone')) | 1547 new_command.extend(('--accept', 'postpone')) |
1678 return new_command | 1548 return new_command |
OLD | NEW |