Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(786)

Side by Side Diff: gclient_scm.py

Issue 227163002: Revamped terminal output for update. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: rebase Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « gclient.py ('k') | gclient_utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « gclient.py ('k') | gclient_utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698