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