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 |
9 import errno | 10 import errno |
10 import logging | 11 import logging |
11 import os | 12 import os |
12 import posixpath | 13 import posixpath |
13 import re | 14 import re |
14 import shlex | 15 import shlex |
15 import sys | 16 import sys |
16 import tempfile | 17 import tempfile |
17 import traceback | 18 import traceback |
18 import urlparse | 19 import urlparse |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
152 | 153 |
153 if not command in commands: | 154 if not command in commands: |
154 raise gclient_utils.Error('Unknown command %s' % command) | 155 raise gclient_utils.Error('Unknown command %s' % command) |
155 | 156 |
156 if not command in dir(self): | 157 if not command in dir(self): |
157 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( | 158 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( |
158 command, self.__class__.__name__)) | 159 command, self.__class__.__name__)) |
159 | 160 |
160 return getattr(self, command)(options, args, file_list) | 161 return getattr(self, command)(options, args, file_list) |
161 | 162 |
162 def GetActualRemoteURL(self, options): | 163 def GetActualRemoteURL(self): |
163 """Attempt to determine the remote URL for this SCMWrapper.""" | 164 """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(options) | 192 actual_remote_url = self.GetActualRemoteURL() |
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 |
233 class GitWrapper(SCMWrapper): | 366 class GitWrapper(SCMWrapper): |
234 """Wrapper for Git""" | 367 """Wrapper for Git""" |
235 name = 'git' | 368 name = 'git' |
236 remote = 'origin' | 369 remote = 'origin' |
237 | 370 |
238 cache_dir = None | 371 cache_dir = None |
239 | 372 |
240 def __init__(self, url=None, *args): | 373 def __init__(self, url=None, *args): |
241 """Removes 'git+' fake prefix from git URL.""" | 374 """Removes 'git+' fake prefix from git URL.""" |
242 if url.startswith('git+http://') or url.startswith('git+https://'): | 375 if url.startswith('git+http://') or url.startswith('git+https://'): |
243 url = url[4:] | 376 url = url[4:] |
244 SCMWrapper.__init__(self, url, *args) | 377 SCMWrapper.__init__(self, url, *args) |
245 filter_kwargs = { 'time_throttle': 1, 'out_fh': self.out_fh } | 378 filter_kwargs = { 'time_throttle': 1, 'out_fh': self.out_fh } |
246 if self.out_cb: | 379 if self.out_cb: |
247 filter_kwargs['predicate'] = self.out_cb | 380 filter_kwargs['predicate'] = self.out_cb |
248 self.filter = gclient_utils.GitFilter(**filter_kwargs) | 381 self.filter = gclient_utils.GitFilter(**filter_kwargs) |
249 | 382 |
250 @staticmethod | 383 @staticmethod |
251 def BinaryExists(): | 384 def BinaryExists(): |
252 """Returns true if the command exists.""" | 385 """Returns true if the command exists.""" |
253 try: | 386 try: |
254 # We assume git is newer than 1.7. See: crbug.com/114483 | 387 # We assume git is newer than 1.7. See: crbug.com/114483 |
255 result, version = scm.GIT.AssertVersion('1.7') | 388 result, version = scm.GIT.AssertVersion('1.7') |
256 if not result: | 389 if not result: |
257 raise gclient_utils.Error('Git version is older than 1.7: %s' % version) | 390 raise gclient_utils.Error('Git version is older than 1.7: %s' % version) |
258 return result | 391 return result |
259 except OSError: | 392 except OSError: |
260 return False | 393 return False |
261 | 394 |
| 395 def ParseRefish(self, value, **kwargs): |
| 396 kwargs.setdefault('remote', self.remote) |
| 397 return GitRefish.Parse(value, **kwargs) |
| 398 |
262 def GetCheckoutRoot(self): | 399 def GetCheckoutRoot(self): |
263 return scm.GIT.GetCheckoutRoot(self.checkout_path) | 400 return scm.GIT.GetCheckoutRoot(self.checkout_path) |
264 | 401 |
265 def GetRevisionDate(self, _revision): | 402 def GetRevisionDate(self, _revision): |
266 """Returns the given revision's date in ISO-8601 format (which contains the | 403 """Returns the given revision's date in ISO-8601 format (which contains the |
267 time zone).""" | 404 time zone).""" |
268 # TODO(floitsch): get the time-stamp of the given revision and not just the | 405 # TODO(floitsch): get the time-stamp of the given revision and not just the |
269 # time-stamp of the currently checked out revision. | 406 # time-stamp of the currently checked out revision. |
270 return self._Capture(['log', '-n', '1', '--format=%ai']) | 407 return self._Capture(['log', '-n', '1', '--format=%ai']) |
271 | 408 |
(...skipping 14 matching lines...) Expand all Loading... |
286 | 423 |
287 The patch file is generated from a diff of the merge base of HEAD and | 424 The patch file is generated from a diff of the merge base of HEAD and |
288 its upstream branch. | 425 its upstream branch. |
289 """ | 426 """ |
290 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) | 427 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) |
291 gclient_utils.CheckCallAndFilter( | 428 gclient_utils.CheckCallAndFilter( |
292 ['git', 'diff', merge_base], | 429 ['git', 'diff', merge_base], |
293 cwd=self.checkout_path, | 430 cwd=self.checkout_path, |
294 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print) | 431 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print) |
295 | 432 |
296 def _FetchAndReset(self, revision, file_list, options): | 433 def _FetchAndReset(self, refish, file_list, options): |
297 """Equivalent to git fetch; git reset.""" | 434 """Equivalent to git fetch; git reset.""" |
298 quiet = [] | 435 quiet = [] |
299 if not options.verbose: | 436 if not options.verbose: |
300 quiet = ['--quiet'] | 437 quiet = ['--quiet'] |
301 self._UpdateBranchHeads(options, fetch=False) | 438 self._UpdateBranchHeads(options, fetch=False) |
302 | 439 |
303 self._Fetch(options, prune=True, quiet=options.verbose) | 440 self._Fetch(options, prune=True, quiet=options.verbose) |
304 self._Run(['reset', '--hard', revision] + quiet, options) | 441 self._Run(['reset', '--hard', refish.local_ref] + quiet, options) |
305 if file_list is not None: | 442 if file_list is not None: |
306 files = self._Capture(['ls-files']).splitlines() | 443 files = self._Capture(['ls-files']).splitlines() |
307 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 444 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
308 | 445 |
309 def _DisableHooks(self): | 446 def _DisableHooks(self): |
310 hook_dir = os.path.join(self.checkout_path, '.git', 'hooks') | 447 hook_dir = os.path.join(self.checkout_path, '.git', 'hooks') |
311 if not os.path.isdir(hook_dir): | 448 if not os.path.isdir(hook_dir): |
312 return | 449 return |
313 for f in os.listdir(hook_dir): | 450 for f in os.listdir(hook_dir): |
314 if not f.endswith('.sample') and not f.endswith('.disabled'): | 451 if not f.endswith('.sample') and not f.endswith('.disabled'): |
315 os.rename(os.path.join(hook_dir, f), | 452 os.rename(os.path.join(hook_dir, f), |
316 os.path.join(hook_dir, f + '.disabled')) | 453 os.path.join(hook_dir, f + '.disabled')) |
317 | 454 |
318 def update(self, options, args, file_list): | 455 def update(self, options, args, file_list): |
319 """Runs git to update or transparently checkout the working copy. | 456 """Runs git to update or transparently checkout the working copy. |
320 | 457 |
321 All updated files will be appended to file_list. | 458 All updated files will be appended to file_list. |
322 | 459 |
323 Raises: | 460 Raises: |
324 Error: if can't get URL for relative path. | 461 Error: if can't get URL for relative path. |
325 """ | 462 """ |
326 if args: | 463 if args: |
327 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) | 464 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) |
328 | 465 |
329 self._CheckMinVersion("1.6.6") | 466 self._CheckMinVersion("1.6.6") |
330 | 467 |
331 # If a dependency is not pinned, track the default remote branch. | 468 # If a dependency is not pinned, track the default remote branch. |
332 default_rev = 'refs/remotes/%s/master' % self.remote | 469 default_rev = 'refs/remotes/%s/master' % (self.remote,) |
333 url, deps_revision = gclient_utils.SplitUrlRevision(self.url) | 470 url, deps_revision = gclient_utils.SplitUrlRevision(self.url) |
334 rev_str = "" | 471 rev_str = "" |
335 revision = deps_revision | 472 revision = deps_revision |
336 managed = True | 473 managed = True |
337 if options.revision: | 474 if options.revision: |
338 # Override the revision number. | 475 # Override the revision number. |
339 revision = str(options.revision) | 476 revision = str(options.revision) |
340 if revision == 'unmanaged': | 477 if revision == 'unmanaged': |
341 # Check again for a revision in case an initial ref was specified | 478 # Check again for a revision in case an initial ref was specified |
342 # in the url, for example bla.git@refs/heads/custombranch | 479 # in the url, for example bla.git@refs/heads/custombranch |
(...skipping 16 matching lines...) Expand all Loading... |
359 rev_str = ' at %s' % revision | 496 rev_str = ' at %s' % revision |
360 files = [] if file_list is not None else None | 497 files = [] if file_list is not None else None |
361 | 498 |
362 printed_path = False | 499 printed_path = False |
363 verbose = [] | 500 verbose = [] |
364 if options.verbose: | 501 if options.verbose: |
365 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) | 502 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
366 verbose = ['--verbose'] | 503 verbose = ['--verbose'] |
367 printed_path = True | 504 printed_path = True |
368 | 505 |
369 if revision.startswith('refs/'): | 506 refish = self.ParseRefish(revision) |
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 | |
379 mirror = self._GetMirror(url, options) | 507 mirror = self._GetMirror(url, options) |
380 if mirror: | 508 if mirror: |
381 url = mirror.mirror_path | 509 url = mirror.mirror_path |
382 | 510 |
383 if (not os.path.exists(self.checkout_path) or | 511 if (not os.path.exists(self.checkout_path) or |
384 (os.path.isdir(self.checkout_path) and | 512 (os.path.isdir(self.checkout_path) and |
385 not os.path.exists(os.path.join(self.checkout_path, '.git')))): | 513 not os.path.exists(os.path.join(self.checkout_path, '.git')))): |
386 if mirror: | 514 if mirror: |
387 self._UpdateMirror(mirror, options) | 515 self._UpdateMirror(mirror, options) |
388 try: | 516 try: |
389 self._Clone(revision, url, options) | 517 self._Clone(refish.local_ref, url, options) |
390 except subprocess2.CalledProcessError: | 518 except subprocess2.CalledProcessError: |
391 self._DeleteOrMove(options.force) | 519 self._DeleteOrMove(options.force) |
392 self._Clone(revision, url, options) | 520 self._Clone(refish.local_ref, url, options) |
393 if deps_revision and deps_revision.startswith('branch-heads/'): | 521 if deps_revision and deps_revision.startswith('branch-heads/'): |
394 deps_branch = deps_revision.replace('branch-heads/', '') | 522 deps_branch = deps_revision.replace('branch-heads/', '') |
395 self._Capture(['branch', deps_branch, deps_revision]) | 523 self._Capture(['branch', deps_branch, deps_revision]) |
396 self._Checkout(options, deps_branch, quiet=True) | 524 self._Checkout(options, deps_branch, quiet=True) |
397 if file_list is not None: | 525 if file_list is not None: |
398 files = self._Capture(['ls-files']).splitlines() | 526 files = self._Capture(['ls-files']).splitlines() |
399 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 527 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
400 if not verbose: | 528 if not verbose: |
401 # Make the output a little prettier. It's nice to have some whitespace | 529 # Make the output a little prettier. It's nice to have some whitespace |
402 # between projects when cloning. | 530 # between projects when cloning. |
(...skipping 20 matching lines...) Expand all Loading... |
423 if (current_url.rstrip('/') != url.rstrip('/') and | 551 if (current_url.rstrip('/') != url.rstrip('/') and |
424 url != 'git://foo' and | 552 url != 'git://foo' and |
425 subprocess2.capture( | 553 subprocess2.capture( |
426 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], | 554 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], |
427 cwd=self.checkout_path).strip() != 'False'): | 555 cwd=self.checkout_path).strip() != 'False'): |
428 self.Print('_____ switching %s to a new upstream' % self.relpath) | 556 self.Print('_____ switching %s to a new upstream' % self.relpath) |
429 # Make sure it's clean | 557 # Make sure it's clean |
430 self._CheckClean(rev_str) | 558 self._CheckClean(rev_str) |
431 # Switch over to the new upstream | 559 # Switch over to the new upstream |
432 self._Run(['remote', 'set-url', self.remote, url], options) | 560 self._Run(['remote', 'set-url', self.remote, url], options) |
433 self._FetchAndReset(revision, file_list, options) | 561 self._FetchAndReset(refish, file_list, options) |
434 return_early = True | 562 return_early = True |
435 | 563 |
436 if return_early: | 564 if return_early: |
437 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 565 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
438 | 566 |
439 cur_branch = self._GetCurrentBranch() | 567 cur_branch = self._GetCurrentBranch() |
440 | 568 |
441 # Cases: | 569 # Cases: |
442 # 0) HEAD is detached. Probably from our initial clone. | 570 # 0) HEAD is detached. Probably from our initial clone. |
443 # - make sure HEAD is contained by a named ref, then update. | 571 # - make sure HEAD is contained by a named ref, then update. |
(...skipping 22 matching lines...) Expand all Loading... |
466 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) | 594 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) |
467 if not upstream_branch or not upstream_branch.startswith('refs/remotes'): | 595 if not upstream_branch or not upstream_branch.startswith('refs/remotes'): |
468 current_type = "hash" | 596 current_type = "hash" |
469 logging.debug("Current branch is not tracking an upstream (remote)" | 597 logging.debug("Current branch is not tracking an upstream (remote)" |
470 " branch.") | 598 " branch.") |
471 elif upstream_branch.startswith('refs/remotes'): | 599 elif upstream_branch.startswith('refs/remotes'): |
472 current_type = "branch" | 600 current_type = "branch" |
473 else: | 601 else: |
474 raise gclient_utils.Error('Invalid Upstream: %s' % upstream_branch) | 602 raise gclient_utils.Error('Invalid Upstream: %s' % upstream_branch) |
475 | 603 |
476 if not scm.GIT.IsValidRevision(self.checkout_path, revision, sha_only=True): | 604 if not scm.GIT.IsValidRevision(self.checkout_path, refish.local_ref, |
| 605 sha_only=True): |
477 # Update the remotes first so we have all the refs. | 606 # Update the remotes first so we have all the refs. |
478 remote_output = scm.GIT.Capture(['remote'] + verbose + ['update'], | 607 remote_output = scm.GIT.Capture(['remote'] + verbose + ['update'], |
479 cwd=self.checkout_path) | 608 cwd=self.checkout_path) |
480 if verbose: | 609 if verbose: |
481 self.Print(remote_output) | 610 self.Print(remote_output) |
482 | 611 |
483 self._UpdateBranchHeads(options, fetch=True) | 612 self._UpdateBranchHeads(options, fetch=True) |
484 | 613 |
485 # This is a big hammer, debatable if it should even be here... | 614 # This is a big hammer, debatable if it should even be here... |
486 if options.force or options.reset: | 615 if options.force or options.reset: |
487 target = 'HEAD' | 616 target = 'HEAD' |
488 if options.upstream and upstream_branch: | 617 if options.upstream and upstream_branch: |
489 target = upstream_branch | 618 target = upstream_branch |
490 self._Run(['reset', '--hard', target], options) | 619 self._Run(['reset', '--hard', target], options) |
491 | 620 |
492 if current_type == 'detached': | 621 if current_type == 'detached': |
493 # case 0 | 622 # case 0 |
494 self._CheckClean(rev_str) | 623 self._CheckClean(rev_str) |
495 self._CheckDetachedHead(rev_str, options) | 624 self._CheckDetachedHead(rev_str, options) |
496 if self._Capture(['rev-list', '-n', '1', 'HEAD']) == revision: | 625 if self._Capture(['rev-list', '-n', '1', 'HEAD']) == refish.local_ref: |
497 self.Print('Up-to-date; skipping checkout.') | 626 self.Print('Up-to-date; skipping checkout.') |
498 else: | 627 else: |
499 # 'git checkout' may need to overwrite existing untracked files. Allow | 628 # 'git checkout' may need to overwrite existing untracked files. Allow |
500 # it only when nuclear options are enabled. | 629 # it only when nuclear options are enabled. |
501 self._Checkout( | 630 self._Checkout( |
502 options, | 631 options, |
503 revision, | 632 revision, |
504 force=(options.force and options.delete_unversioned_trees), | 633 force=(options.force and options.delete_unversioned_trees), |
505 quiet=True, | 634 quiet=True, |
506 ) | 635 ) |
507 if not printed_path: | 636 if not printed_path: |
508 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) | 637 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
509 elif current_type == 'hash': | 638 elif current_type == 'hash': |
510 # case 1 | 639 # case 1 |
511 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: | 640 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: |
512 # Our git-svn branch (upstream_branch) is our upstream | 641 # Our git-svn branch (upstream_branch) is our upstream |
513 self._AttemptRebase(upstream_branch, files, options, | 642 self._AttemptRebase(upstream_branch, files, options, |
514 newbase=revision, printed_path=printed_path, | 643 newbase=refish.local_ref, |
515 merge=options.merge) | 644 printed_path=printed_path, merge=options.merge) |
516 printed_path = True | 645 printed_path = True |
517 else: | 646 else: |
518 # Can't find a merge-base since we don't know our upstream. That makes | 647 # Can't find a merge-base since we don't know our upstream. That makes |
519 # this command VERY likely to produce a rebase failure. For now we | 648 # this command VERY likely to produce a rebase failure. For now we |
520 # assume origin is our upstream since that's what the old behavior was. | 649 # assume origin is our upstream since that's what the old behavior was. |
521 upstream_branch = self.remote | 650 upstream_branch = self.remote |
522 if options.revision or deps_revision: | 651 if options.revision or deps_revision: |
523 upstream_branch = revision | 652 upstream_branch = refish.local_ref |
524 self._AttemptRebase(upstream_branch, files, options, | 653 self._AttemptRebase(upstream_branch, files, options, |
525 printed_path=printed_path, merge=options.merge) | 654 printed_path=printed_path, merge=options.merge) |
526 printed_path = True | 655 printed_path = True |
527 elif rev_type == 'hash': | 656 elif not refish.is_branch: |
528 # case 2 | 657 # case 2 |
529 self._AttemptRebase(upstream_branch, files, options, | 658 self._AttemptRebase(upstream_branch, files, options, |
530 newbase=revision, printed_path=printed_path, | 659 newbase=refish.local_ref, printed_path=printed_path, |
531 merge=options.merge) | 660 merge=options.merge) |
532 printed_path = True | 661 printed_path = True |
533 elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch: | 662 elif refish.upstream_branch != upstream_branch: |
534 # case 4 | 663 # case 4 |
535 new_base = revision.replace('heads', 'remotes/' + self.remote) | 664 new_base = refish.upstream_branch |
536 if not printed_path: | 665 if not printed_path: |
537 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) | 666 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
538 switch_error = ("Switching upstream branch from %s to %s\n" | 667 switch_error = ("Switching upstream branch from %s to %s\n" |
539 % (upstream_branch, new_base) + | 668 % (upstream_branch, new_base) + |
540 "Please merge or rebase manually:\n" + | 669 "Please merge or rebase manually:\n" + |
541 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + | 670 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + |
542 "OR git checkout -b <some new branch> %s" % new_base) | 671 "OR git checkout -b <some new branch> %s" % new_base) |
543 raise gclient_utils.Error(switch_error) | 672 raise gclient_utils.Error(switch_error) |
544 else: | 673 else: |
545 # case 3 - the default case | 674 # case 3 - the default case |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
656 return self.update(options, [], file_list) | 785 return self.update(options, [], file_list) |
657 | 786 |
658 default_rev = "refs/heads/master" | 787 default_rev = "refs/heads/master" |
659 if options.upstream: | 788 if options.upstream: |
660 if self._GetCurrentBranch(): | 789 if self._GetCurrentBranch(): |
661 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) | 790 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) |
662 default_rev = upstream_branch or default_rev | 791 default_rev = upstream_branch or default_rev |
663 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) | 792 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) |
664 if not deps_revision: | 793 if not deps_revision: |
665 deps_revision = default_rev | 794 deps_revision = default_rev |
666 if deps_revision.startswith('refs/heads/'): | 795 refish = self.ParseRefish(deps_revision) |
667 deps_revision = deps_revision.replace('refs/heads/', self.remote + '/') | 796 deps_revision = self.GetUsableRev(refish.remote_refspec, options) |
668 deps_revision = self.GetUsableRev(deps_revision, options) | |
669 | 797 |
670 if file_list is not None: | 798 if file_list is not None: |
671 files = self._Capture(['diff', deps_revision, '--name-only']).split() | 799 files = self._Capture(['diff', deps_revision, '--name-only']).split() |
672 | 800 |
673 self._Run(['reset', '--hard', deps_revision], options) | 801 self._Run(['reset', '--hard', deps_revision], options) |
674 self._Run(['clean', '-f', '-d'], options) | 802 self._Run(['clean', '-f', '-d'], options) |
675 | 803 |
676 if file_list is not None: | 804 if file_list is not None: |
677 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 805 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
678 | 806 |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
835 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'), | 963 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'), |
836 os.path.join(self.checkout_path, '.git')) | 964 os.path.join(self.checkout_path, '.git')) |
837 except: | 965 except: |
838 traceback.print_exc(file=self.out_fh) | 966 traceback.print_exc(file=self.out_fh) |
839 raise | 967 raise |
840 finally: | 968 finally: |
841 if os.listdir(tmp_dir): | 969 if os.listdir(tmp_dir): |
842 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir) | 970 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir) |
843 gclient_utils.rmtree(tmp_dir) | 971 gclient_utils.rmtree(tmp_dir) |
844 self._UpdateBranchHeads(options, fetch=True) | 972 self._UpdateBranchHeads(options, fetch=True) |
845 self._Checkout(options, revision.replace('refs/heads/', ''), quiet=True) | 973 |
| 974 refish = self.ParseRefish(revision) |
| 975 self._Checkout(options, refish.local_ref, quiet=True) |
846 if self._GetCurrentBranch() is None: | 976 if self._GetCurrentBranch() is None: |
847 # Squelch git's very verbose detached HEAD warning and use our own | 977 # Squelch git's very verbose detached HEAD warning and use our own |
848 self.Print( | 978 self.Print( |
849 ('Checked out %s to a detached HEAD. Before making any commits\n' | 979 "Checked out %s to a detached HEAD. Before making any commits\n" |
850 'in this repo, you should use \'git checkout <branch>\' to switch to\n' | 980 "in this repo, you should use 'git checkout <branch>' to switch to\n" |
851 'an existing branch or use \'git checkout %s -b <branch>\' to\n' | 981 "an existing branch or use 'git checkout %s -b <branch>' to\n" |
852 'create a new branch for your work.') % (revision, self.remote)) | 982 "create a new branch for your work." % (revision, self.remote)) |
853 | 983 |
854 def _AskForData(self, prompt, options): | 984 def _AskForData(self, prompt, options): |
855 if options.jobs > 1: | 985 if options.jobs > 1: |
856 self.Print(prompt) | 986 self.Print(prompt) |
857 raise gclient_utils.Error("Background task requires input. Rerun " | 987 raise gclient_utils.Error("Background task requires input. Rerun " |
858 "gclient with --jobs=1 so that\n" | 988 "gclient with --jobs=1 so that\n" |
859 "interaction is possible.") | 989 "interaction is possible.") |
860 try: | 990 try: |
861 return raw_input(prompt) | 991 return raw_input(prompt) |
862 except KeyboardInterrupt: | 992 except KeyboardInterrupt: |
(...skipping 676 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1539 new_command.append('--force') | 1669 new_command.append('--force') |
1540 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1670 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1541 new_command.extend(('--accept', 'theirs-conflict')) | 1671 new_command.extend(('--accept', 'theirs-conflict')) |
1542 elif options.manually_grab_svn_rev: | 1672 elif options.manually_grab_svn_rev: |
1543 new_command.append('--force') | 1673 new_command.append('--force') |
1544 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1674 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1545 new_command.extend(('--accept', 'postpone')) | 1675 new_command.extend(('--accept', 'postpone')) |
1546 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1676 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1547 new_command.extend(('--accept', 'postpone')) | 1677 new_command.extend(('--accept', 'postpone')) |
1548 return new_command | 1678 return new_command |
OLD | NEW |