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