OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 6 # Copyright (C) 2008 Evan Martin <martine@danga.com> |
7 | 7 |
8 """A git-command for integrating reviews on Rietveld.""" | 8 """A git-command for integrating reviews on Rietveld.""" |
9 | 9 |
10 import logging | 10 import logging |
11 import optparse | 11 import optparse |
12 import os | 12 import os |
13 import re | 13 import re |
14 import sys | 14 import sys |
15 import tempfile | |
16 import textwrap | 15 import textwrap |
17 import urlparse | 16 import urlparse |
18 import urllib2 | 17 import urllib2 |
19 | 18 |
20 try: | 19 try: |
21 import readline # pylint: disable=F0401,W0611 | 20 import readline # pylint: disable=F0401,W0611 |
22 except ImportError: | 21 except ImportError: |
23 pass | 22 pass |
24 | 23 |
25 try: | 24 try: |
26 import simplejson as json # pylint: disable=F0401 | 25 import simplejson as json # pylint: disable=F0401 |
27 except ImportError: | 26 except ImportError: |
28 try: | 27 try: |
29 import json # pylint: disable=F0401 | 28 import json # pylint: disable=F0401 |
30 except ImportError: | 29 except ImportError: |
31 # Fall back to the packaged version. | 30 # Fall back to the packaged version. |
32 sys.path.append(os.path.join(os.path.dirname(__file__), 'third_party')) | 31 sys.path.append(os.path.join(os.path.dirname(__file__), 'third_party')) |
33 import simplejson as json # pylint: disable=F0401 | 32 import simplejson as json # pylint: disable=F0401 |
34 | 33 |
35 | 34 |
36 from third_party import upload | 35 from third_party import upload |
37 import breakpad # pylint: disable=W0611 | 36 import breakpad # pylint: disable=W0611 |
38 import fix_encoding | 37 import fix_encoding |
| 38 import gclient_utils |
39 import presubmit_support | 39 import presubmit_support |
40 import rietveld | 40 import rietveld |
41 import scm | 41 import scm |
42 import subprocess2 | 42 import subprocess2 |
43 import watchlists | 43 import watchlists |
44 | 44 |
45 | 45 |
46 DEFAULT_SERVER = 'http://codereview.appspot.com' | 46 DEFAULT_SERVER = 'http://codereview.appspot.com' |
47 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s' | 47 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s' |
48 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' | 48 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' |
(...skipping 592 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
641 # This will displayed on the codereview site. | 641 # This will displayed on the codereview site. |
642 # The first line will also be used as the subject of the review. | 642 # The first line will also be used as the subject of the review. |
643 """ | 643 """ |
644 initial_text += self.description | 644 initial_text += self.description |
645 if 'R=' not in self.description and self.reviewers: | 645 if 'R=' not in self.description and self.reviewers: |
646 initial_text += '\nR=' + self.reviewers | 646 initial_text += '\nR=' + self.reviewers |
647 if 'BUG=' not in self.description: | 647 if 'BUG=' not in self.description: |
648 initial_text += '\nBUG=' | 648 initial_text += '\nBUG=' |
649 if 'TEST=' not in self.description: | 649 if 'TEST=' not in self.description: |
650 initial_text += '\nTEST=' | 650 initial_text += '\nTEST=' |
651 self._ParseDescription(UserEditedLog(initial_text)) | 651 content = gclient_utils.RunEditor(initial_text, True) |
| 652 if not content: |
| 653 DieWithError('Running editor failed') |
| 654 content = re.compile(r'^#.*$', re.MULTILINE).sub('', content).strip() |
| 655 if not content: |
| 656 DieWithError('No CL description, aborting') |
| 657 self._ParseDescription(content) |
652 | 658 |
653 def _ParseDescription(self, description): | 659 def _ParseDescription(self, description): |
654 if not description: | 660 if not description: |
655 self.description = description | 661 self.description = description |
656 return | 662 return |
657 | 663 |
658 parsed_lines = [] | 664 parsed_lines = [] |
659 reviewers_regexp = re.compile('\s*R=(.+)') | 665 reviewers_regexp = re.compile('\s*R=(.+)') |
660 reviewers = '' | 666 reviewers = '' |
661 subject = '' | 667 subject = '' |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
817 log_args = [args[0] + '..'] | 823 log_args = [args[0] + '..'] |
818 elif len(args) == 1 and args[0].endswith('...'): | 824 elif len(args) == 1 and args[0].endswith('...'): |
819 log_args = [args[0][:-1]] | 825 log_args = [args[0][:-1]] |
820 elif len(args) == 2: | 826 elif len(args) == 2: |
821 log_args = [args[0] + '..' + args[1]] | 827 log_args = [args[0] + '..' + args[1]] |
822 else: | 828 else: |
823 log_args = args[:] # Hope for the best! | 829 log_args = args[:] # Hope for the best! |
824 return RunGit(['log', '--pretty=format:%s\n\n%b'] + log_args) | 830 return RunGit(['log', '--pretty=format:%s\n\n%b'] + log_args) |
825 | 831 |
826 | 832 |
827 def UserEditedLog(starting_text): | |
828 """Given some starting text, let the user edit it and return the result.""" | |
829 editor = os.getenv('EDITOR', 'vi') | |
830 | |
831 (file_handle, filename) = tempfile.mkstemp() | |
832 fileobj = os.fdopen(file_handle, 'w') | |
833 fileobj.write(starting_text) | |
834 fileobj.close() | |
835 | |
836 # Open up the default editor in the system to get the CL description. | |
837 try: | |
838 cmd = '%s %s' % (editor, filename) | |
839 if sys.platform == 'win32' and os.environ.get('TERM') == 'msys': | |
840 # Msysgit requires the usage of 'env' to be present. | |
841 cmd = 'env ' + cmd | |
842 # shell=True to allow the shell to handle all forms of quotes in $EDITOR. | |
843 try: | |
844 subprocess2.check_call(cmd, shell=True) | |
845 except subprocess2.CalledProcessError, e: | |
846 DieWithError('Editor returned %d' % e.returncode) | |
847 fileobj = open(filename) | |
848 text = fileobj.read() | |
849 fileobj.close() | |
850 finally: | |
851 os.remove(filename) | |
852 | |
853 if not text: | |
854 return | |
855 | |
856 stripcomment_re = re.compile(r'^#.*$', re.MULTILINE) | |
857 return stripcomment_re.sub('', text).strip() | |
858 | |
859 | |
860 def ConvertToInteger(inputval): | 833 def ConvertToInteger(inputval): |
861 """Convert a string to integer, but returns either an int or None.""" | 834 """Convert a string to integer, but returns either an int or None.""" |
862 try: | 835 try: |
863 return int(inputval) | 836 return int(inputval) |
864 except (TypeError, ValueError): | 837 except (TypeError, ValueError): |
865 return None | 838 return None |
866 | 839 |
867 | 840 |
868 def CMDpresubmit(parser, args): | 841 def CMDpresubmit(parser, args): |
869 """run presubmit tests on the current changelist""" | 842 """run presubmit tests on the current changelist""" |
(...skipping 570 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1440 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 1413 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) |
1441 | 1414 |
1442 # Not a known command. Default to help. | 1415 # Not a known command. Default to help. |
1443 GenUsage(parser, 'help') | 1416 GenUsage(parser, 'help') |
1444 return CMDhelp(parser, argv) | 1417 return CMDhelp(parser, argv) |
1445 | 1418 |
1446 | 1419 |
1447 if __name__ == '__main__': | 1420 if __name__ == '__main__': |
1448 fix_encoding.fix_encoding() | 1421 fix_encoding.fix_encoding() |
1449 sys.exit(main(sys.argv[1:])) | 1422 sys.exit(main(sys.argv[1:])) |
OLD | NEW |