 Chromium Code Reviews
 Chromium Code Reviews Issue 6665018:
  Remove support for generic "hook files" in git-cl, require depot_tools  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
    
  
    Issue 6665018:
  Remove support for generic "hook files" in git-cl, require depot_tools  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools| OLD | NEW | 
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python | 
| 2 # git-cl -- a git-command for integrating reviews on Rietveld | 2 # git-cl -- a git-command for integrating reviews on Rietveld | 
| 3 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 3 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 
| 4 | 4 | 
| 5 import errno | 5 import errno | 
| 6 import logging | 6 import logging | 
| 7 import optparse | 7 import optparse | 
| 8 import os | 8 import os | 
| 9 import re | 9 import re | 
| 10 import subprocess | 10 import subprocess | 
| 11 import sys | 11 import sys | 
| 12 import tempfile | 12 import tempfile | 
| 13 import textwrap | 13 import textwrap | 
| 14 import upload | 14 import upload | 
| 15 import urlparse | 15 import urlparse | 
| 16 import urllib2 | 16 import urllib2 | 
| 17 | 17 | 
| 18 try: | 18 try: | 
| 19 import readline | 19 import readline | 
| 20 except ImportError: | 20 except ImportError: | 
| 21 pass | 21 pass | 
| 22 | 22 | 
| 23 try: | 23 try: | 
| 24 # Add the parent directory in case it's a depot_tools checkout. | 24 # TODO(dpranke): We wrap this in a try block for a limited form of | 
| 
M-A Ruel
2011/03/11 14:42:52
My take is to move git_cl up a directory first and
 | |
| 25 # backwards-compatibility with older versions of git-cl that weren't | |
| 26 # dependent on depot_tools. This version should still work outside of | |
| 27 # depot_tools as long as --bypass-hooks is used. We should remove this | |
| 28 # once this has baked for a while and things seem safe. | |
| 25 depot_tools_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | 29 depot_tools_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | 
| 26 sys.path.append(depot_tools_path) | 30 sys.path.append(depot_tools_path) | 
| 27 import breakpad | 31 import breakpad | 
| 28 except ImportError: | 32 except ImportError: | 
| 29 pass | 33 pass | 
| 30 | 34 | 
| 31 DEFAULT_SERVER = 'http://codereview.appspot.com' | 35 DEFAULT_SERVER = 'http://codereview.appspot.com' | 
| 32 PREDCOMMIT_HOOK = '.git/hooks/pre-cl-dcommit' | |
| 33 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s' | |
| 34 PREUPLOAD_HOOK = '.git/hooks/pre-cl-upload' | |
| 35 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' | 36 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' | 
| 36 | 37 | 
| 37 def DieWithError(message): | 38 def DieWithError(message): | 
| 38 print >>sys.stderr, message | 39 print >>sys.stderr, message | 
| 39 sys.exit(1) | 40 sys.exit(1) | 
| 40 | 41 | 
| 41 | 42 | 
| 42 def Popen(cmd, **kwargs): | 43 def Popen(cmd, **kwargs): | 
| 43 """Wrapper for subprocess.Popen() that logs and watch for cygwin issues""" | 44 """Wrapper for subprocess.Popen() that logs and watch for cygwin issues""" | 
| 44 logging.info('Popen: ' + ' '.join(cmd)) | 45 logging.info('Popen: ' + ' '.join(cmd)) | 
| (...skipping 489 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 534 SetProperty('tree-status-url', 'STATUS', unset_error_ok=True) | 535 SetProperty('tree-status-url', 'STATUS', unset_error_ok=True) | 
| 535 SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True) | 536 SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True) | 
| 536 | 537 | 
| 537 if 'PUSH_URL_CONFIG' in keyvals and 'ORIGIN_URL_CONFIG' in keyvals: | 538 if 'PUSH_URL_CONFIG' in keyvals and 'ORIGIN_URL_CONFIG' in keyvals: | 
| 538 #should be of the form | 539 #should be of the form | 
| 539 #PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof | 540 #PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof | 
| 540 #ORIGIN_URL_CONFIG: http://src.chromium.org/git | 541 #ORIGIN_URL_CONFIG: http://src.chromium.org/git | 
| 541 RunGit(['config', keyvals['PUSH_URL_CONFIG'], | 542 RunGit(['config', keyvals['PUSH_URL_CONFIG'], | 
| 542 keyvals['ORIGIN_URL_CONFIG']]) | 543 keyvals['ORIGIN_URL_CONFIG']]) | 
| 543 | 544 | 
| 544 # Update the hooks if the local hook files aren't present already. | |
| 545 if GetProperty('GITCL_PREUPLOAD') and not os.path.isfile(PREUPLOAD_HOOK): | |
| 546 DownloadToFile(GetProperty('GITCL_PREUPLOAD'), PREUPLOAD_HOOK) | |
| 547 if GetProperty('GITCL_PREDCOMMIT') and not os.path.isfile(PREDCOMMIT_HOOK): | |
| 548 DownloadToFile(GetProperty('GITCL_PREDCOMMIT'), PREDCOMMIT_HOOK) | |
| 549 return 0 | |
| 550 | |
| 551 | 545 | 
| 552 @usage('[repo root containing codereview.settings]') | 546 @usage('[repo root containing codereview.settings]') | 
| 553 def CMDconfig(parser, args): | 547 def CMDconfig(parser, args): | 
| 554 """edit configuration for this tree""" | 548 """edit configuration for this tree""" | 
| 555 | 549 | 
| 556 (options, args) = parser.parse_args(args) | 550 (options, args) = parser.parse_args(args) | 
| 557 if len(args) == 0: | 551 if len(args) == 0: | 
| 558 GetCodereviewSettingsInteractively() | 552 GetCodereviewSettingsInteractively() | 
| 559 return 0 | 553 return 0 | 
| 560 | 554 | 
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 662 return | 656 return | 
| 663 | 657 | 
| 664 fileobj = open(filename) | 658 fileobj = open(filename) | 
| 665 text = fileobj.read() | 659 text = fileobj.read() | 
| 666 fileobj.close() | 660 fileobj.close() | 
| 667 os.remove(filename) | 661 os.remove(filename) | 
| 668 stripcomment_re = re.compile(r'^#.*$', re.MULTILINE) | 662 stripcomment_re = re.compile(r'^#.*$', re.MULTILINE) | 
| 669 return stripcomment_re.sub('', text).strip() | 663 return stripcomment_re.sub('', text).strip() | 
| 670 | 664 | 
| 671 | 665 | 
| 672 def RunHook(hook, upstream_branch, error_ok=False): | 666 def ConvertToInteger(inputval): | 
| 
M-A Ruel
2011/03/11 14:42:52
(random idea)
def safe_convert(inputval, outputtyp
 | |
| 673 """Run a given hook if it exists. By default, we fail on errors.""" | 667 """Convert a string to integer, but returns either an int or None.""" | 
| 674 hook = '%s/%s' % (settings.GetRoot(), hook) | 668 try: | 
| 675 if not os.path.exists(hook): | 669 return int(inputval) | 
| 676 return | 670 except (TypeError, ValueError): | 
| 677 return RunCommand([hook, upstream_branch], error_ok=error_ok, | 671 return None | 
| 678 redirect_stdout=False) | 672 | 
| 673 | |
| 674 def RunHook(committing, upstream_branch): | |
| 675 import presubmit_support | |
| 676 import scm | |
| 677 import watchlists | |
| 678 | |
| 679 root = RunCommand(['git', 'rev-parse', '--show-cdup']).strip() | |
| 680 if not root: | |
| 681 root = "." | |
| 682 absroot = os.path.abspath(root) | |
| 683 if not root: | |
| 684 raise Exception("Could not get root directory.") | |
| 685 | |
| 686 # We use the sha1 of HEAD as a name of this change. | |
| 687 name = RunCommand(['git', 'rev-parse', 'HEAD']).strip() | |
| 688 files = scm.GIT.CaptureStatus([root], upstream_branch) | |
| 689 | |
| 690 cl = Changelist() | |
| 691 issue = ConvertToInteger(cl.GetIssue()) | |
| 692 patchset = ConvertToInteger(cl.GetPatchset()) | |
| 693 if issue: | |
| 694 description = cl.GetDescription() | |
| 695 else: | |
| 696 # If the change was never uploaded, use the log messages of all commits | |
| 697 # up to the branch point, as git cl upload will prefill the description | |
| 698 # with these log messages. | |
| 699 description = RunCommand(['git', 'log', '--pretty=format:%s%n%n%b', | |
| 700 '%s...' % (upstream_branch)]).strip() | |
| 701 change = presubmit_support.GitChange(name, description, absroot, files, | |
| 702 issue, patchset) | |
| 703 | |
| 704 # Apply watchlists on upload. | |
| 705 if not committing: | |
| 706 watchlist = watchlists.Watchlists(change.RepositoryRoot()) | |
| 707 files = [f.LocalPath() for f in change.AffectedFiles()] | |
| 708 watchers = watchlist.GetWatchersForPaths(files) | |
| 709 RunCommand(['git', 'config', '--replace-all', | |
| 710 'rietveld.extracc', ','.join(watchers)]) | |
| 711 | |
| 712 return presubmit_support.DoPresubmitChecks(change, committing, | |
| 
M-A Ruel
2011/03/11 14:42:52
It changes from running out of process to in-proce
 | |
| 713 verbose=None, output_stream=sys.stdout, input_stream=sys.stdin, | |
| 714 default_presubmit=None, may_prompt=None) | |
| 679 | 715 | 
| 680 | 716 | 
| 681 def CMDpresubmit(parser, args): | 717 def CMDpresubmit(parser, args): | 
| 682 """run presubmit tests on the current changelist""" | 718 """run presubmit tests on the current changelist""" | 
| 683 parser.add_option('--upload', action='store_true', | 719 parser.add_option('--upload', action='store_true', | 
| 684 help='Run upload hook instead of the push/dcommit hook') | 720 help='Run upload hook instead of the push/dcommit hook') | 
| 685 (options, args) = parser.parse_args(args) | 721 (options, args) = parser.parse_args(args) | 
| 686 | 722 | 
| 687 # Make sure index is up-to-date before running diff-index. | 723 # Make sure index is up-to-date before running diff-index. | 
| 688 RunGit(['update-index', '--refresh', '-q'], error_ok=True) | 724 RunGit(['update-index', '--refresh', '-q'], error_ok=True) | 
| 689 if RunGit(['diff-index', 'HEAD']): | 725 if RunGit(['diff-index', 'HEAD']): | 
| 690 # TODO(maruel): Is this really necessary? | 726 # TODO(maruel): Is this really necessary? | 
| 691 print 'Cannot presubmit with a dirty tree. You must commit locally first.' | 727 print 'Cannot presubmit with a dirty tree. You must commit locally first.' | 
| 692 return 1 | 728 return 1 | 
| 693 | 729 | 
| 694 if args: | 730 if args: | 
| 695 base_branch = args[0] | 731 base_branch = args[0] | 
| 696 else: | 732 else: | 
| 697 # Default to diffing against the "upstream" branch. | 733 # Default to diffing against the "upstream" branch. | 
| 698 base_branch = Changelist().GetUpstreamBranch() | 734 base_branch = Changelist().GetUpstreamBranch() | 
| 699 | 735 | 
| 700 if options.upload: | 736 if options.upload: | 
| 701 print '*** Presubmit checks for UPLOAD would report: ***' | 737 print '*** Presubmit checks for UPLOAD would report: ***' | 
| 702 return not RunHook(PREUPLOAD_HOOK, upstream_branch=base_branch, | 738 return RunHook(committing=False, upstream_branch=base_branch) | 
| 703 error_ok=True) | |
| 704 else: | 739 else: | 
| 705 print '*** Presubmit checks for DCOMMIT would report: ***' | 740 print '*** Presubmit checks for DCOMMIT would report: ***' | 
| 706 return not RunHook(PREDCOMMIT_HOOK, upstream_branch=base_branch, | 741 return RunHook(committing=True, upstream_branch=base_branch) | 
| 707 error_ok=True) | |
| 708 | 742 | 
| 709 | 743 | 
| 710 @usage('[args to "git diff"]') | 744 @usage('[args to "git diff"]') | 
| 711 def CMDupload(parser, args): | 745 def CMDupload(parser, args): | 
| 712 """upload the current changelist to codereview""" | 746 """upload the current changelist to codereview""" | 
| 713 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', | 747 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', | 
| 714 help='bypass upload presubmit hook') | 748 help='bypass upload presubmit hook') | 
| 715 parser.add_option('-m', dest='message', help='message for patch') | 749 parser.add_option('-m', dest='message', help='message for patch') | 
| 716 parser.add_option('-r', '--reviewers', | 750 parser.add_option('-r', '--reviewers', | 
| 717 help='reviewer email addresses') | 751 help='reviewer email addresses') | 
| (...skipping 18 matching lines...) Expand all Loading... | |
| 736 | 770 | 
| 737 cl = Changelist() | 771 cl = Changelist() | 
| 738 if args: | 772 if args: | 
| 739 base_branch = args[0] | 773 base_branch = args[0] | 
| 740 else: | 774 else: | 
| 741 # Default to diffing against the "upstream" branch. | 775 # Default to diffing against the "upstream" branch. | 
| 742 base_branch = cl.GetUpstreamBranch() | 776 base_branch = cl.GetUpstreamBranch() | 
| 743 args = [base_branch + "..."] | 777 args = [base_branch + "..."] | 
| 744 | 778 | 
| 745 if not options.bypass_hooks: | 779 if not options.bypass_hooks: | 
| 746 RunHook(PREUPLOAD_HOOK, upstream_branch=base_branch, error_ok=False) | 780 RunHook(committing=False, upstream_branch=base_branch) | 
| 747 | 781 | 
| 748 # --no-ext-diff is broken in some versions of Git, so try to work around | 782 # --no-ext-diff is broken in some versions of Git, so try to work around | 
| 749 # this by overriding the environment (but there is still a problem if the | 783 # this by overriding the environment (but there is still a problem if the | 
| 750 # git config key "diff.external" is used). | 784 # git config key "diff.external" is used). | 
| 751 env = os.environ.copy() | 785 env = os.environ.copy() | 
| 752 if 'GIT_EXTERNAL_DIFF' in env: | 786 if 'GIT_EXTERNAL_DIFF' in env: | 
| 753 del env['GIT_EXTERNAL_DIFF'] | 787 del env['GIT_EXTERNAL_DIFF'] | 
| 754 subprocess.call(['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, | 788 subprocess.call(['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, | 
| 755 env=env) | 789 env=env) | 
| 756 | 790 | 
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 893 '--pretty=format:%H']) | 927 '--pretty=format:%H']) | 
| 894 extra_commits = RunGit(['rev-list', '^' + svn_head, base_branch]) | 928 extra_commits = RunGit(['rev-list', '^' + svn_head, base_branch]) | 
| 895 if extra_commits: | 929 if extra_commits: | 
| 896 print ('This branch has %d additional commits not upstreamed yet.' | 930 print ('This branch has %d additional commits not upstreamed yet.' | 
| 897 % len(extra_commits.splitlines())) | 931 % len(extra_commits.splitlines())) | 
| 898 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' | 932 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' | 
| 899 'before attempting to %s.' % (base_branch, cmd)) | 933 'before attempting to %s.' % (base_branch, cmd)) | 
| 900 return 1 | 934 return 1 | 
| 901 | 935 | 
| 902 if not options.force and not options.bypass_hooks: | 936 if not options.force and not options.bypass_hooks: | 
| 903 RunHook(PREDCOMMIT_HOOK, upstream_branch=base_branch, error_ok=False) | 937 RunHook(committing=False, upstream_branch=base_branch) | 
| 904 | 938 | 
| 905 if cmd == 'dcommit': | 939 if cmd == 'dcommit': | 
| 906 # Check the tree status if the tree status URL is set. | 940 # Check the tree status if the tree status URL is set. | 
| 907 status = GetTreeStatus() | 941 status = GetTreeStatus() | 
| 908 if 'closed' == status: | 942 if 'closed' == status: | 
| 909 print ('The tree is closed. Please wait for it to reopen. Use ' | 943 print ('The tree is closed. Please wait for it to reopen. Use ' | 
| 910 '"git cl dcommit -f" to commit on a closed tree.') | 944 '"git cl dcommit -f" to commit on a closed tree.') | 
| 911 return 1 | 945 return 1 | 
| 912 elif 'unknown' == status: | 946 elif 'unknown' == status: | 
| 913 print ('Unable to determine tree status. Please verify manually and ' | 947 print ('Unable to determine tree status. Please verify manually and ' | 
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1021 else: | 1055 else: | 
| 1022 return 1 | 1056 return 1 | 
| 1023 viewvc_url = settings.GetViewVCUrl() | 1057 viewvc_url = settings.GetViewVCUrl() | 
| 1024 if viewvc_url and revision: | 1058 if viewvc_url and revision: | 
| 1025 cl.description += ('\n\nCommitted: ' + viewvc_url + revision) | 1059 cl.description += ('\n\nCommitted: ' + viewvc_url + revision) | 
| 1026 print ('Closing issue ' | 1060 print ('Closing issue ' | 
| 1027 '(you may be prompted for your codereview password)...') | 1061 '(you may be prompted for your codereview password)...') | 
| 1028 cl.CloseIssue() | 1062 cl.CloseIssue() | 
| 1029 cl.SetIssue(0) | 1063 cl.SetIssue(0) | 
| 1030 | 1064 | 
| 1031 if retcode == 0: | |
| 1032 hook = POSTUPSTREAM_HOOK_PATTERN % cmd | |
| 1033 if os.path.isfile(hook): | |
| 1034 RunHook(hook, upstream_branch=base_branch, error_ok=True) | |
| 1035 | |
| 1036 return 0 | 1065 return 0 | 
| 1037 | 1066 | 
| 1038 | 1067 | 
| 1039 @usage('[upstream branch to apply against]') | 1068 @usage('[upstream branch to apply against]') | 
| 1040 def CMDdcommit(parser, args): | 1069 def CMDdcommit(parser, args): | 
| 1041 """commit the current changelist via git-svn""" | 1070 """commit the current changelist via git-svn""" | 
| 1042 if not settings.GetIsGitSvn(): | 1071 if not settings.GetIsGitSvn(): | 
| 1043 message = """This doesn't appear to be an SVN repository. | 1072 message = """This doesn't appear to be an SVN repository. | 
| 1044 If your project has a git mirror with an upstream SVN master, you probably need | 1073 If your project has a git mirror with an upstream SVN master, you probably need | 
| 1045 to run 'git svn init', see your project's git mirror documentation. | 1074 to run 'git svn init', see your project's git mirror documentation. | 
| (...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1279 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' | 1308 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' | 
| 1280 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 1309 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 
| 1281 | 1310 | 
| 1282 # Not a known command. Default to help. | 1311 # Not a known command. Default to help. | 
| 1283 GenUsage(parser, 'help') | 1312 GenUsage(parser, 'help') | 
| 1284 return CMDhelp(parser, argv) | 1313 return CMDhelp(parser, argv) | 
| 1285 | 1314 | 
| 1286 | 1315 | 
| 1287 if __name__ == '__main__': | 1316 if __name__ == '__main__': | 
| 1288 sys.exit(main(sys.argv[1:])) | 1317 sys.exit(main(sys.argv[1:])) | 
| OLD | NEW |