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