OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 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 # Monkeypatch IMapIterator so that Ctrl-C can kill everything properly. | 5 # Monkeypatch IMapIterator so that Ctrl-C can kill everything properly. |
6 # Derived from https://gist.github.com/aljungberg/626518 | 6 # Derived from https://gist.github.com/aljungberg/626518 |
7 import multiprocessing.pool | 7 import multiprocessing.pool |
8 from multiprocessing.pool import IMapIterator | 8 from multiprocessing.pool import IMapIterator |
9 def wrapper(func): | 9 def wrapper(func): |
10 def wrap(self, timeout=None): | 10 def wrap(self, timeout=None): |
(...skipping 14 matching lines...) Expand all Loading... |
25 import setup_color | 25 import setup_color |
26 import shutil | 26 import shutil |
27 import signal | 27 import signal |
28 import sys | 28 import sys |
29 import tempfile | 29 import tempfile |
30 import textwrap | 30 import textwrap |
31 import threading | 31 import threading |
32 | 32 |
33 import subprocess2 | 33 import subprocess2 |
34 | 34 |
| 35 from StringIO import StringIO |
| 36 |
| 37 |
35 ROOT = os.path.abspath(os.path.dirname(__file__)) | 38 ROOT = os.path.abspath(os.path.dirname(__file__)) |
36 | |
37 IS_WIN = sys.platform == 'win32' | 39 IS_WIN = sys.platform == 'win32' |
38 GIT_EXE = ROOT+'\\git.bat' if IS_WIN else 'git' | 40 GIT_EXE = ROOT+'\\git.bat' if IS_WIN else 'git' |
39 TEST_MODE = False | 41 TEST_MODE = False |
40 | 42 |
41 FREEZE = 'FREEZE' | 43 FREEZE = 'FREEZE' |
42 FREEZE_SECTIONS = { | 44 FREEZE_SECTIONS = { |
43 'indexed': 'soft', | 45 'indexed': 'soft', |
44 'unindexed': 'mixed' | 46 'unindexed': 'mixed' |
45 } | 47 } |
46 FREEZE_MATCHER = re.compile(r'%s.(%s)' % (FREEZE, '|'.join(FREEZE_SECTIONS))) | 48 FREEZE_MATCHER = re.compile(r'%s.(%s)' % (FREEZE, '|'.join(FREEZE_SECTIONS))) |
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
385 except subprocess2.CalledProcessError: | 387 except subprocess2.CalledProcessError: |
386 pass | 388 pass |
387 | 389 |
388 | 390 |
389 def diff(oldrev, newrev, *args): | 391 def diff(oldrev, newrev, *args): |
390 return run('diff', oldrev, newrev, *args) | 392 return run('diff', oldrev, newrev, *args) |
391 | 393 |
392 | 394 |
393 def freeze(): | 395 def freeze(): |
394 took_action = False | 396 took_action = False |
| 397 key = 'depot-tools.freeze-size-limit' |
| 398 MB = 2**20 |
| 399 limit_mb = get_config_int(key, 100) |
| 400 untracked_bytes = 0 |
| 401 |
| 402 for f, s in status(): |
| 403 if is_unmerged(s): |
| 404 die("Cannot freeze unmerged changes!") |
| 405 if limit_mb > 0: |
| 406 if s.lstat == '?': |
| 407 untracked_bytes += os.stat(f).st_size |
| 408 if untracked_bytes > limit_mb * MB: |
| 409 die("""\ |
| 410 You appear to have too much untracked+unignored data in your git |
| 411 checkout: %.1f / %d MB. |
| 412 |
| 413 Run `git status` to see what it is. |
| 414 |
| 415 In addition to making many git commands slower, this will prevent |
| 416 depot_tools from freezing your in-progress changes. |
| 417 |
| 418 You should add untracked data that you want to ignore to your repo's |
| 419 .git/info/excludes |
| 420 file. See `git help ignore` for the format of this file. |
| 421 |
| 422 If this data is indended as part of your commit, you may adjust the |
| 423 freeze limit by running: |
| 424 git config %s <new_limit> |
| 425 Where <new_limit> is an integer threshold in megabytes.""", |
| 426 untracked_bytes / (MB * 1.0), limit_mb, key) |
395 | 427 |
396 try: | 428 try: |
397 run('commit', '--no-verify', '-m', FREEZE + '.indexed') | 429 run('commit', '--no-verify', '-m', FREEZE + '.indexed') |
398 took_action = True | 430 took_action = True |
399 except subprocess2.CalledProcessError: | 431 except subprocess2.CalledProcessError: |
400 pass | 432 pass |
401 | 433 |
402 try: | 434 try: |
403 run('add', '-A') | 435 run('add', '-A') |
404 run('commit', '--no-verify', '-m', FREEZE + '.unindexed') | 436 run('commit', '--no-verify', '-m', FREEZE + '.unindexed') |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
495 ret = run('hash-object', '-t', kind, '-w', '--stdin', stdin=f) | 527 ret = run('hash-object', '-t', kind, '-w', '--stdin', stdin=f) |
496 f.close() | 528 f.close() |
497 return ret | 529 return ret |
498 | 530 |
499 | 531 |
500 def is_dormant(branch): | 532 def is_dormant(branch): |
501 # TODO(iannucci): Do an oldness check? | 533 # TODO(iannucci): Do an oldness check? |
502 return branch_config(branch, 'dormant', 'false') != 'false' | 534 return branch_config(branch, 'dormant', 'false') != 'false' |
503 | 535 |
504 | 536 |
| 537 def is_unmerged(stat_value): |
| 538 return ( |
| 539 'U' in (stat_value.lstat, stat_value.rstat) or |
| 540 ((stat_value.lstat == stat_value.rstat) and stat_value.lstat in 'AD') |
| 541 ) |
| 542 |
| 543 |
505 def manual_merge_base(branch, base, parent): | 544 def manual_merge_base(branch, base, parent): |
506 set_branch_config(branch, 'base', base) | 545 set_branch_config(branch, 'base', base) |
507 set_branch_config(branch, 'base-upstream', parent) | 546 set_branch_config(branch, 'base-upstream', parent) |
508 | 547 |
509 | 548 |
510 def mktree(treedict): | 549 def mktree(treedict): |
511 """Makes a git tree object and returns its hash. | 550 """Makes a git tree object and returns its hash. |
512 | 551 |
513 See |tree()| for the values of mode, type, and ref. | 552 See |tree()| for the values of mode, type, and ref. |
514 | 553 |
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
704 if dirty: | 743 if dirty: |
705 print 'Cannot %s with a dirty tree. You must commit locally first.' % cmd | 744 print 'Cannot %s with a dirty tree. You must commit locally first.' % cmd |
706 print 'Uncommitted files: (git diff-index --name-status HEAD)' | 745 print 'Uncommitted files: (git diff-index --name-status HEAD)' |
707 print dirty[:4096] | 746 print dirty[:4096] |
708 if len(dirty) > 4096: # pragma: no cover | 747 if len(dirty) > 4096: # pragma: no cover |
709 print '... (run "git diff-index --name-status HEAD" to see full output).' | 748 print '... (run "git diff-index --name-status HEAD" to see full output).' |
710 return True | 749 return True |
711 return False | 750 return False |
712 | 751 |
713 | 752 |
| 753 def status(): |
| 754 """Returns a parsed version of git-status. |
| 755 |
| 756 Returns a generator of (current_name, (lstat, rstat, src)) pairs where: |
| 757 * current_name is the name of the file |
| 758 * lstat is the left status code letter from git-status |
| 759 * rstat is the left status code letter from git-status |
| 760 * src is the current name of the file, or the original name of the file |
| 761 if lstat == 'R' |
| 762 """ |
| 763 stat_entry = collections.namedtuple('stat_entry', 'lstat rstat src') |
| 764 |
| 765 def tokenizer(stream): |
| 766 acc = StringIO() |
| 767 c = None |
| 768 while c != '': |
| 769 c = stream.read(1) |
| 770 if c in (None, '', '\0'): |
| 771 if acc.len: |
| 772 yield acc.getvalue() |
| 773 acc = StringIO() |
| 774 else: |
| 775 acc.write(c) |
| 776 |
| 777 def parser(tokens): |
| 778 while True: |
| 779 # Raises StopIteration if it runs out of tokens. |
| 780 status_dest = next(tokens) |
| 781 stat, dest = status_dest[:2], status_dest[3:] |
| 782 lstat, rstat = stat |
| 783 if lstat == 'R': |
| 784 src = next(tokens) |
| 785 else: |
| 786 src = dest |
| 787 yield (dest, stat_entry(lstat, rstat, src)) |
| 788 |
| 789 return parser(tokenizer(run_stream('status', '-z', bufsize=-1))) |
| 790 |
| 791 |
714 def squash_current_branch(header=None, merge_base=None): | 792 def squash_current_branch(header=None, merge_base=None): |
715 header = header or 'git squash commit.' | 793 header = header or 'git squash commit.' |
716 merge_base = merge_base or get_or_create_merge_base(current_branch()) | 794 merge_base = merge_base or get_or_create_merge_base(current_branch()) |
717 log_msg = header + '\n' | 795 log_msg = header + '\n' |
718 if log_msg: | 796 if log_msg: |
719 log_msg += '\n' | 797 log_msg += '\n' |
720 log_msg += run('log', '--reverse', '--format=%H%n%B', '%s..HEAD' % merge_base) | 798 log_msg += run('log', '--reverse', '--format=%H%n%B', '%s..HEAD' % merge_base) |
721 run('reset', '--soft', merge_base) | 799 run('reset', '--soft', merge_base) |
722 | 800 |
723 if not get_dirty_files(): | 801 if not get_dirty_files(): |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
915 ['HEAD']) | 993 ['HEAD']) |
916 | 994 |
917 | 995 |
918 def clone_file(repository, new_workdir, link, operation): | 996 def clone_file(repository, new_workdir, link, operation): |
919 if not os.path.exists(os.path.join(repository, link)): | 997 if not os.path.exists(os.path.join(repository, link)): |
920 return | 998 return |
921 link_dir = os.path.dirname(os.path.join(new_workdir, link)) | 999 link_dir = os.path.dirname(os.path.join(new_workdir, link)) |
922 if not os.path.exists(link_dir): | 1000 if not os.path.exists(link_dir): |
923 os.makedirs(link_dir) | 1001 os.makedirs(link_dir) |
924 operation(os.path.join(repository, link), os.path.join(new_workdir, link)) | 1002 operation(os.path.join(repository, link), os.path.join(new_workdir, link)) |
OLD | NEW |