OLD | NEW |
1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2010 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 import logging | 7 import logging |
8 import os | 8 import os |
9 import posixpath | 9 import posixpath |
10 import re | 10 import re |
11 import sys | 11 import sys |
12 import time | 12 import time |
13 | 13 |
14 import scm | 14 import scm |
15 import gclient_utils | 15 import gclient_utils |
16 | 16 |
17 | 17 |
18 class DiffFilterer(object): | 18 class DiffFilterer(object): |
19 """Simple class which tracks which file is being diffed and | 19 """Simple class which tracks which file is being diffed and |
20 replaces instances of its file name in the original and | 20 replaces instances of its file name in the original and |
21 working copy lines of the svn/git diff output.""" | 21 working copy lines of the svn/git diff output.""" |
22 index_string = "Index: " | 22 index_string = "Index: " |
23 original_prefix = "--- " | 23 original_prefix = "--- " |
24 working_prefix = "+++ " | 24 working_prefix = "+++ " |
25 | 25 |
26 def __init__(self, relpath, stdout): | 26 def __init__(self, relpath): |
27 # Note that we always use '/' as the path separator to be | 27 # Note that we always use '/' as the path separator to be |
28 # consistent with svn's cygwin-style output on Windows | 28 # consistent with svn's cygwin-style output on Windows |
29 self._relpath = relpath.replace("\\", "/") | 29 self._relpath = relpath.replace("\\", "/") |
30 self._current_file = "" | 30 self._current_file = "" |
31 self._replacement_file = "" | 31 self._replacement_file = "" |
32 self._stdout = stdout | |
33 | 32 |
34 def SetCurrentFile(self, current_file): | 33 def SetCurrentFile(self, current_file): |
35 self._current_file = current_file | 34 self._current_file = current_file |
36 # Note that we always use '/' as the path separator to be | 35 # Note that we always use '/' as the path separator to be |
37 # consistent with svn's cygwin-style output on Windows | 36 # consistent with svn's cygwin-style output on Windows |
38 self._replacement_file = posixpath.join(self._relpath, current_file) | 37 self._replacement_file = posixpath.join(self._relpath, current_file) |
39 | 38 |
40 def _Replace(self, line): | 39 def _Replace(self, line): |
41 return line.replace(self._current_file, self._replacement_file) | 40 return line.replace(self._current_file, self._replacement_file) |
42 | 41 |
43 def Filter(self, line): | 42 def Filter(self, line): |
44 if (line.startswith(self.index_string)): | 43 if (line.startswith(self.index_string)): |
45 self.SetCurrentFile(line[len(self.index_string):]) | 44 self.SetCurrentFile(line[len(self.index_string):]) |
46 line = self._Replace(line) | 45 line = self._Replace(line) |
47 else: | 46 else: |
48 if (line.startswith(self.original_prefix) or | 47 if (line.startswith(self.original_prefix) or |
49 line.startswith(self.working_prefix)): | 48 line.startswith(self.working_prefix)): |
50 line = self._Replace(line) | 49 line = self._Replace(line) |
51 self._stdout.write(line + '\n') | 50 print(line) |
52 | 51 |
53 | 52 |
54 ### SCM abstraction layer | 53 ### SCM abstraction layer |
55 | 54 |
56 # Factory Method for SCM wrapper creation | 55 # Factory Method for SCM wrapper creation |
57 | 56 |
58 def GetScmName(url): | 57 def GetScmName(url): |
59 if url: | 58 if url: |
60 url, _ = gclient_utils.SplitUrlRevision(url) | 59 url, _ = gclient_utils.SplitUrlRevision(url) |
61 if (url.startswith('git://') or url.startswith('ssh://') or | 60 if (url.startswith('git://') or url.startswith('ssh://') or |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
146 """Generates a patch file which can be applied to the root of the | 145 """Generates a patch file which can be applied to the root of the |
147 repository. | 146 repository. |
148 | 147 |
149 The patch file is generated from a diff of the merge base of HEAD and | 148 The patch file is generated from a diff of the merge base of HEAD and |
150 its upstream branch. | 149 its upstream branch. |
151 """ | 150 """ |
152 merge_base = self._Capture(['merge-base', 'HEAD', 'origin']) | 151 merge_base = self._Capture(['merge-base', 'HEAD', 'origin']) |
153 gclient_utils.CheckCallAndFilter( | 152 gclient_utils.CheckCallAndFilter( |
154 ['git', 'diff', merge_base], | 153 ['git', 'diff', merge_base], |
155 cwd=self.checkout_path, | 154 cwd=self.checkout_path, |
156 filter_fn=DiffFilterer(self.relpath, options.stdout).Filter, | 155 filter_fn=DiffFilterer(self.relpath).Filter) |
157 stdout=options.stdout) | |
158 | 156 |
159 def update(self, options, args, file_list): | 157 def update(self, options, args, file_list): |
160 """Runs git to update or transparently checkout the working copy. | 158 """Runs git to update or transparently checkout the working copy. |
161 | 159 |
162 All updated files will be appended to file_list. | 160 All updated files will be appended to file_list. |
163 | 161 |
164 Raises: | 162 Raises: |
165 Error: if can't get URL for relative path. | 163 Error: if can't get URL for relative path. |
166 """ | 164 """ |
167 if args: | 165 if args: |
(...skipping 10 matching lines...) Expand all Loading... |
178 revision = str(options.revision) | 176 revision = str(options.revision) |
179 if not revision: | 177 if not revision: |
180 revision = default_rev | 178 revision = default_rev |
181 | 179 |
182 rev_str = ' at %s' % revision | 180 rev_str = ' at %s' % revision |
183 files = [] | 181 files = [] |
184 | 182 |
185 printed_path = False | 183 printed_path = False |
186 verbose = [] | 184 verbose = [] |
187 if options.verbose: | 185 if options.verbose: |
188 options.stdout.write("\n_____ %s%s\n" % (self.relpath, rev_str)) | 186 print('\n_____ %s%s' % (self.relpath, rev_str)) |
189 verbose = ['--verbose'] | 187 verbose = ['--verbose'] |
190 printed_path = True | 188 printed_path = True |
191 | 189 |
192 if revision.startswith('refs/heads/'): | 190 if revision.startswith('refs/heads/'): |
193 rev_type = "branch" | 191 rev_type = "branch" |
194 elif revision.startswith('origin/'): | 192 elif revision.startswith('origin/'): |
195 # For compatability with old naming, translate 'origin' to 'refs/heads' | 193 # For compatability with old naming, translate 'origin' to 'refs/heads' |
196 revision = revision.replace('origin/', 'refs/heads/') | 194 revision = revision.replace('origin/', 'refs/heads/') |
197 rev_type = "branch" | 195 rev_type = "branch" |
198 else: | 196 else: |
199 # hash is also a tag, only make a distinction at checkout | 197 # hash is also a tag, only make a distinction at checkout |
200 rev_type = "hash" | 198 rev_type = "hash" |
201 | 199 |
202 if not os.path.exists(self.checkout_path): | 200 if not os.path.exists(self.checkout_path): |
203 self._Clone(revision, url, options) | 201 self._Clone(revision, url, options) |
204 files = self._Capture(['ls-files']).split() | 202 files = self._Capture(['ls-files']).split() |
205 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 203 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
206 if not verbose: | 204 if not verbose: |
207 # Make the output a little prettier. It's nice to have some whitespace | 205 # Make the output a little prettier. It's nice to have some whitespace |
208 # between projects when cloning. | 206 # between projects when cloning. |
209 options.stdout.write('\n') | 207 print('') |
210 return | 208 return |
211 | 209 |
212 if not os.path.exists(os.path.join(self.checkout_path, '.git')): | 210 if not os.path.exists(os.path.join(self.checkout_path, '.git')): |
213 raise gclient_utils.Error('\n____ %s%s\n' | 211 raise gclient_utils.Error('\n____ %s%s\n' |
214 '\tPath is not a git repo. No .git dir.\n' | 212 '\tPath is not a git repo. No .git dir.\n' |
215 '\tTo resolve:\n' | 213 '\tTo resolve:\n' |
216 '\t\trm -rf %s\n' | 214 '\t\trm -rf %s\n' |
217 '\tAnd run gclient sync again\n' | 215 '\tAnd run gclient sync again\n' |
218 % (self.relpath, rev_str, self.relpath)) | 216 % (self.relpath, rev_str, self.relpath)) |
219 | 217 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 for _ in range(10): | 257 for _ in range(10): |
260 try: | 258 try: |
261 remote_output = scm.GIT.Capture( | 259 remote_output = scm.GIT.Capture( |
262 ['remote'] + verbose + ['update'], | 260 ['remote'] + verbose + ['update'], |
263 cwd=self.checkout_path) | 261 cwd=self.checkout_path) |
264 break | 262 break |
265 except gclient_utils.CheckCallError, e: | 263 except gclient_utils.CheckCallError, e: |
266 # Hackish but at that point, git is known to work so just checking for | 264 # Hackish but at that point, git is known to work so just checking for |
267 # 502 in stderr should be fine. | 265 # 502 in stderr should be fine. |
268 if '502' in e.stderr: | 266 if '502' in e.stderr: |
269 options.stdout.write(str(e) + '\n') | 267 print(str(e)) |
270 options.stdout.write('Sleeping %.1f seconds and retrying...\n' % | 268 print('Sleeping %.1f seconds and retrying...' % backoff_time) |
271 backoff_time) | |
272 time.sleep(backoff_time) | 269 time.sleep(backoff_time) |
273 backoff_time *= 1.3 | 270 backoff_time *= 1.3 |
274 continue | 271 continue |
275 raise | 272 raise |
276 | 273 |
277 if verbose: | 274 if verbose: |
278 options.stdout.write(remote_output) | 275 print(remote_output.strip()) |
279 | 276 |
280 # This is a big hammer, debatable if it should even be here... | 277 # This is a big hammer, debatable if it should even be here... |
281 if options.force or options.reset: | 278 if options.force or options.reset: |
282 self._Run(['reset', '--hard', 'HEAD'], options) | 279 self._Run(['reset', '--hard', 'HEAD'], options) |
283 | 280 |
284 if current_type == 'detached': | 281 if current_type == 'detached': |
285 # case 0 | 282 # case 0 |
286 self._CheckClean(rev_str) | 283 self._CheckClean(rev_str) |
287 self._CheckDetachedHead(rev_str, options) | 284 self._CheckDetachedHead(rev_str, options) |
288 self._Capture(['checkout', '--quiet', '%s^0' % revision]) | 285 self._Capture(['checkout', '--quiet', '%s^0' % revision]) |
289 if not printed_path: | 286 if not printed_path: |
290 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 287 print('\n_____ %s%s' % (self.relpath, rev_str)) |
291 elif current_type == 'hash': | 288 elif current_type == 'hash': |
292 # case 1 | 289 # case 1 |
293 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: | 290 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: |
294 # Our git-svn branch (upstream_branch) is our upstream | 291 # Our git-svn branch (upstream_branch) is our upstream |
295 self._AttemptRebase(upstream_branch, files, options, | 292 self._AttemptRebase(upstream_branch, files, options, |
296 newbase=revision, printed_path=printed_path) | 293 newbase=revision, printed_path=printed_path) |
297 printed_path = True | 294 printed_path = True |
298 else: | 295 else: |
299 # Can't find a merge-base since we don't know our upstream. That makes | 296 # Can't find a merge-base since we don't know our upstream. That makes |
300 # this command VERY likely to produce a rebase failure. For now we | 297 # this command VERY likely to produce a rebase failure. For now we |
301 # assume origin is our upstream since that's what the old behavior was. | 298 # assume origin is our upstream since that's what the old behavior was. |
302 upstream_branch = 'origin' | 299 upstream_branch = 'origin' |
303 if options.revision or deps_revision: | 300 if options.revision or deps_revision: |
304 upstream_branch = revision | 301 upstream_branch = revision |
305 self._AttemptRebase(upstream_branch, files, options, | 302 self._AttemptRebase(upstream_branch, files, options, |
306 printed_path=printed_path) | 303 printed_path=printed_path) |
307 printed_path = True | 304 printed_path = True |
308 elif rev_type == 'hash': | 305 elif rev_type == 'hash': |
309 # case 2 | 306 # case 2 |
310 self._AttemptRebase(upstream_branch, files, options, | 307 self._AttemptRebase(upstream_branch, files, options, |
311 newbase=revision, printed_path=printed_path) | 308 newbase=revision, printed_path=printed_path) |
312 printed_path = True | 309 printed_path = True |
313 elif revision.replace('heads', 'remotes/origin') != upstream_branch: | 310 elif revision.replace('heads', 'remotes/origin') != upstream_branch: |
314 # case 4 | 311 # case 4 |
315 new_base = revision.replace('heads', 'remotes/origin') | 312 new_base = revision.replace('heads', 'remotes/origin') |
316 if not printed_path: | 313 if not printed_path: |
317 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 314 print('\n_____ %s%s' % (self.relpath, rev_str)) |
318 switch_error = ("Switching upstream branch from %s to %s\n" | 315 switch_error = ("Switching upstream branch from %s to %s\n" |
319 % (upstream_branch, new_base) + | 316 % (upstream_branch, new_base) + |
320 "Please merge or rebase manually:\n" + | 317 "Please merge or rebase manually:\n" + |
321 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + | 318 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + |
322 "OR git checkout -b <some new branch> %s" % new_base) | 319 "OR git checkout -b <some new branch> %s" % new_base) |
323 raise gclient_utils.Error(switch_error) | 320 raise gclient_utils.Error(switch_error) |
324 else: | 321 else: |
325 # case 3 - the default case | 322 # case 3 - the default case |
326 files = self._Capture(['diff', upstream_branch, '--name-only']).split() | 323 files = self._Capture(['diff', upstream_branch, '--name-only']).split() |
327 if verbose: | 324 if verbose: |
328 options.stdout.write('Trying fast-forward merge to branch : %s\n' % | 325 print('Trying fast-forward merge to branch : %s' % upstream_branch) |
329 upstream_branch) | |
330 try: | 326 try: |
331 merge_output = scm.GIT.Capture(['merge', '--ff-only', upstream_branch], | 327 merge_output = scm.GIT.Capture(['merge', '--ff-only', upstream_branch], |
332 cwd=self.checkout_path) | 328 cwd=self.checkout_path) |
333 except gclient_utils.CheckCallError, e: | 329 except gclient_utils.CheckCallError, e: |
334 if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): | 330 if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): |
335 if not printed_path: | 331 if not printed_path: |
336 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 332 print('\n_____ %s%s' % (self.relpath, rev_str)) |
337 printed_path = True | 333 printed_path = True |
338 while True: | 334 while True: |
339 try: | 335 try: |
340 # TODO(maruel): That can't work with --jobs. | 336 # TODO(maruel): That can't work with --jobs. |
341 action = str(raw_input("Cannot fast-forward merge, attempt to " | 337 action = str(raw_input("Cannot fast-forward merge, attempt to " |
342 "rebase? (y)es / (q)uit / (s)kip : ")) | 338 "rebase? (y)es / (q)uit / (s)kip : ")) |
343 except ValueError: | 339 except ValueError: |
344 gclient_utils.Error('Invalid Character') | 340 gclient_utils.Error('Invalid Character') |
345 continue | 341 continue |
346 if re.match(r'yes|y', action, re.I): | 342 if re.match(r'yes|y', action, re.I): |
347 self._AttemptRebase(upstream_branch, files, options, | 343 self._AttemptRebase(upstream_branch, files, options, |
348 printed_path=printed_path) | 344 printed_path=printed_path) |
349 printed_path = True | 345 printed_path = True |
350 break | 346 break |
351 elif re.match(r'quit|q', action, re.I): | 347 elif re.match(r'quit|q', action, re.I): |
352 raise gclient_utils.Error("Can't fast-forward, please merge or " | 348 raise gclient_utils.Error("Can't fast-forward, please merge or " |
353 "rebase manually.\n" | 349 "rebase manually.\n" |
354 "cd %s && git " % self.checkout_path | 350 "cd %s && git " % self.checkout_path |
355 + "rebase %s" % upstream_branch) | 351 + "rebase %s" % upstream_branch) |
356 elif re.match(r'skip|s', action, re.I): | 352 elif re.match(r'skip|s', action, re.I): |
357 options.stdout.write('Skipping %s\n' % self.relpath) | 353 print('Skipping %s' % self.relpath) |
358 return | 354 return |
359 else: | 355 else: |
360 options.stdout.write('Input not recognized\n') | 356 print('Input not recognized') |
361 elif re.match("error: Your local changes to '.*' would be " | 357 elif re.match("error: Your local changes to '.*' would be " |
362 "overwritten by merge. Aborting.\nPlease, commit your " | 358 "overwritten by merge. Aborting.\nPlease, commit your " |
363 "changes or stash them before you can merge.\n", | 359 "changes or stash them before you can merge.\n", |
364 e.stderr): | 360 e.stderr): |
365 if not printed_path: | 361 if not printed_path: |
366 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 362 print('\n_____ %s%s' % (self.relpath, rev_str)) |
367 printed_path = True | 363 printed_path = True |
368 raise gclient_utils.Error(e.stderr) | 364 raise gclient_utils.Error(e.stderr) |
369 else: | 365 else: |
370 # Some other problem happened with the merge | 366 # Some other problem happened with the merge |
371 logging.error("Error during fast-forward merge in %s!" % self.relpath) | 367 logging.error("Error during fast-forward merge in %s!" % self.relpath) |
372 options.stdout.write(e.stderr + '\n') | 368 print(e.stderr) |
373 raise | 369 raise |
374 else: | 370 else: |
375 # Fast-forward merge was successful | 371 # Fast-forward merge was successful |
376 if not re.match('Already up-to-date.', merge_output) or verbose: | 372 if not re.match('Already up-to-date.', merge_output) or verbose: |
377 if not printed_path: | 373 if not printed_path: |
378 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 374 print('\n_____ %s%s' % (self.relpath, rev_str)) |
379 printed_path = True | 375 printed_path = True |
380 options.stdout.write(merge_output) | 376 print(merge_output.strip()) |
381 if not verbose: | 377 if not verbose: |
382 # Make the output a little prettier. It's nice to have some | 378 # Make the output a little prettier. It's nice to have some |
383 # whitespace between projects when syncing. | 379 # whitespace between projects when syncing. |
384 options.stdout.write('\n') | 380 print('') |
385 | 381 |
386 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 382 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
387 | 383 |
388 # If the rebase generated a conflict, abort and ask user to fix | 384 # If the rebase generated a conflict, abort and ask user to fix |
389 if self._IsRebasing(): | 385 if self._IsRebasing(): |
390 raise gclient_utils.Error('\n____ %s%s\n' | 386 raise gclient_utils.Error('\n____ %s%s\n' |
391 '\nConflict while rebasing this branch.\n' | 387 '\nConflict while rebasing this branch.\n' |
392 'Fix the conflict and run gclient again.\n' | 388 'Fix the conflict and run gclient again.\n' |
393 'See man git-rebase for details.\n' | 389 'See man git-rebase for details.\n' |
394 % (self.relpath, rev_str)) | 390 % (self.relpath, rev_str)) |
395 | 391 |
396 if verbose: | 392 if verbose: |
397 options.stdout.write('Checked out revision %s\n' % | 393 print('Checked out revision %s' % self.revinfo(options, (), None)) |
398 self.revinfo(options, (), None)) | |
399 | 394 |
400 def revert(self, options, args, file_list): | 395 def revert(self, options, args, file_list): |
401 """Reverts local modifications. | 396 """Reverts local modifications. |
402 | 397 |
403 All reverted files will be appended to file_list. | 398 All reverted files will be appended to file_list. |
404 """ | 399 """ |
405 if not os.path.isdir(self.checkout_path): | 400 if not os.path.isdir(self.checkout_path): |
406 # revert won't work if the directory doesn't exist. It needs to | 401 # revert won't work if the directory doesn't exist. It needs to |
407 # checkout instead. | 402 # checkout instead. |
408 options.stdout.write('\n_____ %s is missing, synching instead\n' % | 403 print('\n_____ %s is missing, synching instead' % self.relpath) |
409 self.relpath) | |
410 # Don't reuse the args. | 404 # Don't reuse the args. |
411 return self.update(options, [], file_list) | 405 return self.update(options, [], file_list) |
412 | 406 |
413 default_rev = "refs/heads/master" | 407 default_rev = "refs/heads/master" |
414 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) | 408 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) |
415 if not deps_revision: | 409 if not deps_revision: |
416 deps_revision = default_rev | 410 deps_revision = default_rev |
417 if deps_revision.startswith('refs/heads/'): | 411 if deps_revision.startswith('refs/heads/'): |
418 deps_revision = deps_revision.replace('refs/heads/', 'origin/') | 412 deps_revision = deps_revision.replace('refs/heads/', 'origin/') |
419 | 413 |
420 files = self._Capture(['diff', deps_revision, '--name-only']).split() | 414 files = self._Capture(['diff', deps_revision, '--name-only']).split() |
421 self._Run(['reset', '--hard', deps_revision], options) | 415 self._Run(['reset', '--hard', deps_revision], options) |
422 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 416 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
423 | 417 |
424 def revinfo(self, options, args, file_list): | 418 def revinfo(self, options, args, file_list): |
425 """Returns revision""" | 419 """Returns revision""" |
426 return self._Capture(['rev-parse', 'HEAD']) | 420 return self._Capture(['rev-parse', 'HEAD']) |
427 | 421 |
428 def runhooks(self, options, args, file_list): | 422 def runhooks(self, options, args, file_list): |
429 self.status(options, args, file_list) | 423 self.status(options, args, file_list) |
430 | 424 |
431 def status(self, options, args, file_list): | 425 def status(self, options, args, file_list): |
432 """Display status information.""" | 426 """Display status information.""" |
433 if not os.path.isdir(self.checkout_path): | 427 if not os.path.isdir(self.checkout_path): |
434 options.stdout.write( | 428 print(('\n________ couldn\'t run status in %s:\n' |
435 ('\n________ couldn\'t run status in %s:\nThe directory ' | 429 'The directory does not exist.') % self.checkout_path) |
436 'does not exist.\n') % self.checkout_path) | |
437 else: | 430 else: |
438 merge_base = self._Capture(['merge-base', 'HEAD', 'origin']) | 431 merge_base = self._Capture(['merge-base', 'HEAD', 'origin']) |
439 self._Run(['diff', '--name-status', merge_base], options) | 432 self._Run(['diff', '--name-status', merge_base], options) |
440 files = self._Capture(['diff', '--name-only', merge_base]).split() | 433 files = self._Capture(['diff', '--name-only', merge_base]).split() |
441 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 434 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
442 | 435 |
443 def FullUrlForRelativeUrl(self, url): | 436 def FullUrlForRelativeUrl(self, url): |
444 # Strip from last '/' | 437 # Strip from last '/' |
445 # Equivalent to unix basename | 438 # Equivalent to unix basename |
446 base_url = self.url | 439 base_url = self.url |
447 return base_url[:base_url.rfind('/')] + url | 440 return base_url[:base_url.rfind('/')] + url |
448 | 441 |
449 def _Clone(self, revision, url, options): | 442 def _Clone(self, revision, url, options): |
450 """Clone a git repository from the given URL. | 443 """Clone a git repository from the given URL. |
451 | 444 |
452 Once we've cloned the repo, we checkout a working branch if the specified | 445 Once we've cloned the repo, we checkout a working branch if the specified |
453 revision is a branch head. If it is a tag or a specific commit, then we | 446 revision is a branch head. If it is a tag or a specific commit, then we |
454 leave HEAD detached as it makes future updates simpler -- in this case the | 447 leave HEAD detached as it makes future updates simpler -- in this case the |
455 user should first create a new branch or switch to an existing branch before | 448 user should first create a new branch or switch to an existing branch before |
456 making changes in the repo.""" | 449 making changes in the repo.""" |
457 if not options.verbose: | 450 if not options.verbose: |
458 # git clone doesn't seem to insert a newline properly before printing | 451 # git clone doesn't seem to insert a newline properly before printing |
459 # to stdout | 452 # to stdout |
460 options.stdout.write('\n') | 453 print('') |
461 | 454 |
462 clone_cmd = ['clone'] | 455 clone_cmd = ['clone'] |
463 if revision.startswith('refs/heads/'): | 456 if revision.startswith('refs/heads/'): |
464 clone_cmd.extend(['-b', revision.replace('refs/heads/', '')]) | 457 clone_cmd.extend(['-b', revision.replace('refs/heads/', '')]) |
465 detach_head = False | 458 detach_head = False |
466 else: | 459 else: |
467 clone_cmd.append('--no-checkout') | 460 clone_cmd.append('--no-checkout') |
468 detach_head = True | 461 detach_head = True |
469 if options.verbose: | 462 if options.verbose: |
470 clone_cmd.append('--verbose') | 463 clone_cmd.append('--verbose') |
471 clone_cmd.extend([url, self.checkout_path]) | 464 clone_cmd.extend([url, self.checkout_path]) |
472 | 465 |
473 for _ in range(3): | 466 for _ in range(3): |
474 try: | 467 try: |
475 self._Run(clone_cmd, options, cwd=self._root_dir) | 468 self._Run(clone_cmd, options, cwd=self._root_dir) |
476 break | 469 break |
477 except gclient_utils.Error, e: | 470 except gclient_utils.Error, e: |
478 # TODO(maruel): Hackish, should be fixed by moving _Run() to | 471 # TODO(maruel): Hackish, should be fixed by moving _Run() to |
479 # CheckCall(). | 472 # CheckCall(). |
480 # Too bad we don't have access to the actual output. | 473 # Too bad we don't have access to the actual output. |
481 # We should check for "transfer closed with NNN bytes remaining to | 474 # We should check for "transfer closed with NNN bytes remaining to |
482 # read". In the meantime, just make sure .git exists. | 475 # read". In the meantime, just make sure .git exists. |
483 if (e.args[0] == 'git command clone returned 128' and | 476 if (e.args[0] == 'git command clone returned 128' and |
484 os.path.exists(os.path.join(self.checkout_path, '.git'))): | 477 os.path.exists(os.path.join(self.checkout_path, '.git'))): |
485 options.stdout.write(str(e) + '\n') | 478 print(str(e)) |
486 options.stdout.write('Retrying...\n') | 479 print('Retrying...') |
487 continue | 480 continue |
488 raise e | 481 raise e |
489 | 482 |
490 if detach_head: | 483 if detach_head: |
491 # Squelch git's very verbose detached HEAD warning and use our own | 484 # Squelch git's very verbose detached HEAD warning and use our own |
492 self._Capture(['checkout', '--quiet', '%s^0' % revision]) | 485 self._Capture(['checkout', '--quiet', '%s^0' % revision]) |
493 options.stdout.write( | 486 print( |
494 ('Checked out %s to a detached HEAD. Before making any commits\n' | 487 ('Checked out %s to a detached HEAD. Before making any commits\n' |
495 'in this repo, you should use \'git checkout <branch>\' to switch to\n' | 488 'in this repo, you should use \'git checkout <branch>\' to switch to\n' |
496 'an existing branch or use \'git checkout origin -b <branch>\' to\n' | 489 'an existing branch or use \'git checkout origin -b <branch>\' to\n' |
497 'create a new branch for your work.') % revision) | 490 'create a new branch for your work.') % revision) |
498 | 491 |
499 def _AttemptRebase(self, upstream, files, options, newbase=None, | 492 def _AttemptRebase(self, upstream, files, options, newbase=None, |
500 branch=None, printed_path=False): | 493 branch=None, printed_path=False): |
501 """Attempt to rebase onto either upstream or, if specified, newbase.""" | 494 """Attempt to rebase onto either upstream or, if specified, newbase.""" |
502 files.extend(self._Capture(['diff', upstream, '--name-only']).split()) | 495 files.extend(self._Capture(['diff', upstream, '--name-only']).split()) |
503 revision = upstream | 496 revision = upstream |
504 if newbase: | 497 if newbase: |
505 revision = newbase | 498 revision = newbase |
506 if not printed_path: | 499 if not printed_path: |
507 options.stdout.write('\n_____ %s : Attempting rebase onto %s...\n' % ( | 500 print('\n_____ %s : Attempting rebase onto %s...' % ( |
508 self.relpath, revision)) | 501 self.relpath, revision)) |
509 printed_path = True | 502 printed_path = True |
510 else: | 503 else: |
511 options.stdout.write('Attempting rebase onto %s...\n' % revision) | 504 print('Attempting rebase onto %s...' % revision) |
512 | 505 |
513 # Build the rebase command here using the args | 506 # Build the rebase command here using the args |
514 # git rebase [options] [--onto <newbase>] <upstream> [<branch>] | 507 # git rebase [options] [--onto <newbase>] <upstream> [<branch>] |
515 rebase_cmd = ['rebase'] | 508 rebase_cmd = ['rebase'] |
516 if options.verbose: | 509 if options.verbose: |
517 rebase_cmd.append('--verbose') | 510 rebase_cmd.append('--verbose') |
518 if newbase: | 511 if newbase: |
519 rebase_cmd.extend(['--onto', newbase]) | 512 rebase_cmd.extend(['--onto', newbase]) |
520 rebase_cmd.append(upstream) | 513 rebase_cmd.append(upstream) |
521 if branch: | 514 if branch: |
(...skipping 14 matching lines...) Expand all Loading... |
536 if re.match(r'yes|y', rebase_action, re.I): | 529 if re.match(r'yes|y', rebase_action, re.I): |
537 self._Run(['reset', '--hard', 'HEAD'], options) | 530 self._Run(['reset', '--hard', 'HEAD'], options) |
538 # Should this be recursive? | 531 # Should this be recursive? |
539 rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path) | 532 rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path) |
540 break | 533 break |
541 elif re.match(r'quit|q', rebase_action, re.I): | 534 elif re.match(r'quit|q', rebase_action, re.I): |
542 raise gclient_utils.Error("Please merge or rebase manually\n" | 535 raise gclient_utils.Error("Please merge or rebase manually\n" |
543 "cd %s && git " % self.checkout_path | 536 "cd %s && git " % self.checkout_path |
544 + "%s" % ' '.join(rebase_cmd)) | 537 + "%s" % ' '.join(rebase_cmd)) |
545 elif re.match(r'show|s', rebase_action, re.I): | 538 elif re.match(r'show|s', rebase_action, re.I): |
546 options.stdout.write('\n%s\n' % e.stderr.strip()) | 539 print('\n%s' % e.stderr.strip()) |
547 continue | 540 continue |
548 else: | 541 else: |
549 gclient_utils.Error("Input not recognized") | 542 gclient_utils.Error("Input not recognized") |
550 continue | 543 continue |
551 elif re.search(r'^CONFLICT', e.stdout, re.M): | 544 elif re.search(r'^CONFLICT', e.stdout, re.M): |
552 raise gclient_utils.Error("Conflict while rebasing this branch.\n" | 545 raise gclient_utils.Error("Conflict while rebasing this branch.\n" |
553 "Fix the conflict and run gclient again.\n" | 546 "Fix the conflict and run gclient again.\n" |
554 "See 'man git-rebase' for details.\n") | 547 "See 'man git-rebase' for details.\n") |
555 else: | 548 else: |
556 options.stdout.write(e.stdout.strip() + '\n') | 549 print(e.stdout.strip()) |
557 options.stdout.write('Rebase produced error output:\n%s\n' % | 550 print('Rebase produced error output:\n%s' % e.stderr.strip()) |
558 e.stderr.strip()) | |
559 raise gclient_utils.Error("Unrecognized error, please merge or rebase " | 551 raise gclient_utils.Error("Unrecognized error, please merge or rebase " |
560 "manually.\ncd %s && git " % | 552 "manually.\ncd %s && git " % |
561 self.checkout_path | 553 self.checkout_path |
562 + "%s" % ' '.join(rebase_cmd)) | 554 + "%s" % ' '.join(rebase_cmd)) |
563 | 555 |
564 options.stdout.write(rebase_output) | 556 print(rebase_output.strip()) |
565 if not options.verbose: | 557 if not options.verbose: |
566 # Make the output a little prettier. It's nice to have some | 558 # Make the output a little prettier. It's nice to have some |
567 # whitespace between projects when syncing. | 559 # whitespace between projects when syncing. |
568 options.stdout.write('\n') | 560 print('') |
569 | 561 |
570 @staticmethod | 562 @staticmethod |
571 def _CheckMinVersion(min_version): | 563 def _CheckMinVersion(min_version): |
572 (ok, current_version) = scm.GIT.AssertVersion(min_version) | 564 (ok, current_version) = scm.GIT.AssertVersion(min_version) |
573 if not ok: | 565 if not ok: |
574 raise gclient_utils.Error('git version %s < minimum required %s' % | 566 raise gclient_utils.Error('git version %s < minimum required %s' % |
575 (current_version, min_version)) | 567 (current_version, min_version)) |
576 | 568 |
577 def _IsRebasing(self): | 569 def _IsRebasing(self): |
578 # Check for any of REBASE-i/REBASE-m/REBASE/AM. Unfortunately git doesn't | 570 # 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... |
617 raise gclient_utils.Error('\n____ %s%s\n' | 609 raise gclient_utils.Error('\n____ %s%s\n' |
618 '\tAlready in a conflict, i.e. (no branch).\n' | 610 '\tAlready in a conflict, i.e. (no branch).\n' |
619 '\tFix the conflict and run gclient again.\n' | 611 '\tFix the conflict and run gclient again.\n' |
620 '\tOr to abort run:\n\t\tgit-rebase --abort\n' | 612 '\tOr to abort run:\n\t\tgit-rebase --abort\n' |
621 '\tSee man git-rebase for details.\n' | 613 '\tSee man git-rebase for details.\n' |
622 % (self.relpath, rev_str)) | 614 % (self.relpath, rev_str)) |
623 # Let's just save off the commit so we can proceed. | 615 # Let's just save off the commit so we can proceed. |
624 name = ('saved-by-gclient-' + | 616 name = ('saved-by-gclient-' + |
625 self._Capture(['rev-parse', '--short', 'HEAD'])) | 617 self._Capture(['rev-parse', '--short', 'HEAD'])) |
626 self._Capture(['branch', name]) | 618 self._Capture(['branch', name]) |
627 options.stdout.write( | 619 print('\n_____ found an unreferenced commit and saved it as \'%s\'' % |
628 '\n_____ found an unreferenced commit and saved it as \'%s\'\n' % | |
629 name) | 620 name) |
630 | 621 |
631 def _GetCurrentBranch(self): | 622 def _GetCurrentBranch(self): |
632 # Returns name of current branch or None for detached HEAD | 623 # Returns name of current branch or None for detached HEAD |
633 branch = self._Capture(['rev-parse', '--abbrev-ref=strict', 'HEAD']) | 624 branch = self._Capture(['rev-parse', '--abbrev-ref=strict', 'HEAD']) |
634 if branch == 'HEAD': | 625 if branch == 'HEAD': |
635 return None | 626 return None |
636 return branch | 627 return branch |
637 | 628 |
638 def _Capture(self, args): | 629 def _Capture(self, args): |
639 return gclient_utils.CheckCall( | 630 return gclient_utils.CheckCall( |
640 ['git'] + args, cwd=self.checkout_path, print_error=False)[0].strip() | 631 ['git'] + args, cwd=self.checkout_path, print_error=False)[0].strip() |
641 | 632 |
642 def _Run(self, args, options, **kwargs): | 633 def _Run(self, args, options, **kwargs): |
643 kwargs.setdefault('cwd', self.checkout_path) | 634 kwargs.setdefault('cwd', self.checkout_path) |
644 gclient_utils.CheckCallAndFilterAndHeader(['git'] + args, | 635 gclient_utils.CheckCallAndFilterAndHeader(['git'] + args, |
645 always=options.verbose, stdout=options.stdout, **kwargs) | 636 always=options.verbose, **kwargs) |
646 | 637 |
647 | 638 |
648 class SVNWrapper(SCMWrapper): | 639 class SVNWrapper(SCMWrapper): |
649 """ Wrapper for SVN """ | 640 """ Wrapper for SVN """ |
650 | 641 |
651 def cleanup(self, options, args, file_list): | 642 def cleanup(self, options, args, file_list): |
652 """Cleanup working copy.""" | 643 """Cleanup working copy.""" |
653 self._Run(['cleanup'] + args, options) | 644 self._Run(['cleanup'] + args, options) |
654 | 645 |
655 def diff(self, options, args, file_list): | 646 def diff(self, options, args, file_list): |
(...skipping 17 matching lines...) Expand all Loading... |
673 def pack(self, options, args, file_list): | 664 def pack(self, options, args, file_list): |
674 """Generates a patch file which can be applied to the root of the | 665 """Generates a patch file which can be applied to the root of the |
675 repository.""" | 666 repository.""" |
676 if not os.path.isdir(self.checkout_path): | 667 if not os.path.isdir(self.checkout_path): |
677 raise gclient_utils.Error('Directory %s is not present.' % | 668 raise gclient_utils.Error('Directory %s is not present.' % |
678 self.checkout_path) | 669 self.checkout_path) |
679 gclient_utils.CheckCallAndFilter( | 670 gclient_utils.CheckCallAndFilter( |
680 ['svn', 'diff', '-x', '--ignore-eol-style'] + args, | 671 ['svn', 'diff', '-x', '--ignore-eol-style'] + args, |
681 cwd=self.checkout_path, | 672 cwd=self.checkout_path, |
682 print_stdout=False, | 673 print_stdout=False, |
683 filter_fn=DiffFilterer(self.relpath, options.stdout).Filter, | 674 filter_fn=DiffFilterer(self.relpath).Filter) |
684 stdout=options.stdout) | |
685 | 675 |
686 def update(self, options, args, file_list): | 676 def update(self, options, args, file_list): |
687 """Runs svn to update or transparently checkout the working copy. | 677 """Runs svn to update or transparently checkout the working copy. |
688 | 678 |
689 All updated files will be appended to file_list. | 679 All updated files will be appended to file_list. |
690 | 680 |
691 Raises: | 681 Raises: |
692 Error: if can't get URL for relative path. | 682 Error: if can't get URL for relative path. |
693 """ | 683 """ |
694 # Only update if git is not controlling the directory. | 684 # Only update if git is not controlling the directory. |
695 git_path = os.path.join(self.checkout_path, '.git') | 685 git_path = os.path.join(self.checkout_path, '.git') |
696 if os.path.exists(git_path): | 686 if os.path.exists(git_path): |
697 options.stdout.write('________ found .git directory; skipping %s\n' % | 687 print('________ found .git directory; skipping %s' % self.relpath) |
698 self.relpath) | |
699 return | 688 return |
700 | 689 |
701 if args: | 690 if args: |
702 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) | 691 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) |
703 | 692 |
704 # revision is the revision to match. It is None if no revision is specified, | 693 # revision is the revision to match. It is None if no revision is specified, |
705 # i.e. the 'deps ain't pinned'. | 694 # i.e. the 'deps ain't pinned'. |
706 url, revision = gclient_utils.SplitUrlRevision(self.url) | 695 url, revision = gclient_utils.SplitUrlRevision(self.url) |
707 # Keep the original unpinned url for reference in case the repo is switched. | 696 # Keep the original unpinned url for reference in case the repo is switched. |
708 base_url = url | 697 base_url = url |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
750 # The repository url changed, need to switch. | 739 # The repository url changed, need to switch. |
751 try: | 740 try: |
752 to_info = scm.SVN.CaptureInfo(url) | 741 to_info = scm.SVN.CaptureInfo(url) |
753 except gclient_utils.Error: | 742 except gclient_utils.Error: |
754 # The url is invalid or the server is not accessible, it's safer to bail | 743 # The url is invalid or the server is not accessible, it's safer to bail |
755 # out right now. | 744 # out right now. |
756 raise gclient_utils.Error('This url is unreachable: %s' % url) | 745 raise gclient_utils.Error('This url is unreachable: %s' % url) |
757 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) | 746 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) |
758 and (from_info['UUID'] == to_info['UUID'])) | 747 and (from_info['UUID'] == to_info['UUID'])) |
759 if can_switch: | 748 if can_switch: |
760 options.stdout.write('\n_____ relocating %s to a new checkout\n' % | 749 print('\n_____ relocating %s to a new checkout' % self.relpath) |
761 self.relpath) | |
762 # We have different roots, so check if we can switch --relocate. | 750 # We have different roots, so check if we can switch --relocate. |
763 # Subversion only permits this if the repository UUIDs match. | 751 # Subversion only permits this if the repository UUIDs match. |
764 # Perform the switch --relocate, then rewrite the from_url | 752 # Perform the switch --relocate, then rewrite the from_url |
765 # to reflect where we "are now." (This is the same way that | 753 # to reflect where we "are now." (This is the same way that |
766 # Subversion itself handles the metadata when switch --relocate | 754 # Subversion itself handles the metadata when switch --relocate |
767 # is used.) This makes the checks below for whether we | 755 # is used.) This makes the checks below for whether we |
768 # can update to a revision or have to switch to a different | 756 # can update to a revision or have to switch to a different |
769 # branch work as expected. | 757 # branch work as expected. |
770 # TODO(maruel): TEST ME ! | 758 # TODO(maruel): TEST ME ! |
771 command = ['switch', '--relocate', | 759 command = ['switch', '--relocate', |
772 from_info['Repository Root'], | 760 from_info['Repository Root'], |
773 to_info['Repository Root'], | 761 to_info['Repository Root'], |
774 self.relpath] | 762 self.relpath] |
775 self._Run(command, options, cwd=self._root_dir) | 763 self._Run(command, options, cwd=self._root_dir) |
776 from_info['URL'] = from_info['URL'].replace( | 764 from_info['URL'] = from_info['URL'].replace( |
777 from_info['Repository Root'], | 765 from_info['Repository Root'], |
778 to_info['Repository Root']) | 766 to_info['Repository Root']) |
779 else: | 767 else: |
780 if not options.force and not options.reset: | 768 if not options.force and not options.reset: |
781 # Look for local modifications but ignore unversioned files. | 769 # Look for local modifications but ignore unversioned files. |
782 for status in scm.SVN.CaptureStatus(self.checkout_path): | 770 for status in scm.SVN.CaptureStatus(self.checkout_path): |
783 if status[0] != '?': | 771 if status[0] != '?': |
784 raise gclient_utils.Error( | 772 raise gclient_utils.Error( |
785 ('Can\'t switch the checkout to %s; UUID don\'t match and ' | 773 ('Can\'t switch the checkout to %s; UUID don\'t match and ' |
786 'there is local changes in %s. Delete the directory and ' | 774 'there is local changes in %s. Delete the directory and ' |
787 'try again.') % (url, self.checkout_path)) | 775 'try again.') % (url, self.checkout_path)) |
788 # Ok delete it. | 776 # Ok delete it. |
789 options.stdout.write('\n_____ switching %s to a new checkout\n' % | 777 print('\n_____ switching %s to a new checkout' % self.relpath) |
790 self.relpath) | |
791 gclient_utils.RemoveDirectory(self.checkout_path) | 778 gclient_utils.RemoveDirectory(self.checkout_path) |
792 # We need to checkout. | 779 # We need to checkout. |
793 command = ['checkout', url, self.checkout_path] | 780 command = ['checkout', url, self.checkout_path] |
794 command = self._AddAdditionalUpdateFlags(command, options, revision) | 781 command = self._AddAdditionalUpdateFlags(command, options, revision) |
795 self._RunAndGetFileList(command, options, file_list, self._root_dir) | 782 self._RunAndGetFileList(command, options, file_list, self._root_dir) |
796 return | 783 return |
797 | 784 |
798 # If the provided url has a revision number that matches the revision | 785 # If the provided url has a revision number that matches the revision |
799 # number of the existing directory, then we don't need to bother updating. | 786 # number of the existing directory, then we don't need to bother updating. |
800 if not options.force and str(from_info['Revision']) == revision: | 787 if not options.force and str(from_info['Revision']) == revision: |
801 if options.verbose or not forced_revision: | 788 if options.verbose or not forced_revision: |
802 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 789 print('\n_____ %s%s' % (self.relpath, rev_str)) |
803 return | 790 return |
804 | 791 |
805 command = ['update', self.checkout_path] | 792 command = ['update', self.checkout_path] |
806 command = self._AddAdditionalUpdateFlags(command, options, revision) | 793 command = self._AddAdditionalUpdateFlags(command, options, revision) |
807 self._RunAndGetFileList(command, options, file_list, self._root_dir) | 794 self._RunAndGetFileList(command, options, file_list, self._root_dir) |
808 | 795 |
809 def updatesingle(self, options, args, file_list): | 796 def updatesingle(self, options, args, file_list): |
810 filename = args.pop() | 797 filename = args.pop() |
811 if scm.SVN.AssertVersion("1.5")[0]: | 798 if scm.SVN.AssertVersion("1.5")[0]: |
812 if not os.path.exists(os.path.join(self.checkout_path, '.svn')): | 799 if not os.path.exists(os.path.join(self.checkout_path, '.svn')): |
(...skipping 23 matching lines...) Expand all Loading... |
836 | 823 |
837 def revert(self, options, args, file_list): | 824 def revert(self, options, args, file_list): |
838 """Reverts local modifications. Subversion specific. | 825 """Reverts local modifications. Subversion specific. |
839 | 826 |
840 All reverted files will be appended to file_list, even if Subversion | 827 All reverted files will be appended to file_list, even if Subversion |
841 doesn't know about them. | 828 doesn't know about them. |
842 """ | 829 """ |
843 if not os.path.isdir(self.checkout_path): | 830 if not os.path.isdir(self.checkout_path): |
844 # svn revert won't work if the directory doesn't exist. It needs to | 831 # svn revert won't work if the directory doesn't exist. It needs to |
845 # checkout instead. | 832 # checkout instead. |
846 options.stdout.write('\n_____ %s is missing, synching instead\n' % | 833 print('\n_____ %s is missing, synching instead' % self.relpath) |
847 self.relpath) | |
848 # Don't reuse the args. | 834 # Don't reuse the args. |
849 return self.update(options, [], file_list) | 835 return self.update(options, [], file_list) |
850 | 836 |
851 # Do a flush of sys.stdout every 10 secs or so otherwise it may never be | |
852 # flushed fast enough for buildbot. | |
853 last_flushed_at = time.time() | |
854 sys.stdout.flush() | |
855 | |
856 for file_status in scm.SVN.CaptureStatus(self.checkout_path): | 837 for file_status in scm.SVN.CaptureStatus(self.checkout_path): |
857 file_path = os.path.join(self.checkout_path, file_status[1]) | 838 file_path = os.path.join(self.checkout_path, file_status[1]) |
858 if file_status[0][0] == 'X': | 839 if file_status[0][0] == 'X': |
859 # Ignore externals. | 840 # Ignore externals. |
860 logging.info('Ignoring external %s' % file_path) | 841 logging.info('Ignoring external %s' % file_path) |
861 continue | 842 continue |
862 | 843 |
863 if logging.getLogger().isEnabledFor(logging.INFO): | 844 if logging.getLogger().isEnabledFor(logging.INFO): |
864 logging.info('%s%s' % (file[0], file[1])) | 845 logging.info('%s%s' % (file[0], file[1])) |
865 else: | 846 else: |
866 options.stdout.write(file_path + '\n') | 847 print(file_path) |
867 # Flush at least 10 seconds between line writes. We wait at least 10 | |
868 # seconds to avoid overloading the reader that called us with output, | |
869 # which can slow busy readers down. | |
870 if (time.time() - last_flushed_at) > 10: | |
871 last_flushed_at = time.time() | |
872 sys.stdout.flush() | |
873 | 848 |
874 if file_status[0].isspace(): | 849 if file_status[0].isspace(): |
875 logging.error('No idea what is the status of %s.\n' | 850 logging.error('No idea what is the status of %s.\n' |
876 'You just found a bug in gclient, please ping ' | 851 'You just found a bug in gclient, please ping ' |
877 'maruel@chromium.org ASAP!' % file_path) | 852 'maruel@chromium.org ASAP!' % file_path) |
878 # svn revert is really stupid. It fails on inconsistent line-endings, | 853 # svn revert is really stupid. It fails on inconsistent line-endings, |
879 # on switched directories, etc. So take no chance and delete everything! | 854 # on switched directories, etc. So take no chance and delete everything! |
880 try: | 855 try: |
881 if not os.path.exists(file_path): | 856 if not os.path.exists(file_path): |
882 pass | 857 pass |
(...skipping 27 matching lines...) Expand all Loading... |
910 return None | 885 return None |
911 | 886 |
912 def runhooks(self, options, args, file_list): | 887 def runhooks(self, options, args, file_list): |
913 self.status(options, args, file_list) | 888 self.status(options, args, file_list) |
914 | 889 |
915 def status(self, options, args, file_list): | 890 def status(self, options, args, file_list): |
916 """Display status information.""" | 891 """Display status information.""" |
917 command = ['status'] + args | 892 command = ['status'] + args |
918 if not os.path.isdir(self.checkout_path): | 893 if not os.path.isdir(self.checkout_path): |
919 # svn status won't work if the directory doesn't exist. | 894 # svn status won't work if the directory doesn't exist. |
920 options.stdout.write( | 895 print(('\n________ couldn\'t run \'%s\' in \'%s\':\n' |
921 ('\n________ couldn\'t run \'%s\' in \'%s\':\nThe directory ' | 896 'The directory does not exist.') % |
922 'does not exist.') % (' '.join(command), self.checkout_path)) | 897 (' '.join(command), self.checkout_path)) |
923 # There's no file list to retrieve. | 898 # There's no file list to retrieve. |
924 else: | 899 else: |
925 self._RunAndGetFileList(command, options, file_list) | 900 self._RunAndGetFileList(command, options, file_list) |
926 | 901 |
927 def FullUrlForRelativeUrl(self, url): | 902 def FullUrlForRelativeUrl(self, url): |
928 # Find the forth '/' and strip from there. A bit hackish. | 903 # Find the forth '/' and strip from there. A bit hackish. |
929 return '/'.join(self.url.split('/')[:4]) + url | 904 return '/'.join(self.url.split('/')[:4]) + url |
930 | 905 |
931 def _Run(self, args, options, **kwargs): | 906 def _Run(self, args, options, **kwargs): |
932 """Runs a commands that goes to stdout.""" | 907 """Runs a commands that goes to stdout.""" |
933 kwargs.setdefault('cwd', self.checkout_path) | 908 kwargs.setdefault('cwd', self.checkout_path) |
934 gclient_utils.CheckCallAndFilterAndHeader(['svn'] + args, | 909 gclient_utils.CheckCallAndFilterAndHeader(['svn'] + args, |
935 always=options.verbose, stdout=options.stdout, **kwargs) | 910 always=options.verbose, **kwargs) |
936 | 911 |
937 def _RunAndGetFileList(self, args, options, file_list, cwd=None): | 912 def _RunAndGetFileList(self, args, options, file_list, cwd=None): |
938 """Runs a commands that goes to stdout and grabs the file listed.""" | 913 """Runs a commands that goes to stdout and grabs the file listed.""" |
939 cwd = cwd or self.checkout_path | 914 cwd = cwd or self.checkout_path |
940 scm.SVN.RunAndGetFileList(options.verbose, args, cwd=cwd, | 915 scm.SVN.RunAndGetFileList(options.verbose, args, cwd=cwd, |
941 file_list=file_list, stdout=options.stdout) | 916 file_list=file_list) |
942 | 917 |
943 @staticmethod | 918 @staticmethod |
944 def _AddAdditionalUpdateFlags(command, options, revision): | 919 def _AddAdditionalUpdateFlags(command, options, revision): |
945 """Add additional flags to command depending on what options are set. | 920 """Add additional flags to command depending on what options are set. |
946 command should be a list of strings that represents an svn command. | 921 command should be a list of strings that represents an svn command. |
947 | 922 |
948 This method returns a new list to be used as a command.""" | 923 This method returns a new list to be used as a command.""" |
949 new_command = command[:] | 924 new_command = command[:] |
950 if revision: | 925 if revision: |
951 new_command.extend(['--revision', str(revision).strip()]) | 926 new_command.extend(['--revision', str(revision).strip()]) |
952 # --force was added to 'svn update' in svn 1.5. | 927 # --force was added to 'svn update' in svn 1.5. |
953 if ((options.force or options.manually_grab_svn_rev) and | 928 if ((options.force or options.manually_grab_svn_rev) and |
954 scm.SVN.AssertVersion("1.5")[0]): | 929 scm.SVN.AssertVersion("1.5")[0]): |
955 new_command.append('--force') | 930 new_command.append('--force') |
956 return new_command | 931 return new_command |
OLD | NEW |