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

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: Fix tests 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') | gclient_utils.py » ('J')
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 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « gclient.py ('k') | gclient_utils.py » ('j') | gclient_utils.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698