Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(146)

Side by Side Diff: git_cl/git_cl.py

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
Patch Set: rebase properly Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698