| 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 |
| 8 |
| 7 import logging | 9 import logging |
| 8 import os | 10 import os |
| 9 import posixpath | 11 import posixpath |
| 10 import re | 12 import re |
| 11 import shlex | 13 import shlex |
| 12 import sys | 14 import sys |
| 13 import tempfile | 15 import tempfile |
| 14 import traceback | 16 import traceback |
| 15 import urlparse | 17 import urlparse |
| 16 | 18 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 28 | 30 |
| 29 | 31 |
| 30 class DiffFiltererWrapper(object): | 32 class DiffFiltererWrapper(object): |
| 31 """Simple base class which tracks which file is being diffed and | 33 """Simple base class which tracks which file is being diffed and |
| 32 replaces instances of its file name in the original and | 34 replaces instances of its file name in the original and |
| 33 working copy lines of the svn/git diff output.""" | 35 working copy lines of the svn/git diff output.""" |
| 34 index_string = None | 36 index_string = None |
| 35 original_prefix = "--- " | 37 original_prefix = "--- " |
| 36 working_prefix = "+++ " | 38 working_prefix = "+++ " |
| 37 | 39 |
| 38 def __init__(self, relpath): | 40 def __init__(self, relpath, print_func): |
| 39 # Note that we always use '/' as the path separator to be | 41 # Note that we always use '/' as the path separator to be |
| 40 # consistent with svn's cygwin-style output on Windows | 42 # consistent with svn's cygwin-style output on Windows |
| 41 self._relpath = relpath.replace("\\", "/") | 43 self._relpath = relpath.replace("\\", "/") |
| 42 self._current_file = None | 44 self._current_file = None |
| 45 self._print_func = print_func |
| 43 | 46 |
| 44 def SetCurrentFile(self, current_file): | 47 def SetCurrentFile(self, current_file): |
| 45 self._current_file = current_file | 48 self._current_file = current_file |
| 46 | 49 |
| 47 @property | 50 @property |
| 48 def _replacement_file(self): | 51 def _replacement_file(self): |
| 49 return posixpath.join(self._relpath, self._current_file) | 52 return posixpath.join(self._relpath, self._current_file) |
| 50 | 53 |
| 51 def _Replace(self, line): | 54 def _Replace(self, line): |
| 52 return line.replace(self._current_file, self._replacement_file) | 55 return line.replace(self._current_file, self._replacement_file) |
| 53 | 56 |
| 54 def Filter(self, line): | 57 def Filter(self, line): |
| 55 if (line.startswith(self.index_string)): | 58 if (line.startswith(self.index_string)): |
| 56 self.SetCurrentFile(line[len(self.index_string):]) | 59 self.SetCurrentFile(line[len(self.index_string):]) |
| 57 line = self._Replace(line) | 60 line = self._Replace(line) |
| 58 else: | 61 else: |
| 59 if (line.startswith(self.original_prefix) or | 62 if (line.startswith(self.original_prefix) or |
| 60 line.startswith(self.working_prefix)): | 63 line.startswith(self.working_prefix)): |
| 61 line = self._Replace(line) | 64 line = self._Replace(line) |
| 62 print(line) | 65 self._print_func(line) |
| 63 | 66 |
| 64 | 67 |
| 65 class SvnDiffFilterer(DiffFiltererWrapper): | 68 class SvnDiffFilterer(DiffFiltererWrapper): |
| 66 index_string = "Index: " | 69 index_string = "Index: " |
| 67 | 70 |
| 68 | 71 |
| 69 class GitDiffFilterer(DiffFiltererWrapper): | 72 class GitDiffFilterer(DiffFiltererWrapper): |
| 70 index_string = "diff --git " | 73 index_string = "diff --git " |
| 71 | 74 |
| 72 def SetCurrentFile(self, current_file): | 75 def SetCurrentFile(self, current_file): |
| (...skipping 14 matching lines...) Expand all Loading... |
| 87 if (url.startswith('git://') or url.startswith('ssh://') or | 90 if (url.startswith('git://') or url.startswith('ssh://') or |
| 88 url.startswith('git+http://') or url.startswith('git+https://') or | 91 url.startswith('git+http://') or url.startswith('git+https://') or |
| 89 url.endswith('.git') or url.startswith('sso://')): | 92 url.endswith('.git') or url.startswith('sso://')): |
| 90 return 'git' | 93 return 'git' |
| 91 elif (url.startswith('http://') or url.startswith('https://') or | 94 elif (url.startswith('http://') or url.startswith('https://') or |
| 92 url.startswith('svn://') or url.startswith('svn+ssh://')): | 95 url.startswith('svn://') or url.startswith('svn+ssh://')): |
| 93 return 'svn' | 96 return 'svn' |
| 94 return None | 97 return None |
| 95 | 98 |
| 96 | 99 |
| 97 def CreateSCM(url, root_dir=None, relpath=None): | 100 def CreateSCM(url, root_dir=None, relpath=None, out_fh=None, out_cb=None): |
| 98 SCM_MAP = { | 101 SCM_MAP = { |
| 99 'svn' : SVNWrapper, | 102 'svn' : SVNWrapper, |
| 100 'git' : GitWrapper, | 103 'git' : GitWrapper, |
| 101 } | 104 } |
| 102 | 105 |
| 103 scm_name = GetScmName(url) | 106 scm_name = GetScmName(url) |
| 104 if not scm_name in SCM_MAP: | 107 if not scm_name in SCM_MAP: |
| 105 raise gclient_utils.Error('No SCM found for url %s' % url) | 108 raise gclient_utils.Error('No SCM found for url %s' % url) |
| 106 scm_class = SCM_MAP[scm_name] | 109 scm_class = SCM_MAP[scm_name] |
| 107 if not scm_class.BinaryExists(): | 110 if not scm_class.BinaryExists(): |
| 108 raise gclient_utils.Error('%s command not found' % scm_name) | 111 raise gclient_utils.Error('%s command not found' % scm_name) |
| 109 return scm_class(url, root_dir, relpath) | 112 return scm_class(url, root_dir, relpath, out_fh, out_cb) |
| 110 | 113 |
| 111 | 114 |
| 112 # SCMWrapper base class | 115 # SCMWrapper base class |
| 113 | 116 |
| 114 class SCMWrapper(object): | 117 class SCMWrapper(object): |
| 115 """Add necessary glue between all the supported SCM. | 118 """Add necessary glue between all the supported SCM. |
| 116 | 119 |
| 117 This is the abstraction layer to bind to different SCM. | 120 This is the abstraction layer to bind to different SCM. |
| 118 """ | 121 """ |
| 119 def __init__(self, url=None, root_dir=None, relpath=None): | 122 |
| 123 def __init__(self, url=None, root_dir=None, relpath=None, out_fh=None, |
| 124 out_cb=None): |
| 120 self.url = url | 125 self.url = url |
| 121 self._root_dir = root_dir | 126 self._root_dir = root_dir |
| 122 if self._root_dir: | 127 if self._root_dir: |
| 123 self._root_dir = self._root_dir.replace('/', os.sep) | 128 self._root_dir = self._root_dir.replace('/', os.sep) |
| 124 self.relpath = relpath | 129 self.relpath = relpath |
| 125 if self.relpath: | 130 if self.relpath: |
| 126 self.relpath = self.relpath.replace('/', os.sep) | 131 self.relpath = self.relpath.replace('/', os.sep) |
| 127 if self.relpath and self._root_dir: | 132 if self.relpath and self._root_dir: |
| 128 self.checkout_path = os.path.join(self._root_dir, self.relpath) | 133 self.checkout_path = os.path.join(self._root_dir, self.relpath) |
| 134 if out_fh is None: |
| 135 out_fh = sys.stdout |
| 136 self.out_fh = out_fh |
| 137 self.out_cb = out_cb |
| 138 |
| 139 def Print(self, *args, **kwargs): |
| 140 kwargs.setdefault('file', self.out_fh) |
| 141 if kwargs.pop('timestamp', True): |
| 142 self.out_fh.write('[%s] ' % gclient_utils.Elapsed()) |
| 143 print(*args, **kwargs) |
| 129 | 144 |
| 130 def RunCommand(self, command, options, args, file_list=None): | 145 def RunCommand(self, command, options, args, file_list=None): |
| 131 commands = ['cleanup', 'update', 'updatesingle', 'revert', | 146 commands = ['cleanup', 'update', 'updatesingle', 'revert', |
| 132 'revinfo', 'status', 'diff', 'pack', 'runhooks'] | 147 'revinfo', 'status', 'diff', 'pack', 'runhooks'] |
| 133 | 148 |
| 134 if not command in commands: | 149 if not command in commands: |
| 135 raise gclient_utils.Error('Unknown command %s' % command) | 150 raise gclient_utils.Error('Unknown command %s' % command) |
| 136 | 151 |
| 137 if not command in dir(self): | 152 if not command in dir(self): |
| 138 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( | 153 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 return False | 199 return False |
| 185 | 200 |
| 186 | 201 |
| 187 class GitWrapper(SCMWrapper): | 202 class GitWrapper(SCMWrapper): |
| 188 """Wrapper for Git""" | 203 """Wrapper for Git""" |
| 189 name = 'git' | 204 name = 'git' |
| 190 remote = 'origin' | 205 remote = 'origin' |
| 191 | 206 |
| 192 cache_dir = None | 207 cache_dir = None |
| 193 | 208 |
| 194 def __init__(self, url=None, root_dir=None, relpath=None): | 209 def __init__(self, url=None, root_dir=None, relpath=None, out_fh=None, |
| 210 out_cb=None): |
| 195 """Removes 'git+' fake prefix from git URL.""" | 211 """Removes 'git+' fake prefix from git URL.""" |
| 196 if url.startswith('git+http://') or url.startswith('git+https://'): | 212 if url.startswith('git+http://') or url.startswith('git+https://'): |
| 197 url = url[4:] | 213 url = url[4:] |
| 198 SCMWrapper.__init__(self, url, root_dir, relpath) | 214 SCMWrapper.__init__(self, url, root_dir, relpath, out_fh, out_cb) |
| 199 | 215 |
| 200 @staticmethod | 216 @staticmethod |
| 201 def BinaryExists(): | 217 def BinaryExists(): |
| 202 """Returns true if the command exists.""" | 218 """Returns true if the command exists.""" |
| 203 try: | 219 try: |
| 204 # We assume git is newer than 1.7. See: crbug.com/114483 | 220 # We assume git is newer than 1.7. See: crbug.com/114483 |
| 205 result, version = scm.GIT.AssertVersion('1.7') | 221 result, version = scm.GIT.AssertVersion('1.7') |
| 206 if not result: | 222 if not result: |
| 207 raise gclient_utils.Error('Git version is older than 1.7: %s' % version) | 223 raise gclient_utils.Error('Git version is older than 1.7: %s' % version) |
| 208 return result | 224 return result |
| (...skipping 25 matching lines...) Expand all Loading... |
| 234 """Generates a patch file which can be applied to the root of the | 250 """Generates a patch file which can be applied to the root of the |
| 235 repository. | 251 repository. |
| 236 | 252 |
| 237 The patch file is generated from a diff of the merge base of HEAD and | 253 The patch file is generated from a diff of the merge base of HEAD and |
| 238 its upstream branch. | 254 its upstream branch. |
| 239 """ | 255 """ |
| 240 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) | 256 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) |
| 241 gclient_utils.CheckCallAndFilter( | 257 gclient_utils.CheckCallAndFilter( |
| 242 ['git', 'diff', merge_base], | 258 ['git', 'diff', merge_base], |
| 243 cwd=self.checkout_path, | 259 cwd=self.checkout_path, |
| 244 filter_fn=GitDiffFilterer(self.relpath).Filter) | 260 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print) |
| 245 | 261 |
| 246 def UpdateSubmoduleConfig(self): | 262 def UpdateSubmoduleConfig(self): |
| 247 submod_cmd = ['git', 'config', '-f', '$toplevel/.git/config', | 263 submod_cmd = ['git', 'config', '-f', '$toplevel/.git/config', |
| 248 'submodule.$name.ignore', 'all'] | 264 'submodule.$name.ignore', 'all'] |
| 249 cmd = ['git', 'submodule', '--quiet', 'foreach', ' '.join(submod_cmd)] | 265 cmd = ['git', 'submodule', '--quiet', 'foreach', ' '.join(submod_cmd)] |
| 250 cmd2 = ['git', 'config', 'diff.ignoreSubmodules', 'all'] | 266 cmd2 = ['git', 'config', 'diff.ignoreSubmodules', 'all'] |
| 251 cmd3 = ['git', 'config', 'branch.autosetupmerge'] | 267 cmd3 = ['git', 'config', 'branch.autosetupmerge'] |
| 252 cmd4 = ['git', 'config', 'fetch.recurseSubmodules', 'false'] | 268 cmd4 = ['git', 'config', 'fetch.recurseSubmodules', 'false'] |
| 253 kwargs = {'cwd': self.checkout_path, | 269 kwargs = {'cwd': self.checkout_path, |
| 254 'print_stdout': False, | 270 'print_stdout': False, |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 revision = None | 325 revision = None |
| 310 managed = False | 326 managed = False |
| 311 if not revision: | 327 if not revision: |
| 312 revision = default_rev | 328 revision = default_rev |
| 313 | 329 |
| 314 if gclient_utils.IsDateRevision(revision): | 330 if gclient_utils.IsDateRevision(revision): |
| 315 # Date-revisions only work on git-repositories if the reflog hasn't | 331 # Date-revisions only work on git-repositories if the reflog hasn't |
| 316 # expired yet. Use rev-list to get the corresponding revision. | 332 # expired yet. Use rev-list to get the corresponding revision. |
| 317 # git rev-list -n 1 --before='time-stamp' branchname | 333 # git rev-list -n 1 --before='time-stamp' branchname |
| 318 if options.transitive: | 334 if options.transitive: |
| 319 print('Warning: --transitive only works for SVN repositories.') | 335 self.Print('Warning: --transitive only works for SVN repositories.') |
| 320 revision = default_rev | 336 revision = default_rev |
| 321 | 337 |
| 322 rev_str = ' at %s' % revision | 338 rev_str = ' at %s' % revision |
| 323 files = [] if file_list is not None else None | 339 files = [] if file_list is not None else None |
| 324 | 340 |
| 325 printed_path = False | 341 printed_path = False |
| 326 verbose = [] | 342 verbose = [] |
| 327 if options.verbose: | 343 if options.verbose: |
| 328 print('\n_____ %s%s' % (self.relpath, rev_str)) | 344 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
| 329 verbose = ['--verbose'] | 345 verbose = ['--verbose'] |
| 330 printed_path = True | 346 printed_path = True |
| 331 | 347 |
| 332 url = self._CreateOrUpdateCache(url, options) | 348 url = self._CreateOrUpdateCache(url, options) |
| 333 | 349 |
| 334 if revision.startswith('refs/'): | 350 if revision.startswith('refs/'): |
| 335 rev_type = "branch" | 351 rev_type = "branch" |
| 336 elif revision.startswith(self.remote + '/'): | 352 elif revision.startswith(self.remote + '/'): |
| 337 # For compatibility with old naming, translate 'origin' to 'refs/heads' | 353 # For compatibility with old naming, translate 'origin' to 'refs/heads' |
| 338 revision = revision.replace(self.remote + '/', 'refs/heads/') | 354 revision = revision.replace(self.remote + '/', 'refs/heads/') |
| 339 rev_type = "branch" | 355 rev_type = "branch" |
| 340 else: | 356 else: |
| 341 # hash is also a tag, only make a distinction at checkout | 357 # hash is also a tag, only make a distinction at checkout |
| 342 rev_type = "hash" | 358 rev_type = "hash" |
| 343 | 359 |
| 344 if (not os.path.exists(self.checkout_path) or | 360 if (not os.path.exists(self.checkout_path) or |
| 345 (os.path.isdir(self.checkout_path) and | 361 (os.path.isdir(self.checkout_path) and |
| 346 not os.path.exists(os.path.join(self.checkout_path, '.git')))): | 362 not os.path.exists(os.path.join(self.checkout_path, '.git')))): |
| 347 self._Clone(revision, url, options) | 363 self._Clone(revision, url, options) |
| 348 self.UpdateSubmoduleConfig() | 364 self.UpdateSubmoduleConfig() |
| 349 if file_list is not None: | 365 if file_list is not None: |
| 350 files = self._Capture(['ls-files']).splitlines() | 366 files = self._Capture(['ls-files']).splitlines() |
| 351 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 367 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 352 if not verbose: | 368 if not verbose: |
| 353 # Make the output a little prettier. It's nice to have some whitespace | 369 # Make the output a little prettier. It's nice to have some whitespace |
| 354 # between projects when cloning. | 370 # between projects when cloning. |
| 355 print('') | 371 self.Print('') |
| 356 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 372 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
| 357 | 373 |
| 358 if not managed: | 374 if not managed: |
| 359 self._UpdateBranchHeads(options, fetch=False) | 375 self._UpdateBranchHeads(options, fetch=False) |
| 360 self.UpdateSubmoduleConfig() | 376 self.UpdateSubmoduleConfig() |
| 361 print ('________ unmanaged solution; skipping %s' % self.relpath) | 377 self.Print('________ unmanaged solution; skipping %s' % self.relpath) |
| 362 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 378 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
| 363 | 379 |
| 364 if not os.path.exists(os.path.join(self.checkout_path, '.git')): | 380 if not os.path.exists(os.path.join(self.checkout_path, '.git')): |
| 365 raise gclient_utils.Error('\n____ %s%s\n' | 381 raise gclient_utils.Error('\n____ %s%s\n' |
| 366 '\tPath is not a git repo. No .git dir.\n' | 382 '\tPath is not a git repo. No .git dir.\n' |
| 367 '\tTo resolve:\n' | 383 '\tTo resolve:\n' |
| 368 '\t\trm -rf %s\n' | 384 '\t\trm -rf %s\n' |
| 369 '\tAnd run gclient sync again\n' | 385 '\tAnd run gclient sync again\n' |
| 370 % (self.relpath, rev_str, self.relpath)) | 386 % (self.relpath, rev_str, self.relpath)) |
| 371 | 387 |
| 372 # See if the url has changed (the unittests use git://foo for the url, let | 388 # See if the url has changed (the unittests use git://foo for the url, let |
| 373 # that through). | 389 # that through). |
| 374 current_url = self._Capture(['config', 'remote.%s.url' % self.remote]) | 390 current_url = self._Capture(['config', 'remote.%s.url' % self.remote]) |
| 375 return_early = False | 391 return_early = False |
| 376 # TODO(maruel): Delete url != 'git://foo' since it's just to make the | 392 # TODO(maruel): Delete url != 'git://foo' since it's just to make the |
| 377 # unit test pass. (and update the comment above) | 393 # unit test pass. (and update the comment above) |
| 378 # Skip url auto-correction if remote.origin.gclient-auto-fix-url is set. | 394 # Skip url auto-correction if remote.origin.gclient-auto-fix-url is set. |
| 379 # This allows devs to use experimental repos which have a different url | 395 # This allows devs to use experimental repos which have a different url |
| 380 # but whose branch(s) are the same as official repos. | 396 # but whose branch(s) are the same as official repos. |
| 381 if (current_url != url and | 397 if (current_url != url and |
| 382 url != 'git://foo' and | 398 url != 'git://foo' and |
| 383 subprocess2.capture( | 399 subprocess2.capture( |
| 384 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], | 400 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], |
| 385 cwd=self.checkout_path).strip() != 'False'): | 401 cwd=self.checkout_path).strip() != 'False'): |
| 386 print('_____ switching %s to a new upstream' % self.relpath) | 402 self.Print('_____ switching %s to a new upstream' % self.relpath) |
| 387 # Make sure it's clean | 403 # Make sure it's clean |
| 388 self._CheckClean(rev_str) | 404 self._CheckClean(rev_str) |
| 389 # Switch over to the new upstream | 405 # Switch over to the new upstream |
| 390 self._Run(['remote', 'set-url', self.remote, url], options) | 406 self._Run(['remote', 'set-url', self.remote, url], options) |
| 391 self._FetchAndReset(revision, file_list, options) | 407 self._FetchAndReset(revision, file_list, options) |
| 392 return_early = True | 408 return_early = True |
| 393 | 409 |
| 394 if return_early: | 410 if return_early: |
| 395 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 411 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
| 396 | 412 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 429 elif upstream_branch.startswith('refs/remotes'): | 445 elif upstream_branch.startswith('refs/remotes'): |
| 430 current_type = "branch" | 446 current_type = "branch" |
| 431 else: | 447 else: |
| 432 raise gclient_utils.Error('Invalid Upstream: %s' % upstream_branch) | 448 raise gclient_utils.Error('Invalid Upstream: %s' % upstream_branch) |
| 433 | 449 |
| 434 if not scm.GIT.IsValidRevision(self.checkout_path, revision, sha_only=True): | 450 if not scm.GIT.IsValidRevision(self.checkout_path, revision, sha_only=True): |
| 435 # Update the remotes first so we have all the refs. | 451 # Update the remotes first so we have all the refs. |
| 436 remote_output = scm.GIT.Capture(['remote'] + verbose + ['update'], | 452 remote_output = scm.GIT.Capture(['remote'] + verbose + ['update'], |
| 437 cwd=self.checkout_path) | 453 cwd=self.checkout_path) |
| 438 if verbose: | 454 if verbose: |
| 439 print(remote_output) | 455 self.Print(remote_output) |
| 440 | 456 |
| 441 self._UpdateBranchHeads(options, fetch=True) | 457 self._UpdateBranchHeads(options, fetch=True) |
| 442 | 458 |
| 443 # This is a big hammer, debatable if it should even be here... | 459 # This is a big hammer, debatable if it should even be here... |
| 444 if options.force or options.reset: | 460 if options.force or options.reset: |
| 445 target = 'HEAD' | 461 target = 'HEAD' |
| 446 if options.upstream and upstream_branch: | 462 if options.upstream and upstream_branch: |
| 447 target = upstream_branch | 463 target = upstream_branch |
| 448 self._Run(['reset', '--hard', target], options) | 464 self._Run(['reset', '--hard', target], options) |
| 449 | 465 |
| 450 if current_type == 'detached': | 466 if current_type == 'detached': |
| 451 # case 0 | 467 # case 0 |
| 452 self._CheckClean(rev_str) | 468 self._CheckClean(rev_str) |
| 453 self._CheckDetachedHead(rev_str, options) | 469 self._CheckDetachedHead(rev_str, options) |
| 454 self._Capture(['checkout', '--quiet', '%s' % revision]) | 470 self._Capture(['checkout', '--quiet', '%s' % revision]) |
| 455 if not printed_path: | 471 if not printed_path: |
| 456 print('\n_____ %s%s' % (self.relpath, rev_str)) | 472 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
| 457 elif current_type == 'hash': | 473 elif current_type == 'hash': |
| 458 # case 1 | 474 # case 1 |
| 459 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: | 475 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: |
| 460 # Our git-svn branch (upstream_branch) is our upstream | 476 # Our git-svn branch (upstream_branch) is our upstream |
| 461 self._AttemptRebase(upstream_branch, files, options, | 477 self._AttemptRebase(upstream_branch, files, options, |
| 462 newbase=revision, printed_path=printed_path, | 478 newbase=revision, printed_path=printed_path, |
| 463 merge=options.merge) | 479 merge=options.merge) |
| 464 printed_path = True | 480 printed_path = True |
| 465 else: | 481 else: |
| 466 # Can't find a merge-base since we don't know our upstream. That makes | 482 # Can't find a merge-base since we don't know our upstream. That makes |
| 467 # this command VERY likely to produce a rebase failure. For now we | 483 # this command VERY likely to produce a rebase failure. For now we |
| 468 # assume origin is our upstream since that's what the old behavior was. | 484 # assume origin is our upstream since that's what the old behavior was. |
| 469 upstream_branch = self.remote | 485 upstream_branch = self.remote |
| 470 if options.revision or deps_revision: | 486 if options.revision or deps_revision: |
| 471 upstream_branch = revision | 487 upstream_branch = revision |
| 472 self._AttemptRebase(upstream_branch, files, options, | 488 self._AttemptRebase(upstream_branch, files, options, |
| 473 printed_path=printed_path, merge=options.merge) | 489 printed_path=printed_path, merge=options.merge) |
| 474 printed_path = True | 490 printed_path = True |
| 475 elif rev_type == 'hash': | 491 elif rev_type == 'hash': |
| 476 # case 2 | 492 # case 2 |
| 477 self._AttemptRebase(upstream_branch, files, options, | 493 self._AttemptRebase(upstream_branch, files, options, |
| 478 newbase=revision, printed_path=printed_path, | 494 newbase=revision, printed_path=printed_path, |
| 479 merge=options.merge) | 495 merge=options.merge) |
| 480 printed_path = True | 496 printed_path = True |
| 481 elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch: | 497 elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch: |
| 482 # case 4 | 498 # case 4 |
| 483 new_base = revision.replace('heads', 'remotes/' + self.remote) | 499 new_base = revision.replace('heads', 'remotes/' + self.remote) |
| 484 if not printed_path: | 500 if not printed_path: |
| 485 print('\n_____ %s%s' % (self.relpath, rev_str)) | 501 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
| 486 switch_error = ("Switching upstream branch from %s to %s\n" | 502 switch_error = ("Switching upstream branch from %s to %s\n" |
| 487 % (upstream_branch, new_base) + | 503 % (upstream_branch, new_base) + |
| 488 "Please merge or rebase manually:\n" + | 504 "Please merge or rebase manually:\n" + |
| 489 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + | 505 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + |
| 490 "OR git checkout -b <some new branch> %s" % new_base) | 506 "OR git checkout -b <some new branch> %s" % new_base) |
| 491 raise gclient_utils.Error(switch_error) | 507 raise gclient_utils.Error(switch_error) |
| 492 else: | 508 else: |
| 493 # case 3 - the default case | 509 # case 3 - the default case |
| 494 if files is not None: | 510 if files is not None: |
| 495 files = self._Capture(['diff', upstream_branch, '--name-only']).split() | 511 files = self._Capture(['diff', upstream_branch, '--name-only']).split() |
| 496 if verbose: | 512 if verbose: |
| 497 print('Trying fast-forward merge to branch : %s' % upstream_branch) | 513 self.Print('Trying fast-forward merge to branch : %s' % upstream_branch) |
| 498 try: | 514 try: |
| 499 merge_args = ['merge'] | 515 merge_args = ['merge'] |
| 500 if options.merge: | 516 if options.merge: |
| 501 merge_args.append('--ff') | 517 merge_args.append('--ff') |
| 502 else: | 518 else: |
| 503 merge_args.append('--ff-only') | 519 merge_args.append('--ff-only') |
| 504 merge_args.append(upstream_branch) | 520 merge_args.append(upstream_branch) |
| 505 merge_output = scm.GIT.Capture(merge_args, cwd=self.checkout_path) | 521 merge_output = self._Capture(merge_args) |
| 506 except subprocess2.CalledProcessError as e: | 522 except subprocess2.CalledProcessError as e: |
| 507 if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): | 523 if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): |
| 508 files = [] | 524 files = [] |
| 509 if not printed_path: | 525 if not printed_path: |
| 510 print('\n_____ %s%s' % (self.relpath, rev_str)) | 526 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
| 511 printed_path = True | 527 printed_path = True |
| 512 while True: | 528 while True: |
| 513 try: | 529 try: |
| 514 action = self._AskForData( | 530 action = self._AskForData( |
| 515 'Cannot %s, attempt to rebase? ' | 531 'Cannot %s, attempt to rebase? ' |
| 516 '(y)es / (q)uit / (s)kip : ' % | 532 '(y)es / (q)uit / (s)kip : ' % |
| 517 ('merge' if options.merge else 'fast-forward merge'), | 533 ('merge' if options.merge else 'fast-forward merge'), |
| 518 options) | 534 options) |
| 519 except ValueError: | 535 except ValueError: |
| 520 raise gclient_utils.Error('Invalid Character') | 536 raise gclient_utils.Error('Invalid Character') |
| 521 if re.match(r'yes|y', action, re.I): | 537 if re.match(r'yes|y', action, re.I): |
| 522 self._AttemptRebase(upstream_branch, files, options, | 538 self._AttemptRebase(upstream_branch, files, options, |
| 523 printed_path=printed_path, merge=False) | 539 printed_path=printed_path, merge=False) |
| 524 printed_path = True | 540 printed_path = True |
| 525 break | 541 break |
| 526 elif re.match(r'quit|q', action, re.I): | 542 elif re.match(r'quit|q', action, re.I): |
| 527 raise gclient_utils.Error("Can't fast-forward, please merge or " | 543 raise gclient_utils.Error("Can't fast-forward, please merge or " |
| 528 "rebase manually.\n" | 544 "rebase manually.\n" |
| 529 "cd %s && git " % self.checkout_path | 545 "cd %s && git " % self.checkout_path |
| 530 + "rebase %s" % upstream_branch) | 546 + "rebase %s" % upstream_branch) |
| 531 elif re.match(r'skip|s', action, re.I): | 547 elif re.match(r'skip|s', action, re.I): |
| 532 print('Skipping %s' % self.relpath) | 548 self.Print('Skipping %s' % self.relpath) |
| 533 return | 549 return |
| 534 else: | 550 else: |
| 535 print('Input not recognized') | 551 self.Print('Input not recognized') |
| 536 elif re.match("error: Your local changes to '.*' would be " | 552 elif re.match("error: Your local changes to '.*' would be " |
| 537 "overwritten by merge. Aborting.\nPlease, commit your " | 553 "overwritten by merge. Aborting.\nPlease, commit your " |
| 538 "changes or stash them before you can merge.\n", | 554 "changes or stash them before you can merge.\n", |
| 539 e.stderr): | 555 e.stderr): |
| 540 if not printed_path: | 556 if not printed_path: |
| 541 print('\n_____ %s%s' % (self.relpath, rev_str)) | 557 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
| 542 printed_path = True | 558 printed_path = True |
| 543 raise gclient_utils.Error(e.stderr) | 559 raise gclient_utils.Error(e.stderr) |
| 544 else: | 560 else: |
| 545 # Some other problem happened with the merge | 561 # Some other problem happened with the merge |
| 546 logging.error("Error during fast-forward merge in %s!" % self.relpath) | 562 logging.error("Error during fast-forward merge in %s!" % self.relpath) |
| 547 print(e.stderr) | 563 self.Print(e.stderr) |
| 548 raise | 564 raise |
| 549 else: | 565 else: |
| 550 # Fast-forward merge was successful | 566 # Fast-forward merge was successful |
| 551 if not re.match('Already up-to-date.', merge_output) or verbose: | 567 if not re.match('Already up-to-date.', merge_output) or verbose: |
| 552 if not printed_path: | 568 if not printed_path: |
| 553 print('\n_____ %s%s' % (self.relpath, rev_str)) | 569 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
| 554 printed_path = True | 570 printed_path = True |
| 555 print(merge_output.strip()) | 571 self.Print(merge_output.strip()) |
| 556 if not verbose: | 572 if not verbose: |
| 557 # Make the output a little prettier. It's nice to have some | 573 # Make the output a little prettier. It's nice to have some |
| 558 # whitespace between projects when syncing. | 574 # whitespace between projects when syncing. |
| 559 print('') | 575 self.Print('') |
| 560 | 576 |
| 561 self.UpdateSubmoduleConfig() | 577 self.UpdateSubmoduleConfig() |
| 562 if file_list is not None: | 578 if file_list is not None: |
| 563 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 579 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 564 | 580 |
| 565 # If the rebase generated a conflict, abort and ask user to fix | 581 # If the rebase generated a conflict, abort and ask user to fix |
| 566 if self._IsRebasing(): | 582 if self._IsRebasing(): |
| 567 raise gclient_utils.Error('\n____ %s%s\n' | 583 raise gclient_utils.Error('\n____ %s%s\n' |
| 568 '\nConflict while rebasing this branch.\n' | 584 '\nConflict while rebasing this branch.\n' |
| 569 'Fix the conflict and run gclient again.\n' | 585 'Fix the conflict and run gclient again.\n' |
| 570 'See man git-rebase for details.\n' | 586 'See man git-rebase for details.\n' |
| 571 % (self.relpath, rev_str)) | 587 % (self.relpath, rev_str)) |
| 572 | 588 |
| 573 if verbose: | 589 if verbose: |
| 574 print('Checked out revision %s' % self.revinfo(options, (), None)) | 590 self.Print('Checked out revision %s' % self.revinfo(options, (), None), |
| 591 timestamp=False) |
| 575 | 592 |
| 576 # If --reset and --delete_unversioned_trees are specified, remove any | 593 # If --reset and --delete_unversioned_trees are specified, remove any |
| 577 # untracked directories. | 594 # untracked directories. |
| 578 if options.reset and options.delete_unversioned_trees: | 595 if options.reset and options.delete_unversioned_trees: |
| 579 # GIT.CaptureStatus() uses 'dit diff' to compare to a specific SHA1 (the | 596 # GIT.CaptureStatus() uses 'dit diff' to compare to a specific SHA1 (the |
| 580 # merge-base by default), so doesn't include untracked files. So we use | 597 # merge-base by default), so doesn't include untracked files. So we use |
| 581 # 'git ls-files --directory --others --exclude-standard' here directly. | 598 # 'git ls-files --directory --others --exclude-standard' here directly. |
| 582 paths = scm.GIT.Capture( | 599 paths = scm.GIT.Capture( |
| 583 ['ls-files', '--directory', '--others', '--exclude-standard'], | 600 ['ls-files', '--directory', '--others', '--exclude-standard'], |
| 584 self.checkout_path) | 601 self.checkout_path) |
| 585 for path in (p for p in paths.splitlines() if p.endswith('/')): | 602 for path in (p for p in paths.splitlines() if p.endswith('/')): |
| 586 full_path = os.path.join(self.checkout_path, path) | 603 full_path = os.path.join(self.checkout_path, path) |
| 587 if not os.path.islink(full_path): | 604 if not os.path.islink(full_path): |
| 588 print('\n_____ removing unversioned directory %s' % path) | 605 self.Print('_____ removing unversioned directory %s' % path) |
| 589 gclient_utils.rmtree(full_path) | 606 gclient_utils.rmtree(full_path) |
| 590 | 607 |
| 591 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 608 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
| 592 | 609 |
| 593 | 610 |
| 594 def revert(self, options, _args, file_list): | 611 def revert(self, options, _args, file_list): |
| 595 """Reverts local modifications. | 612 """Reverts local modifications. |
| 596 | 613 |
| 597 All reverted files will be appended to file_list. | 614 All reverted files will be appended to file_list. |
| 598 """ | 615 """ |
| 599 if not os.path.isdir(self.checkout_path): | 616 if not os.path.isdir(self.checkout_path): |
| 600 # revert won't work if the directory doesn't exist. It needs to | 617 # revert won't work if the directory doesn't exist. It needs to |
| 601 # checkout instead. | 618 # checkout instead. |
| 602 print('\n_____ %s is missing, synching instead' % self.relpath) | 619 self.Print('_____ %s is missing, synching instead' % self.relpath) |
| 603 # Don't reuse the args. | 620 # Don't reuse the args. |
| 604 return self.update(options, [], file_list) | 621 return self.update(options, [], file_list) |
| 605 | 622 |
| 606 default_rev = "refs/heads/master" | 623 default_rev = "refs/heads/master" |
| 607 if options.upstream: | 624 if options.upstream: |
| 608 if self._GetCurrentBranch(): | 625 if self._GetCurrentBranch(): |
| 609 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) | 626 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) |
| 610 default_rev = upstream_branch or default_rev | 627 default_rev = upstream_branch or default_rev |
| 611 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) | 628 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) |
| 612 if not deps_revision: | 629 if not deps_revision: |
| (...skipping 14 matching lines...) Expand all Loading... |
| 627 def revinfo(self, _options, _args, _file_list): | 644 def revinfo(self, _options, _args, _file_list): |
| 628 """Returns revision""" | 645 """Returns revision""" |
| 629 return self._Capture(['rev-parse', 'HEAD']) | 646 return self._Capture(['rev-parse', 'HEAD']) |
| 630 | 647 |
| 631 def runhooks(self, options, args, file_list): | 648 def runhooks(self, options, args, file_list): |
| 632 self.status(options, args, file_list) | 649 self.status(options, args, file_list) |
| 633 | 650 |
| 634 def status(self, options, _args, file_list): | 651 def status(self, options, _args, file_list): |
| 635 """Display status information.""" | 652 """Display status information.""" |
| 636 if not os.path.isdir(self.checkout_path): | 653 if not os.path.isdir(self.checkout_path): |
| 637 print(('\n________ couldn\'t run status in %s:\n' | 654 self.Print('________ couldn\'t run status in %s:\n' |
| 638 'The directory does not exist.') % self.checkout_path) | 655 'The directory does not exist.' % self.checkout_path) |
| 639 else: | 656 else: |
| 640 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) | 657 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) |
| 641 self._Run(['diff', '--name-status', merge_base], options) | 658 self._Run(['diff', '--name-status', merge_base], options, |
| 659 stdout=self.out_fh) |
| 642 if file_list is not None: | 660 if file_list is not None: |
| 643 files = self._Capture(['diff', '--name-only', merge_base]).split() | 661 files = self._Capture(['diff', '--name-only', merge_base]).split() |
| 644 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 662 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 645 | 663 |
| 646 def GetUsableRev(self, rev, options): | 664 def GetUsableRev(self, rev, options): |
| 647 """Finds a useful revision for this repository. | 665 """Finds a useful revision for this repository. |
| 648 | 666 |
| 649 If SCM is git-svn and the head revision is less than |rev|, git svn fetch | 667 If SCM is git-svn and the head revision is less than |rev|, git svn fetch |
| 650 will be called on the source.""" | 668 will be called on the source.""" |
| 651 sha1 = None | 669 sha1 = None |
| (...skipping 13 matching lines...) Expand all Loading... |
| 665 if not local_head or local_head < int(rev): | 683 if not local_head or local_head < int(rev): |
| 666 try: | 684 try: |
| 667 logging.debug('Looking for git-svn configuration optimizations.') | 685 logging.debug('Looking for git-svn configuration optimizations.') |
| 668 if scm.GIT.Capture(['config', '--get', 'svn-remote.svn.fetch'], | 686 if scm.GIT.Capture(['config', '--get', 'svn-remote.svn.fetch'], |
| 669 cwd=self.checkout_path): | 687 cwd=self.checkout_path): |
| 670 scm.GIT.Capture(['fetch'], cwd=self.checkout_path) | 688 scm.GIT.Capture(['fetch'], cwd=self.checkout_path) |
| 671 except subprocess2.CalledProcessError: | 689 except subprocess2.CalledProcessError: |
| 672 logging.debug('git config --get svn-remote.svn.fetch failed, ' | 690 logging.debug('git config --get svn-remote.svn.fetch failed, ' |
| 673 'ignoring possible optimization.') | 691 'ignoring possible optimization.') |
| 674 if options.verbose: | 692 if options.verbose: |
| 675 print('Running git svn fetch. This might take a while.\n') | 693 self.Print('Running git svn fetch. This might take a while.\n') |
| 676 scm.GIT.Capture(['svn', 'fetch'], cwd=self.checkout_path) | 694 scm.GIT.Capture(['svn', 'fetch'], cwd=self.checkout_path) |
| 677 try: | 695 try: |
| 678 sha1 = scm.GIT.GetBlessedSha1ForSvnRev( | 696 sha1 = scm.GIT.GetBlessedSha1ForSvnRev( |
| 679 cwd=self.checkout_path, rev=rev) | 697 cwd=self.checkout_path, rev=rev) |
| 680 except gclient_utils.Error, e: | 698 except gclient_utils.Error, e: |
| 681 sha1 = e.message | 699 sha1 = e.message |
| 682 print('\nWarning: Could not find a git revision with accurate\n' | 700 self.Print('Warning: Could not find a git revision with accurate\n' |
| 683 '.DEPS.git that maps to SVN revision %s. Sync-ing to\n' | 701 '.DEPS.git that maps to SVN revision %s. Sync-ing to\n' |
| 684 'the closest sane git revision, which is:\n' | 702 'the closest sane git revision, which is:\n' |
| 685 ' %s\n' % (rev, e.message)) | 703 ' %s\n' % (rev, e.message)) |
| 686 if not sha1: | 704 if not sha1: |
| 687 raise gclient_utils.Error( | 705 raise gclient_utils.Error( |
| 688 ( 'It appears that either your git-svn remote is incorrectly\n' | 706 ( 'It appears that either your git-svn remote is incorrectly\n' |
| 689 'configured or the revision in your safesync_url is\n' | 707 'configured or the revision in your safesync_url is\n' |
| 690 'higher than git-svn remote\'s HEAD as we couldn\'t find a\n' | 708 'higher than git-svn remote\'s HEAD as we couldn\'t find a\n' |
| 691 'corresponding git hash for SVN rev %s.' ) % rev) | 709 'corresponding git hash for SVN rev %s.' ) % rev) |
| 692 else: | 710 else: |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 734 """Clone a git repository from the given URL. | 752 """Clone a git repository from the given URL. |
| 735 | 753 |
| 736 Once we've cloned the repo, we checkout a working branch if the specified | 754 Once we've cloned the repo, we checkout a working branch if the specified |
| 737 revision is a branch head. If it is a tag or a specific commit, then we | 755 revision is a branch head. If it is a tag or a specific commit, then we |
| 738 leave HEAD detached as it makes future updates simpler -- in this case the | 756 leave HEAD detached as it makes future updates simpler -- in this case the |
| 739 user should first create a new branch or switch to an existing branch before | 757 user should first create a new branch or switch to an existing branch before |
| 740 making changes in the repo.""" | 758 making changes in the repo.""" |
| 741 if not options.verbose: | 759 if not options.verbose: |
| 742 # git clone doesn't seem to insert a newline properly before printing | 760 # git clone doesn't seem to insert a newline properly before printing |
| 743 # to stdout | 761 # to stdout |
| 744 print('') | 762 self.Print('') |
| 745 template_path = os.path.join( | 763 template_path = os.path.join( |
| 746 os.path.dirname(THIS_FILE_PATH), 'git-templates') | 764 os.path.dirname(THIS_FILE_PATH), 'git-templates') |
| 747 cfg = gclient_utils.DefaultIndexPackConfig(self.url) | 765 cfg = gclient_utils.DefaultIndexPackConfig(self.url) |
| 748 clone_cmd = cfg + [ | 766 clone_cmd = cfg + [ |
| 749 'clone', '--no-checkout', '--progress', '--template=%s' % template_path] | 767 'clone', '--no-checkout', '--progress', '--template=%s' % template_path] |
| 750 if self.cache_dir: | 768 if self.cache_dir: |
| 751 clone_cmd.append('--shared') | 769 clone_cmd.append('--shared') |
| 752 if options.verbose: | 770 if options.verbose: |
| 753 clone_cmd.append('--verbose') | 771 clone_cmd.append('--verbose') |
| 754 clone_cmd.append(url) | 772 clone_cmd.append(url) |
| 755 # If the parent directory does not exist, Git clone on Windows will not | 773 # If the parent directory does not exist, Git clone on Windows will not |
| 756 # create it, so we need to do it manually. | 774 # create it, so we need to do it manually. |
| 757 parent_dir = os.path.dirname(self.checkout_path) | 775 parent_dir = os.path.dirname(self.checkout_path) |
| 758 gclient_utils.safe_makedirs(parent_dir) | 776 gclient_utils.safe_makedirs(parent_dir) |
| 759 tmp_dir = tempfile.mkdtemp( | 777 tmp_dir = tempfile.mkdtemp( |
| 760 prefix='_gclient_%s_' % os.path.basename(self.checkout_path), | 778 prefix='_gclient_%s_' % os.path.basename(self.checkout_path), |
| 761 dir=parent_dir) | 779 dir=parent_dir) |
| 762 try: | 780 try: |
| 763 clone_cmd.append(tmp_dir) | 781 clone_cmd.append(tmp_dir) |
| 764 self._Run(clone_cmd, options, cwd=self._root_dir, retry=True) | 782 self._Run(clone_cmd, options, cwd=self._root_dir, retry=True) |
| 765 gclient_utils.safe_makedirs(self.checkout_path) | 783 gclient_utils.safe_makedirs(self.checkout_path) |
| 766 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'), | 784 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'), |
| 767 os.path.join(self.checkout_path, '.git')) | 785 os.path.join(self.checkout_path, '.git')) |
| 768 except: | 786 except: |
| 769 traceback.print_exc(file=sys.stderr) | 787 traceback.print_exc(file=self.out_fh) |
| 770 raise | 788 raise |
| 771 finally: | 789 finally: |
| 772 if os.listdir(tmp_dir): | 790 if os.listdir(tmp_dir): |
| 773 print('\n_____ removing non-empty tmp dir %s' % tmp_dir) | 791 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir) |
| 774 gclient_utils.rmtree(tmp_dir) | 792 gclient_utils.rmtree(tmp_dir) |
| 775 if revision.startswith('refs/heads/'): | 793 if revision.startswith('refs/heads/'): |
| 776 self._Run( | 794 self._Run( |
| 777 ['checkout', '--quiet', revision.replace('refs/heads/', '')], options) | 795 ['checkout', '--quiet', revision.replace('refs/heads/', '')], options) |
| 778 else: | 796 else: |
| 779 # Squelch git's very verbose detached HEAD warning and use our own | 797 # Squelch git's very verbose detached HEAD warning and use our own |
| 780 self._Run(['checkout', '--quiet', revision], options) | 798 self._Run(['checkout', '--quiet', revision], options) |
| 781 print( | 799 self.Print( |
| 782 ('Checked out %s to a detached HEAD. Before making any commits\n' | 800 ('Checked out %s to a detached HEAD. Before making any commits\n' |
| 783 'in this repo, you should use \'git checkout <branch>\' to switch to\n' | 801 'in this repo, you should use \'git checkout <branch>\' to switch to\n' |
| 784 'an existing branch or use \'git checkout %s -b <branch>\' to\n' | 802 'an existing branch or use \'git checkout %s -b <branch>\' to\n' |
| 785 'create a new branch for your work.') % (revision, self.remote)) | 803 'create a new branch for your work.') % (revision, self.remote)) |
| 786 | 804 |
| 787 @staticmethod | 805 @staticmethod |
| 788 def _AskForData(prompt, options): | 806 def _AskForData(prompt, options): |
| 789 if options.jobs > 1: | 807 if options.jobs > 1: |
| 790 raise gclient_utils.Error("Background task requires input. Rerun " | 808 raise gclient_utils.Error("Background task requires input. Rerun " |
| 791 "gclient with --jobs=1 so that\n" | 809 "gclient with --jobs=1 so that\n" |
| 792 "interaction is possible.") | 810 "interaction is possible.") |
| 793 try: | 811 try: |
| 794 return raw_input(prompt) | 812 return raw_input(prompt) |
| 795 except KeyboardInterrupt: | 813 except KeyboardInterrupt: |
| 796 # Hide the exception. | 814 # Hide the exception. |
| 797 sys.exit(1) | 815 sys.exit(1) |
| 798 | 816 |
| 799 | 817 |
| 800 def _AttemptRebase(self, upstream, files, options, newbase=None, | 818 def _AttemptRebase(self, upstream, files, options, newbase=None, |
| 801 branch=None, printed_path=False, merge=False): | 819 branch=None, printed_path=False, merge=False): |
| 802 """Attempt to rebase onto either upstream or, if specified, newbase.""" | 820 """Attempt to rebase onto either upstream or, if specified, newbase.""" |
| 803 if files is not None: | 821 if files is not None: |
| 804 files.extend(self._Capture(['diff', upstream, '--name-only']).split()) | 822 files.extend(self._Capture(['diff', upstream, '--name-only']).split()) |
| 805 revision = upstream | 823 revision = upstream |
| 806 if newbase: | 824 if newbase: |
| 807 revision = newbase | 825 revision = newbase |
| 808 action = 'merge' if merge else 'rebase' | 826 action = 'merge' if merge else 'rebase' |
| 809 if not printed_path: | 827 if not printed_path: |
| 810 print('\n_____ %s : Attempting %s onto %s...' % ( | 828 self.Print('_____ %s : Attempting %s onto %s...' % ( |
| 811 self.relpath, action, revision)) | 829 self.relpath, action, revision)) |
| 812 printed_path = True | 830 printed_path = True |
| 813 else: | 831 else: |
| 814 print('Attempting %s onto %s...' % (action, revision)) | 832 self.Print('Attempting %s onto %s...' % (action, revision)) |
| 815 | 833 |
| 816 if merge: | 834 if merge: |
| 817 merge_output = self._Capture(['merge', revision]) | 835 merge_output = self._Capture(['merge', revision]) |
| 818 if options.verbose: | 836 if options.verbose: |
| 819 print(merge_output) | 837 self.Print(merge_output) |
| 820 return | 838 return |
| 821 | 839 |
| 822 # Build the rebase command here using the args | 840 # Build the rebase command here using the args |
| 823 # git rebase [options] [--onto <newbase>] <upstream> [<branch>] | 841 # git rebase [options] [--onto <newbase>] <upstream> [<branch>] |
| 824 rebase_cmd = ['rebase'] | 842 rebase_cmd = ['rebase'] |
| 825 if options.verbose: | 843 if options.verbose: |
| 826 rebase_cmd.append('--verbose') | 844 rebase_cmd.append('--verbose') |
| 827 if newbase: | 845 if newbase: |
| 828 rebase_cmd.extend(['--onto', newbase]) | 846 rebase_cmd.extend(['--onto', newbase]) |
| 829 rebase_cmd.append(upstream) | 847 rebase_cmd.append(upstream) |
| (...skipping 15 matching lines...) Expand all Loading... |
| 845 if re.match(r'yes|y', rebase_action, re.I): | 863 if re.match(r'yes|y', rebase_action, re.I): |
| 846 self._Run(['reset', '--hard', 'HEAD'], options) | 864 self._Run(['reset', '--hard', 'HEAD'], options) |
| 847 # Should this be recursive? | 865 # Should this be recursive? |
| 848 rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path) | 866 rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path) |
| 849 break | 867 break |
| 850 elif re.match(r'quit|q', rebase_action, re.I): | 868 elif re.match(r'quit|q', rebase_action, re.I): |
| 851 raise gclient_utils.Error("Please merge or rebase manually\n" | 869 raise gclient_utils.Error("Please merge or rebase manually\n" |
| 852 "cd %s && git " % self.checkout_path | 870 "cd %s && git " % self.checkout_path |
| 853 + "%s" % ' '.join(rebase_cmd)) | 871 + "%s" % ' '.join(rebase_cmd)) |
| 854 elif re.match(r'show|s', rebase_action, re.I): | 872 elif re.match(r'show|s', rebase_action, re.I): |
| 855 print('\n%s' % e.stderr.strip()) | 873 self.Print('%s' % e.stderr.strip()) |
| 856 continue | 874 continue |
| 857 else: | 875 else: |
| 858 gclient_utils.Error("Input not recognized") | 876 gclient_utils.Error("Input not recognized") |
| 859 continue | 877 continue |
| 860 elif re.search(r'^CONFLICT', e.stdout, re.M): | 878 elif re.search(r'^CONFLICT', e.stdout, re.M): |
| 861 raise gclient_utils.Error("Conflict while rebasing this branch.\n" | 879 raise gclient_utils.Error("Conflict while rebasing this branch.\n" |
| 862 "Fix the conflict and run gclient again.\n" | 880 "Fix the conflict and run gclient again.\n" |
| 863 "See 'man git-rebase' for details.\n") | 881 "See 'man git-rebase' for details.\n") |
| 864 else: | 882 else: |
| 865 print(e.stdout.strip()) | 883 self.Print(e.stdout.strip()) |
| 866 print('Rebase produced error output:\n%s' % e.stderr.strip()) | 884 self.Print('Rebase produced error output:\n%s' % e.stderr.strip()) |
| 867 raise gclient_utils.Error("Unrecognized error, please merge or rebase " | 885 raise gclient_utils.Error("Unrecognized error, please merge or rebase " |
| 868 "manually.\ncd %s && git " % | 886 "manually.\ncd %s && git " % |
| 869 self.checkout_path | 887 self.checkout_path |
| 870 + "%s" % ' '.join(rebase_cmd)) | 888 + "%s" % ' '.join(rebase_cmd)) |
| 871 | 889 |
| 872 print(rebase_output.strip()) | 890 self.Print(rebase_output.strip()) |
| 873 if not options.verbose: | 891 if not options.verbose: |
| 874 # Make the output a little prettier. It's nice to have some | 892 # Make the output a little prettier. It's nice to have some |
| 875 # whitespace between projects when syncing. | 893 # whitespace between projects when syncing. |
| 876 print('') | 894 self.Print('') |
| 877 | 895 |
| 878 @staticmethod | 896 @staticmethod |
| 879 def _CheckMinVersion(min_version): | 897 def _CheckMinVersion(min_version): |
| 880 (ok, current_version) = scm.GIT.AssertVersion(min_version) | 898 (ok, current_version) = scm.GIT.AssertVersion(min_version) |
| 881 if not ok: | 899 if not ok: |
| 882 raise gclient_utils.Error('git version %s < minimum required %s' % | 900 raise gclient_utils.Error('git version %s < minimum required %s' % |
| 883 (current_version, min_version)) | 901 (current_version, min_version)) |
| 884 | 902 |
| 885 def _IsRebasing(self): | 903 def _IsRebasing(self): |
| 886 # Check for any of REBASE-i/REBASE-m/REBASE/AM. Unfortunately git doesn't | 904 # Check for any of REBASE-i/REBASE-m/REBASE/AM. Unfortunately git doesn't |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 925 raise gclient_utils.Error('\n____ %s%s\n' | 943 raise gclient_utils.Error('\n____ %s%s\n' |
| 926 '\tAlready in a conflict, i.e. (no branch).\n' | 944 '\tAlready in a conflict, i.e. (no branch).\n' |
| 927 '\tFix the conflict and run gclient again.\n' | 945 '\tFix the conflict and run gclient again.\n' |
| 928 '\tOr to abort run:\n\t\tgit-rebase --abort\n' | 946 '\tOr to abort run:\n\t\tgit-rebase --abort\n' |
| 929 '\tSee man git-rebase for details.\n' | 947 '\tSee man git-rebase for details.\n' |
| 930 % (self.relpath, rev_str)) | 948 % (self.relpath, rev_str)) |
| 931 # Let's just save off the commit so we can proceed. | 949 # Let's just save off the commit so we can proceed. |
| 932 name = ('saved-by-gclient-' + | 950 name = ('saved-by-gclient-' + |
| 933 self._Capture(['rev-parse', '--short', 'HEAD'])) | 951 self._Capture(['rev-parse', '--short', 'HEAD'])) |
| 934 self._Capture(['branch', '-f', name]) | 952 self._Capture(['branch', '-f', name]) |
| 935 print('\n_____ found an unreferenced commit and saved it as \'%s\'' % | 953 self.Print('_____ found an unreferenced commit and saved it as \'%s\'' % |
| 936 name) | 954 name) |
| 937 | 955 |
| 938 def _GetCurrentBranch(self): | 956 def _GetCurrentBranch(self): |
| 939 # Returns name of current branch or None for detached HEAD | 957 # Returns name of current branch or None for detached HEAD |
| 940 branch = self._Capture(['rev-parse', '--abbrev-ref=strict', 'HEAD']) | 958 branch = self._Capture(['rev-parse', '--abbrev-ref=strict', 'HEAD']) |
| 941 if branch == 'HEAD': | 959 if branch == 'HEAD': |
| 942 return None | 960 return None |
| 943 return branch | 961 return branch |
| 944 | 962 |
| 945 def _Capture(self, args, cwd=None): | 963 def _Capture(self, args, cwd=None, **kwargs): |
| 946 return subprocess2.check_output( | 964 kwargs.setdefault('cwd', self.checkout_path) |
| 947 ['git'] + args, | 965 kwargs.setdefault('stderr', subprocess2.PIPE) |
| 948 stderr=subprocess2.VOID, | 966 return subprocess2.check_output(['git'] + args, **kwargs).strip() |
| 949 cwd=cwd or self.checkout_path).strip() | |
| 950 | 967 |
| 951 def _UpdateBranchHeads(self, options, fetch=False): | 968 def _UpdateBranchHeads(self, options, fetch=False): |
| 952 """Adds, and optionally fetches, "branch-heads" refspecs if requested.""" | 969 """Adds, and optionally fetches, "branch-heads" refspecs if requested.""" |
| 953 if hasattr(options, 'with_branch_heads') and options.with_branch_heads: | 970 if hasattr(options, 'with_branch_heads') and options.with_branch_heads: |
| 954 config_cmd = ['config', 'remote.%s.fetch' % self.remote, | 971 config_cmd = ['config', 'remote.%s.fetch' % self.remote, |
| 955 '+refs/branch-heads/*:refs/remotes/branch-heads/*', | 972 '+refs/branch-heads/*:refs/remotes/branch-heads/*', |
| 956 '^\\+refs/branch-heads/\\*:.*$'] | 973 '^\\+refs/branch-heads/\\*:.*$'] |
| 957 self._Run(config_cmd, options) | 974 self._Run(config_cmd, options) |
| 958 if fetch: | 975 if fetch: |
| 959 cfg = gclient_utils.DefaultIndexPackConfig(self.url) | 976 cfg = gclient_utils.DefaultIndexPackConfig(self.url) |
| 960 fetch_cmd = cfg + ['fetch', self.remote] | 977 fetch_cmd = cfg + ['fetch', self.remote] |
| 961 if options.verbose: | 978 if options.verbose: |
| 962 fetch_cmd.append('--verbose') | 979 fetch_cmd.append('--verbose') |
| 963 self._Run(fetch_cmd, options, retry=True) | 980 self._Run(fetch_cmd, options, retry=True) |
| 964 | 981 |
| 965 def _Run(self, args, options, **kwargs): | 982 def _Run(self, args, options, **kwargs): |
| 966 kwargs.setdefault('cwd', self.checkout_path) | 983 cwd = kwargs.setdefault('cwd', self.checkout_path) |
| 967 git_filter = not options.verbose | 984 kwargs.setdefault('stdout', self.out_fh) |
| 968 if git_filter: | 985 filter_kwargs = { 'time_throttle': 10, 'out_fh': self.out_fh } |
| 969 kwargs['filter_fn'] = gclient_utils.GitFilter(kwargs.get('filter_fn')) | 986 if self.out_cb: |
| 970 kwargs.setdefault('print_stdout', False) | 987 filter_kwargs['predicate'] = self.out_cb |
| 971 # Don't prompt for passwords; just fail quickly and noisily. | 988 kwargs['filter_fn'] = git_filter = gclient_utils.GitFilter(**filter_kwargs) |
| 972 # By default, git will use an interactive terminal prompt when a username/ | 989 kwargs.setdefault('print_stdout', False) |
| 973 # password is needed. That shouldn't happen in the chromium workflow, | 990 # Don't prompt for passwords; just fail quickly and noisily. |
| 974 # and if it does, then gclient may hide the prompt in the midst of a flood | 991 # By default, git will use an interactive terminal prompt when a username/ |
| 975 # of terminal spew. The only indication that something has gone wrong | 992 # password is needed. That shouldn't happen in the chromium workflow, |
| 976 # will be when gclient hangs unresponsively. Instead, we disable the | 993 # and if it does, then gclient may hide the prompt in the midst of a flood |
| 977 # password prompt and simply allow git to fail noisily. The error | 994 # of terminal spew. The only indication that something has gone wrong |
| 978 # message produced by git will be copied to gclient's output. | 995 # will be when gclient hangs unresponsively. Instead, we disable the |
| 979 env = kwargs.get('env') or kwargs.setdefault('env', os.environ.copy()) | 996 # password prompt and simply allow git to fail noisily. The error |
| 980 env.setdefault('GIT_ASKPASS', 'true') | 997 # message produced by git will be copied to gclient's output. |
| 981 env.setdefault('SSH_ASKPASS', 'true') | 998 env = kwargs.get('env') or kwargs.setdefault('env', os.environ.copy()) |
| 982 else: | 999 env.setdefault('GIT_ASKPASS', 'true') |
| 983 kwargs.setdefault('print_stdout', True) | 1000 env.setdefault('SSH_ASKPASS', 'true') |
| 1001 |
| 984 cmd = ['git'] + args | 1002 cmd = ['git'] + args |
| 985 return gclient_utils.CheckCallAndFilterAndHeader(cmd, **kwargs) | 1003 header = "running '%s' in '%s'" % (' '.join(cmd), cwd) |
| 1004 git_filter(header) |
| 1005 return gclient_utils.CheckCallAndFilter(cmd, **kwargs) |
| 986 | 1006 |
| 987 | 1007 |
| 988 class SVNWrapper(SCMWrapper): | 1008 class SVNWrapper(SCMWrapper): |
| 989 """ Wrapper for SVN """ | 1009 """ Wrapper for SVN """ |
| 990 name = 'svn' | 1010 name = 'svn' |
| 991 | 1011 |
| 992 @staticmethod | 1012 @staticmethod |
| 993 def BinaryExists(): | 1013 def BinaryExists(): |
| 994 """Returns true if the command exists.""" | 1014 """Returns true if the command exists.""" |
| 995 try: | 1015 try: |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1025 def pack(self, _options, args, _file_list): | 1045 def pack(self, _options, args, _file_list): |
| 1026 """Generates a patch file which can be applied to the root of the | 1046 """Generates a patch file which can be applied to the root of the |
| 1027 repository.""" | 1047 repository.""" |
| 1028 if not os.path.isdir(self.checkout_path): | 1048 if not os.path.isdir(self.checkout_path): |
| 1029 raise gclient_utils.Error('Directory %s is not present.' % | 1049 raise gclient_utils.Error('Directory %s is not present.' % |
| 1030 self.checkout_path) | 1050 self.checkout_path) |
| 1031 gclient_utils.CheckCallAndFilter( | 1051 gclient_utils.CheckCallAndFilter( |
| 1032 ['svn', 'diff', '-x', '--ignore-eol-style'] + args, | 1052 ['svn', 'diff', '-x', '--ignore-eol-style'] + args, |
| 1033 cwd=self.checkout_path, | 1053 cwd=self.checkout_path, |
| 1034 print_stdout=False, | 1054 print_stdout=False, |
| 1035 filter_fn=SvnDiffFilterer(self.relpath).Filter) | 1055 filter_fn=SvnDiffFilterer(self.relpath).Filter, print_func=self.Print) |
| 1036 | 1056 |
| 1037 def update(self, options, args, file_list): | 1057 def update(self, options, args, file_list): |
| 1038 """Runs svn to update or transparently checkout the working copy. | 1058 """Runs svn to update or transparently checkout the working copy. |
| 1039 | 1059 |
| 1040 All updated files will be appended to file_list. | 1060 All updated files will be appended to file_list. |
| 1041 | 1061 |
| 1042 Raises: | 1062 Raises: |
| 1043 Error: if can't get URL for relative path. | 1063 Error: if can't get URL for relative path. |
| 1044 """ | 1064 """ |
| 1045 # Only update if git or hg is not controlling the directory. | 1065 # Only update if git or hg is not controlling the directory. |
| 1046 git_path = os.path.join(self.checkout_path, '.git') | 1066 git_path = os.path.join(self.checkout_path, '.git') |
| 1047 if os.path.exists(git_path): | 1067 if os.path.exists(git_path): |
| 1048 print('________ found .git directory; skipping %s' % self.relpath) | 1068 self.Print('________ found .git directory; skipping %s' % self.relpath) |
| 1049 return | 1069 return |
| 1050 | 1070 |
| 1051 hg_path = os.path.join(self.checkout_path, '.hg') | 1071 hg_path = os.path.join(self.checkout_path, '.hg') |
| 1052 if os.path.exists(hg_path): | 1072 if os.path.exists(hg_path): |
| 1053 print('________ found .hg directory; skipping %s' % self.relpath) | 1073 self.Print('________ found .hg directory; skipping %s' % self.relpath) |
| 1054 return | 1074 return |
| 1055 | 1075 |
| 1056 if args: | 1076 if args: |
| 1057 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) | 1077 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) |
| 1058 | 1078 |
| 1059 # revision is the revision to match. It is None if no revision is specified, | 1079 # revision is the revision to match. It is None if no revision is specified, |
| 1060 # i.e. the 'deps ain't pinned'. | 1080 # i.e. the 'deps ain't pinned'. |
| 1061 url, revision = gclient_utils.SplitUrlRevision(self.url) | 1081 url, revision = gclient_utils.SplitUrlRevision(self.url) |
| 1062 # Keep the original unpinned url for reference in case the repo is switched. | 1082 # Keep the original unpinned url for reference in case the repo is switched. |
| 1063 base_url = url | 1083 base_url = url |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1079 rev_str = '' | 1099 rev_str = '' |
| 1080 | 1100 |
| 1081 # Get the existing scm url and the revision number of the current checkout. | 1101 # Get the existing scm url and the revision number of the current checkout. |
| 1082 exists = os.path.exists(self.checkout_path) | 1102 exists = os.path.exists(self.checkout_path) |
| 1083 if exists and managed: | 1103 if exists and managed: |
| 1084 try: | 1104 try: |
| 1085 from_info = scm.SVN.CaptureLocalInfo( | 1105 from_info = scm.SVN.CaptureLocalInfo( |
| 1086 [], os.path.join(self.checkout_path, '.')) | 1106 [], os.path.join(self.checkout_path, '.')) |
| 1087 except (gclient_utils.Error, subprocess2.CalledProcessError): | 1107 except (gclient_utils.Error, subprocess2.CalledProcessError): |
| 1088 if options.reset and options.delete_unversioned_trees: | 1108 if options.reset and options.delete_unversioned_trees: |
| 1089 print 'Removing troublesome path %s' % self.checkout_path | 1109 self.Print('Removing troublesome path %s' % self.checkout_path) |
| 1090 gclient_utils.rmtree(self.checkout_path) | 1110 gclient_utils.rmtree(self.checkout_path) |
| 1091 exists = False | 1111 exists = False |
| 1092 else: | 1112 else: |
| 1093 msg = ('Can\'t update/checkout %s if an unversioned directory is ' | 1113 msg = ('Can\'t update/checkout %s if an unversioned directory is ' |
| 1094 'present. Delete the directory and try again.') | 1114 'present. Delete the directory and try again.') |
| 1095 raise gclient_utils.Error(msg % self.checkout_path) | 1115 raise gclient_utils.Error(msg % self.checkout_path) |
| 1096 | 1116 |
| 1097 BASE_URLS = { | 1117 BASE_URLS = { |
| 1098 '/chrome/trunk/src': 'gs://chromium-svn-checkout/chrome/', | 1118 '/chrome/trunk/src': 'gs://chromium-svn-checkout/chrome/', |
| 1099 '/blink/trunk': 'gs://chromium-svn-checkout/blink/', | 1119 '/blink/trunk': 'gs://chromium-svn-checkout/blink/', |
| (...skipping 20 matching lines...) Expand all Loading... |
| 1120 gsutil = download_from_google_storage.Gsutil( | 1140 gsutil = download_from_google_storage.Gsutil( |
| 1121 GSUTIL_DEFAULT_PATH, boto_path=os.devnull) | 1141 GSUTIL_DEFAULT_PATH, boto_path=os.devnull) |
| 1122 | 1142 |
| 1123 gs_path = BASE_URLS[base_path] | 1143 gs_path = BASE_URLS[base_path] |
| 1124 _, out, _ = gsutil.check_call('ls', gs_path) | 1144 _, out, _ = gsutil.check_call('ls', gs_path) |
| 1125 # So that we can get the most recent revision. | 1145 # So that we can get the most recent revision. |
| 1126 sorted_items = sorted(out.splitlines()) | 1146 sorted_items = sorted(out.splitlines()) |
| 1127 latest_checkout = sorted_items[-1] | 1147 latest_checkout = sorted_items[-1] |
| 1128 | 1148 |
| 1129 tempdir = tempfile.mkdtemp() | 1149 tempdir = tempfile.mkdtemp() |
| 1130 print 'Downloading %s...' % latest_checkout | 1150 self.Print('Downloading %s...' % latest_checkout) |
| 1131 code, out, err = gsutil.check_call('cp', latest_checkout, tempdir) | 1151 code, out, err = gsutil.check_call('cp', latest_checkout, tempdir) |
| 1132 if code: | 1152 if code: |
| 1133 print '%s\n%s' % (out, err) | 1153 self.Print('%s\n%s' % (out, err)) |
| 1134 raise Exception() | 1154 raise Exception() |
| 1135 filename = latest_checkout.split('/')[-1] | 1155 filename = latest_checkout.split('/')[-1] |
| 1136 tarball = os.path.join(tempdir, filename) | 1156 tarball = os.path.join(tempdir, filename) |
| 1137 print 'Unpacking into %s...' % self.checkout_path | 1157 self.Print('Unpacking into %s...' % self.checkout_path) |
| 1138 gclient_utils.safe_makedirs(self.checkout_path) | 1158 gclient_utils.safe_makedirs(self.checkout_path) |
| 1139 # TODO(hinoka): Use 7z for windows. | 1159 # TODO(hinoka): Use 7z for windows. |
| 1140 cmd = ['tar', '--extract', '--ungzip', | 1160 cmd = ['tar', '--extract', '--ungzip', |
| 1141 '--directory', self.checkout_path, | 1161 '--directory', self.checkout_path, |
| 1142 '--file', tarball] | 1162 '--file', tarball] |
| 1143 gclient_utils.CheckCallAndFilter( | 1163 gclient_utils.CheckCallAndFilter( |
| 1144 cmd, stdout=sys.stdout, print_stdout=True) | 1164 cmd, stdout=sys.stdout, print_stdout=True) |
| 1145 | 1165 |
| 1146 print 'Deleting temp file' | 1166 self.Print('Deleting temp file') |
| 1147 gclient_utils.rmtree(tempdir) | 1167 gclient_utils.rmtree(tempdir) |
| 1148 | 1168 |
| 1149 # Rewrite the repository root to match. | 1169 # Rewrite the repository root to match. |
| 1150 tarball_url = scm.SVN.CaptureLocalInfo( | 1170 tarball_url = scm.SVN.CaptureLocalInfo( |
| 1151 ['.'], self.checkout_path)['Repository Root'] | 1171 ['.'], self.checkout_path)['Repository Root'] |
| 1152 tarball_parsed = urlparse.urlparse(tarball_url) | 1172 tarball_parsed = urlparse.urlparse(tarball_url) |
| 1153 tarball_root = '%s://%s' % (tarball_parsed.scheme, | 1173 tarball_root = '%s://%s' % (tarball_parsed.scheme, |
| 1154 tarball_parsed.netloc) | 1174 tarball_parsed.netloc) |
| 1155 | 1175 |
| 1156 if tarball_root != local_root: | 1176 if tarball_root != local_root: |
| 1157 print 'Switching repository root to %s' % local_root | 1177 self.Print('Switching repository root to %s' % local_root) |
| 1158 self._Run(['switch', '--relocate', tarball_root, | 1178 self._Run(['switch', '--relocate', tarball_root, |
| 1159 local_root, self.checkout_path], | 1179 local_root, self.checkout_path], |
| 1160 options) | 1180 options) |
| 1161 except Exception as e: | 1181 except Exception as e: |
| 1162 print 'We tried to get a source tarball but failed.' | 1182 self.Print('We tried to get a source tarball but failed.') |
| 1163 print 'Resuming normal operations.' | 1183 self.Print('Resuming normal operations.') |
| 1164 print str(e) | 1184 self.Print(str(e)) |
| 1165 | 1185 |
| 1166 gclient_utils.safe_makedirs(os.path.dirname(self.checkout_path)) | 1186 gclient_utils.safe_makedirs(os.path.dirname(self.checkout_path)) |
| 1167 # We need to checkout. | 1187 # We need to checkout. |
| 1168 command = ['checkout', url, self.checkout_path] | 1188 command = ['checkout', url, self.checkout_path] |
| 1169 command = self._AddAdditionalUpdateFlags(command, options, revision) | 1189 command = self._AddAdditionalUpdateFlags(command, options, revision) |
| 1170 self._RunAndGetFileList(command, options, file_list, self._root_dir) | 1190 self._RunAndGetFileList(command, options, file_list, self._root_dir) |
| 1171 return self.Svnversion() | 1191 return self.Svnversion() |
| 1172 | 1192 |
| 1173 if not managed: | 1193 if not managed: |
| 1174 print ('________ unmanaged solution; skipping %s' % self.relpath) | 1194 self.Print(('________ unmanaged solution; skipping %s' % self.relpath)) |
| 1175 return self.Svnversion() | 1195 return self.Svnversion() |
| 1176 | 1196 |
| 1177 if 'URL' not in from_info: | 1197 if 'URL' not in from_info: |
| 1178 raise gclient_utils.Error( | 1198 raise gclient_utils.Error( |
| 1179 ('gclient is confused. Couldn\'t get the url for %s.\n' | 1199 ('gclient is confused. Couldn\'t get the url for %s.\n' |
| 1180 'Try using @unmanaged.\n%s') % ( | 1200 'Try using @unmanaged.\n%s') % ( |
| 1181 self.checkout_path, from_info)) | 1201 self.checkout_path, from_info)) |
| 1182 | 1202 |
| 1183 # Look for locked directories. | 1203 # Look for locked directories. |
| 1184 dir_info = scm.SVN.CaptureStatus( | 1204 dir_info = scm.SVN.CaptureStatus( |
| 1185 None, os.path.join(self.checkout_path, '.')) | 1205 None, os.path.join(self.checkout_path, '.')) |
| 1186 if any(d[0][2] == 'L' for d in dir_info): | 1206 if any(d[0][2] == 'L' for d in dir_info): |
| 1187 try: | 1207 try: |
| 1188 self._Run(['cleanup', self.checkout_path], options) | 1208 self._Run(['cleanup', self.checkout_path], options) |
| 1189 except subprocess2.CalledProcessError, e: | 1209 except subprocess2.CalledProcessError, e: |
| 1190 # Get the status again, svn cleanup may have cleaned up at least | 1210 # Get the status again, svn cleanup may have cleaned up at least |
| 1191 # something. | 1211 # something. |
| 1192 dir_info = scm.SVN.CaptureStatus( | 1212 dir_info = scm.SVN.CaptureStatus( |
| 1193 None, os.path.join(self.checkout_path, '.')) | 1213 None, os.path.join(self.checkout_path, '.')) |
| 1194 | 1214 |
| 1195 # Try to fix the failures by removing troublesome files. | 1215 # Try to fix the failures by removing troublesome files. |
| 1196 for d in dir_info: | 1216 for d in dir_info: |
| 1197 if d[0][2] == 'L': | 1217 if d[0][2] == 'L': |
| 1198 if d[0][0] == '!' and options.force: | 1218 if d[0][0] == '!' and options.force: |
| 1199 # We don't pass any files/directories to CaptureStatus and set | 1219 # We don't pass any files/directories to CaptureStatus and set |
| 1200 # cwd=self.checkout_path, so we should get relative paths here. | 1220 # cwd=self.checkout_path, so we should get relative paths here. |
| 1201 assert not os.path.isabs(d[1]) | 1221 assert not os.path.isabs(d[1]) |
| 1202 path_to_remove = os.path.normpath( | 1222 path_to_remove = os.path.normpath( |
| 1203 os.path.join(self.checkout_path, d[1])) | 1223 os.path.join(self.checkout_path, d[1])) |
| 1204 print 'Removing troublesome path %s' % path_to_remove | 1224 self.Print('Removing troublesome path %s' % path_to_remove) |
| 1205 gclient_utils.rmtree(path_to_remove) | 1225 gclient_utils.rmtree(path_to_remove) |
| 1206 else: | 1226 else: |
| 1207 print 'Not removing troublesome path %s automatically.' % d[1] | 1227 self.Print( |
| 1228 'Not removing troublesome path %s automatically.' % d[1]) |
| 1208 if d[0][0] == '!': | 1229 if d[0][0] == '!': |
| 1209 print 'You can pass --force to enable automatic removal.' | 1230 self.Print('You can pass --force to enable automatic removal.') |
| 1210 raise e | 1231 raise e |
| 1211 | 1232 |
| 1212 # Retrieve the current HEAD version because svn is slow at null updates. | 1233 # Retrieve the current HEAD version because svn is slow at null updates. |
| 1213 if options.manually_grab_svn_rev and not revision: | 1234 if options.manually_grab_svn_rev and not revision: |
| 1214 from_info_live = scm.SVN.CaptureRemoteInfo(from_info['URL']) | 1235 from_info_live = scm.SVN.CaptureRemoteInfo(from_info['URL']) |
| 1215 revision = str(from_info_live['Revision']) | 1236 revision = str(from_info_live['Revision']) |
| 1216 rev_str = ' at %s' % revision | 1237 rev_str = ' at %s' % revision |
| 1217 | 1238 |
| 1218 if from_info['URL'] != base_url: | 1239 if from_info['URL'] != base_url: |
| 1219 # The repository url changed, need to switch. | 1240 # The repository url changed, need to switch. |
| 1220 try: | 1241 try: |
| 1221 to_info = scm.SVN.CaptureRemoteInfo(url) | 1242 to_info = scm.SVN.CaptureRemoteInfo(url) |
| 1222 except (gclient_utils.Error, subprocess2.CalledProcessError): | 1243 except (gclient_utils.Error, subprocess2.CalledProcessError): |
| 1223 # The url is invalid or the server is not accessible, it's safer to bail | 1244 # The url is invalid or the server is not accessible, it's safer to bail |
| 1224 # out right now. | 1245 # out right now. |
| 1225 raise gclient_utils.Error('This url is unreachable: %s' % url) | 1246 raise gclient_utils.Error('This url is unreachable: %s' % url) |
| 1226 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) | 1247 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) |
| 1227 and (from_info['UUID'] == to_info['UUID'])) | 1248 and (from_info['UUID'] == to_info['UUID'])) |
| 1228 if can_switch: | 1249 if can_switch: |
| 1229 print('\n_____ relocating %s to a new checkout' % self.relpath) | 1250 self.Print('_____ relocating %s to a new checkout' % self.relpath) |
| 1230 # We have different roots, so check if we can switch --relocate. | 1251 # We have different roots, so check if we can switch --relocate. |
| 1231 # Subversion only permits this if the repository UUIDs match. | 1252 # Subversion only permits this if the repository UUIDs match. |
| 1232 # Perform the switch --relocate, then rewrite the from_url | 1253 # Perform the switch --relocate, then rewrite the from_url |
| 1233 # to reflect where we "are now." (This is the same way that | 1254 # to reflect where we "are now." (This is the same way that |
| 1234 # Subversion itself handles the metadata when switch --relocate | 1255 # Subversion itself handles the metadata when switch --relocate |
| 1235 # is used.) This makes the checks below for whether we | 1256 # is used.) This makes the checks below for whether we |
| 1236 # can update to a revision or have to switch to a different | 1257 # can update to a revision or have to switch to a different |
| 1237 # branch work as expected. | 1258 # branch work as expected. |
| 1238 # TODO(maruel): TEST ME ! | 1259 # TODO(maruel): TEST ME ! |
| 1239 command = ['switch', '--relocate', | 1260 command = ['switch', '--relocate', |
| 1240 from_info['Repository Root'], | 1261 from_info['Repository Root'], |
| 1241 to_info['Repository Root'], | 1262 to_info['Repository Root'], |
| 1242 self.relpath] | 1263 self.relpath] |
| 1243 self._Run(command, options, cwd=self._root_dir) | 1264 self._Run(command, options, cwd=self._root_dir) |
| 1244 from_info['URL'] = from_info['URL'].replace( | 1265 from_info['URL'] = from_info['URL'].replace( |
| 1245 from_info['Repository Root'], | 1266 from_info['Repository Root'], |
| 1246 to_info['Repository Root']) | 1267 to_info['Repository Root']) |
| 1247 else: | 1268 else: |
| 1248 if not options.force and not options.reset: | 1269 if not options.force and not options.reset: |
| 1249 # Look for local modifications but ignore unversioned files. | 1270 # Look for local modifications but ignore unversioned files. |
| 1250 for status in scm.SVN.CaptureStatus(None, self.checkout_path): | 1271 for status in scm.SVN.CaptureStatus(None, self.checkout_path): |
| 1251 if status[0][0] != '?': | 1272 if status[0][0] != '?': |
| 1252 raise gclient_utils.Error( | 1273 raise gclient_utils.Error( |
| 1253 ('Can\'t switch the checkout to %s; UUID don\'t match and ' | 1274 ('Can\'t switch the checkout to %s; UUID don\'t match and ' |
| 1254 'there is local changes in %s. Delete the directory and ' | 1275 'there is local changes in %s. Delete the directory and ' |
| 1255 'try again.') % (url, self.checkout_path)) | 1276 'try again.') % (url, self.checkout_path)) |
| 1256 # Ok delete it. | 1277 # Ok delete it. |
| 1257 print('\n_____ switching %s to a new checkout' % self.relpath) | 1278 self.Print('_____ switching %s to a new checkout' % self.relpath) |
| 1258 gclient_utils.rmtree(self.checkout_path) | 1279 gclient_utils.rmtree(self.checkout_path) |
| 1259 # We need to checkout. | 1280 # We need to checkout. |
| 1260 command = ['checkout', url, self.checkout_path] | 1281 command = ['checkout', url, self.checkout_path] |
| 1261 command = self._AddAdditionalUpdateFlags(command, options, revision) | 1282 command = self._AddAdditionalUpdateFlags(command, options, revision) |
| 1262 self._RunAndGetFileList(command, options, file_list, self._root_dir) | 1283 self._RunAndGetFileList(command, options, file_list, self._root_dir) |
| 1263 return self.Svnversion() | 1284 return self.Svnversion() |
| 1264 | 1285 |
| 1265 # If the provided url has a revision number that matches the revision | 1286 # If the provided url has a revision number that matches the revision |
| 1266 # number of the existing directory, then we don't need to bother updating. | 1287 # number of the existing directory, then we don't need to bother updating. |
| 1267 if not options.force and str(from_info['Revision']) == revision: | 1288 if not options.force and str(from_info['Revision']) == revision: |
| 1268 if options.verbose or not forced_revision: | 1289 if options.verbose or not forced_revision: |
| 1269 print('\n_____ %s%s' % (self.relpath, rev_str)) | 1290 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
| 1270 else: | 1291 else: |
| 1271 command = ['update', self.checkout_path] | 1292 command = ['update', self.checkout_path] |
| 1272 command = self._AddAdditionalUpdateFlags(command, options, revision) | 1293 command = self._AddAdditionalUpdateFlags(command, options, revision) |
| 1273 self._RunAndGetFileList(command, options, file_list, self._root_dir) | 1294 self._RunAndGetFileList(command, options, file_list, self._root_dir) |
| 1274 | 1295 |
| 1275 # If --reset and --delete_unversioned_trees are specified, remove any | 1296 # If --reset and --delete_unversioned_trees are specified, remove any |
| 1276 # untracked files and directories. | 1297 # untracked files and directories. |
| 1277 if options.reset and options.delete_unversioned_trees: | 1298 if options.reset and options.delete_unversioned_trees: |
| 1278 for status in scm.SVN.CaptureStatus(None, self.checkout_path): | 1299 for status in scm.SVN.CaptureStatus(None, self.checkout_path): |
| 1279 full_path = os.path.join(self.checkout_path, status[1]) | 1300 full_path = os.path.join(self.checkout_path, status[1]) |
| 1280 if (status[0][0] == '?' | 1301 if (status[0][0] == '?' |
| 1281 and os.path.isdir(full_path) | 1302 and os.path.isdir(full_path) |
| 1282 and not os.path.islink(full_path)): | 1303 and not os.path.islink(full_path)): |
| 1283 print('\n_____ removing unversioned directory %s' % status[1]) | 1304 self.Print('_____ removing unversioned directory %s' % status[1]) |
| 1284 gclient_utils.rmtree(full_path) | 1305 gclient_utils.rmtree(full_path) |
| 1285 return self.Svnversion() | 1306 return self.Svnversion() |
| 1286 | 1307 |
| 1287 def updatesingle(self, options, args, file_list): | 1308 def updatesingle(self, options, args, file_list): |
| 1288 filename = args.pop() | 1309 filename = args.pop() |
| 1289 if scm.SVN.AssertVersion("1.5")[0]: | 1310 if scm.SVN.AssertVersion("1.5")[0]: |
| 1290 if not os.path.exists(os.path.join(self.checkout_path, '.svn')): | 1311 if not os.path.exists(os.path.join(self.checkout_path, '.svn')): |
| 1291 # Create an empty checkout and then update the one file we want. Future | 1312 # Create an empty checkout and then update the one file we want. Future |
| 1292 # operations will only apply to the one file we checked out. | 1313 # operations will only apply to the one file we checked out. |
| 1293 command = ["checkout", "--depth", "empty", self.url, self.checkout_path] | 1314 command = ["checkout", "--depth", "empty", self.url, self.checkout_path] |
| (...skipping 22 matching lines...) Expand all Loading... |
| 1316 """Reverts local modifications. Subversion specific. | 1337 """Reverts local modifications. Subversion specific. |
| 1317 | 1338 |
| 1318 All reverted files will be appended to file_list, even if Subversion | 1339 All reverted files will be appended to file_list, even if Subversion |
| 1319 doesn't know about them. | 1340 doesn't know about them. |
| 1320 """ | 1341 """ |
| 1321 if not os.path.isdir(self.checkout_path): | 1342 if not os.path.isdir(self.checkout_path): |
| 1322 if os.path.exists(self.checkout_path): | 1343 if os.path.exists(self.checkout_path): |
| 1323 gclient_utils.rmtree(self.checkout_path) | 1344 gclient_utils.rmtree(self.checkout_path) |
| 1324 # svn revert won't work if the directory doesn't exist. It needs to | 1345 # svn revert won't work if the directory doesn't exist. It needs to |
| 1325 # checkout instead. | 1346 # checkout instead. |
| 1326 print('\n_____ %s is missing, synching instead' % self.relpath) | 1347 self.Print('_____ %s is missing, synching instead' % self.relpath) |
| 1327 # Don't reuse the args. | 1348 # Don't reuse the args. |
| 1328 return self.update(options, [], file_list) | 1349 return self.update(options, [], file_list) |
| 1329 | 1350 |
| 1330 if not os.path.isdir(os.path.join(self.checkout_path, '.svn')): | 1351 if not os.path.isdir(os.path.join(self.checkout_path, '.svn')): |
| 1331 if os.path.isdir(os.path.join(self.checkout_path, '.git')): | 1352 if os.path.isdir(os.path.join(self.checkout_path, '.git')): |
| 1332 print('________ found .git directory; skipping %s' % self.relpath) | 1353 self.Print('________ found .git directory; skipping %s' % self.relpath) |
| 1333 return | 1354 return |
| 1334 if os.path.isdir(os.path.join(self.checkout_path, '.hg')): | 1355 if os.path.isdir(os.path.join(self.checkout_path, '.hg')): |
| 1335 print('________ found .hg directory; skipping %s' % self.relpath) | 1356 self.Print('________ found .hg directory; skipping %s' % self.relpath) |
| 1336 return | 1357 return |
| 1337 if not options.force: | 1358 if not options.force: |
| 1338 raise gclient_utils.Error('Invalid checkout path, aborting') | 1359 raise gclient_utils.Error('Invalid checkout path, aborting') |
| 1339 print( | 1360 self.Print( |
| 1340 '\n_____ %s is not a valid svn checkout, synching instead' % | 1361 '\n_____ %s is not a valid svn checkout, synching instead' % |
| 1341 self.relpath) | 1362 self.relpath) |
| 1342 gclient_utils.rmtree(self.checkout_path) | 1363 gclient_utils.rmtree(self.checkout_path) |
| 1343 # Don't reuse the args. | 1364 # Don't reuse the args. |
| 1344 return self.update(options, [], file_list) | 1365 return self.update(options, [], file_list) |
| 1345 | 1366 |
| 1346 def printcb(file_status): | 1367 def printcb(file_status): |
| 1347 if file_list is not None: | 1368 if file_list is not None: |
| 1348 file_list.append(file_status[1]) | 1369 file_list.append(file_status[1]) |
| 1349 if logging.getLogger().isEnabledFor(logging.INFO): | 1370 if logging.getLogger().isEnabledFor(logging.INFO): |
| 1350 logging.info('%s%s' % (file_status[0], file_status[1])) | 1371 logging.info('%s%s' % (file_status[0], file_status[1])) |
| 1351 else: | 1372 else: |
| 1352 print(os.path.join(self.checkout_path, file_status[1])) | 1373 self.Print(os.path.join(self.checkout_path, file_status[1])) |
| 1353 scm.SVN.Revert(self.checkout_path, callback=printcb) | 1374 scm.SVN.Revert(self.checkout_path, callback=printcb) |
| 1354 | 1375 |
| 1355 # Revert() may delete the directory altogether. | 1376 # Revert() may delete the directory altogether. |
| 1356 if not os.path.isdir(self.checkout_path): | 1377 if not os.path.isdir(self.checkout_path): |
| 1357 # Don't reuse the args. | 1378 # Don't reuse the args. |
| 1358 return self.update(options, [], file_list) | 1379 return self.update(options, [], file_list) |
| 1359 | 1380 |
| 1360 try: | 1381 try: |
| 1361 # svn revert is so broken we don't even use it. Using | 1382 # svn revert is so broken we don't even use it. Using |
| 1362 # "svn up --revision BASE" achieve the same effect. | 1383 # "svn up --revision BASE" achieve the same effect. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1375 return None | 1396 return None |
| 1376 | 1397 |
| 1377 def runhooks(self, options, args, file_list): | 1398 def runhooks(self, options, args, file_list): |
| 1378 self.status(options, args, file_list) | 1399 self.status(options, args, file_list) |
| 1379 | 1400 |
| 1380 def status(self, options, args, file_list): | 1401 def status(self, options, args, file_list): |
| 1381 """Display status information.""" | 1402 """Display status information.""" |
| 1382 command = ['status'] + args | 1403 command = ['status'] + args |
| 1383 if not os.path.isdir(self.checkout_path): | 1404 if not os.path.isdir(self.checkout_path): |
| 1384 # svn status won't work if the directory doesn't exist. | 1405 # svn status won't work if the directory doesn't exist. |
| 1385 print(('\n________ couldn\'t run \'%s\' in \'%s\':\n' | 1406 self.Print(('\n________ couldn\'t run \'%s\' in \'%s\':\n' |
| 1386 'The directory does not exist.') % | 1407 'The directory does not exist.') % |
| 1387 (' '.join(command), self.checkout_path)) | 1408 (' '.join(command), self.checkout_path)) |
| 1388 # There's no file list to retrieve. | 1409 # There's no file list to retrieve. |
| 1389 else: | 1410 else: |
| 1390 self._RunAndGetFileList(command, options, file_list) | 1411 self._RunAndGetFileList(command, options, file_list) |
| 1391 | 1412 |
| 1392 def GetUsableRev(self, rev, _options): | 1413 def GetUsableRev(self, rev, _options): |
| 1393 """Verifies the validity of the revision for this repository.""" | 1414 """Verifies the validity of the revision for this repository.""" |
| 1394 if not scm.SVN.IsValidRevision(url='%s@%s' % (self.url, rev)): | 1415 if not scm.SVN.IsValidRevision(url='%s@%s' % (self.url, rev)): |
| 1395 raise gclient_utils.Error( | 1416 raise gclient_utils.Error( |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1444 new_command.append('--force') | 1465 new_command.append('--force') |
| 1445 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1466 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1446 new_command.extend(('--accept', 'theirs-conflict')) | 1467 new_command.extend(('--accept', 'theirs-conflict')) |
| 1447 elif options.manually_grab_svn_rev: | 1468 elif options.manually_grab_svn_rev: |
| 1448 new_command.append('--force') | 1469 new_command.append('--force') |
| 1449 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1470 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1450 new_command.extend(('--accept', 'postpone')) | 1471 new_command.extend(('--accept', 'postpone')) |
| 1451 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1472 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1452 new_command.extend(('--accept', 'postpone')) | 1473 new_command.extend(('--accept', 'postpone')) |
| 1453 return new_command | 1474 return new_command |
| OLD | NEW |