OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2006-2009 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 """Wrapper script around Rietveld's upload.py that groups files into | 6 """\ |
7 changelists.""" | 7 Wrapper script around Rietveld's upload.py that simplifies working with groups |
| 8 of files. |
| 9 """ |
8 | 10 |
9 import getpass | 11 import getpass |
10 import os | 12 import os |
11 import random | 13 import random |
12 import re | 14 import re |
13 import shutil | 15 import shutil |
14 import string | 16 import string |
15 import subprocess | 17 import subprocess |
16 import sys | 18 import sys |
17 import tempfile | 19 import tempfile |
18 import time | 20 import time |
19 from third_party import upload | 21 from third_party import upload |
20 import urllib2 | 22 import urllib2 |
21 | 23 |
22 __pychecker__ = 'unusednames=breakpad' | 24 __pychecker__ = 'unusednames=breakpad' |
23 import breakpad | 25 import breakpad |
24 __pychecker__ = '' | 26 __pychecker__ = '' |
25 | 27 |
26 # gcl now depends on gclient. | 28 # gcl now depends on gclient. |
27 from scm import SVN | 29 from scm import SVN |
28 import gclient_utils | 30 import gclient_utils |
29 | 31 |
30 __version__ = '1.1.4' | 32 __version__ = '1.2' |
31 | 33 |
32 | 34 |
33 CODEREVIEW_SETTINGS = { | 35 CODEREVIEW_SETTINGS = { |
34 # Ideally, we want to set |CODE_REVIEW_SERVER| to a generic server like | 36 # Ideally, we want to set |CODE_REVIEW_SERVER| to a generic server like |
35 # codereview.appspot.com and remove |CC_LIST| and |VIEW_VC|. In practice, we | 37 # codereview.appspot.com and remove |CC_LIST| and |VIEW_VC|. In practice, we |
36 # need these settings so developers making changes in directories such as | 38 # need these settings so developers making changes in directories such as |
37 # Chromium's src/third_party/WebKit will send change lists to the correct | 39 # Chromium's src/third_party/WebKit will send change lists to the correct |
38 # server. | 40 # server. |
39 # | 41 # |
40 # To make gcl send reviews to a different server, check in a file named | 42 # To make gcl send reviews to a different server, check in a file named |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
75 home_vars.append('USERPROFILE') | 77 home_vars.append('USERPROFILE') |
76 for home_var in home_vars: | 78 for home_var in home_vars: |
77 home = os.getenv(home_var) | 79 home = os.getenv(home_var) |
78 if home != None: | 80 if home != None: |
79 full_path = os.path.join(home, filename) | 81 full_path = os.path.join(home, filename) |
80 if os.path.exists(full_path): | 82 if os.path.exists(full_path): |
81 return full_path | 83 return full_path |
82 return None | 84 return None |
83 | 85 |
84 | 86 |
85 def UnknownFiles(extra_args): | 87 def UnknownFiles(): |
86 """Runs svn status and returns unknown files. | 88 """Runs svn status and returns unknown files.""" |
87 | 89 return [item[1] for item in SVN.CaptureStatus([]) if item[0][0] == '?'] |
88 Any args in |extra_args| are passed to the tool to support giving alternate | |
89 code locations. | |
90 """ | |
91 return [item[1] for item in SVN.CaptureStatus(extra_args) | |
92 if item[0][0] == '?'] | |
93 | 90 |
94 | 91 |
95 def GetRepositoryRoot(): | 92 def GetRepositoryRoot(): |
96 """Returns the top level directory of the current repository. | 93 """Returns the top level directory of the current repository. |
97 | 94 |
98 The directory is returned as an absolute path. | 95 The directory is returned as an absolute path. |
99 """ | 96 """ |
100 global REPOSITORY_ROOT | 97 global REPOSITORY_ROOT |
101 if not REPOSITORY_ROOT: | 98 if not REPOSITORY_ROOT: |
102 REPOSITORY_ROOT = SVN.GetCheckoutRoot(os.getcwd()) | 99 REPOSITORY_ROOT = SVN.GetCheckoutRoot(os.getcwd()) |
(...skipping 502 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
605 continue | 602 continue |
606 note = "" | 603 note = "" |
607 change_info = ChangeInfo.Load(cl_name, GetRepositoryRoot(), | 604 change_info = ChangeInfo.Load(cl_name, GetRepositoryRoot(), |
608 fail_on_not_found=True, update_status=False) | 605 fail_on_not_found=True, update_status=False) |
609 if len(change_info.GetFiles()) != len(files[cl_name]): | 606 if len(change_info.GetFiles()) != len(files[cl_name]): |
610 note = " (Note: this changelist contains files outside this directory)" | 607 note = " (Note: this changelist contains files outside this directory)" |
611 print "\n--- Changelist " + cl_name + note + ":" | 608 print "\n--- Changelist " + cl_name + note + ":" |
612 for filename in files[cl_name]: | 609 for filename in files[cl_name]: |
613 print "".join(filename) | 610 print "".join(filename) |
614 if show_unknown_files: | 611 if show_unknown_files: |
615 unknown_files = UnknownFiles([]) | 612 unknown_files = UnknownFiles() |
616 if (files.get('') or (show_unknown_files and len(unknown_files))): | 613 if (files.get('') or (show_unknown_files and len(unknown_files))): |
617 print "\n--- Not in any changelist:" | 614 print "\n--- Not in any changelist:" |
618 for item in files.get('', []): | 615 for item in files.get('', []): |
619 print "".join(item) | 616 print "".join(item) |
620 if show_unknown_files: | 617 if show_unknown_files: |
621 for filename in unknown_files: | 618 for filename in unknown_files: |
622 print "? %s" % filename | 619 print "? %s" % filename |
623 return 0 | 620 return 0 |
624 | 621 |
625 | 622 |
626 def CMDopened(args): | |
627 """Lists modified files in the current directory down.""" | |
628 if args: | |
629 ErrorExit("Doesn't support arguments") | |
630 return ListFiles(False) | |
631 | |
632 | |
633 def CMDstatus(args): | |
634 """Lists modified and unknown files in the current directory down.""" | |
635 if args: | |
636 ErrorExit("Doesn't support arguments") | |
637 return ListFiles(True) | |
638 | |
639 | |
640 def CMDhelp(args): | |
641 """Prints this help or help for the given command.""" | |
642 if args and len(args) > 2: | |
643 if args[2] == 'try': | |
644 return TryChange(None, ['--help'], swallow_exception=False) | |
645 if args[2] == 'upload': | |
646 upload.RealMain(['upload.py', '--help']) | |
647 return 0 | |
648 | |
649 print ( | |
650 """GCL is a wrapper for Subversion that simplifies working with groups of files. | |
651 version """ + __version__ + """ | |
652 | |
653 Basic commands: | |
654 ----------------------------------------- | |
655 gcl change change_name | |
656 Add/remove files to a changelist. Only scans the current directory and | |
657 subdirectories. | |
658 | |
659 gcl upload change_name [-r reviewer1@gmail.com,reviewer2@gmail.com,...] | |
660 [--send_mail] [--no_try] [--no_presubmit] | |
661 [--no_watchlists] | |
662 Uploads the changelist to the server for review. | |
663 (You can create the file '.gcl_upload_no_try' in your home dir to | |
664 skip the automatic tries.) | |
665 | |
666 gcl commit change_name [--no_presubmit] | |
667 Commits the changelist to the repository. | |
668 | |
669 gcl lint change_name | |
670 Check all the files in the changelist for possible style violations. | |
671 | |
672 Advanced commands: | |
673 ----------------------------------------- | |
674 gcl delete change_name | |
675 Deletes a changelist. | |
676 | |
677 gcl diff change_name | |
678 Diffs all files in the changelist. | |
679 | |
680 gcl presubmit change_name | |
681 Runs presubmit checks without uploading the changelist. | |
682 | |
683 gcl diff | |
684 Diffs all files in the current directory and subdirectories that aren't in | |
685 a changelist. | |
686 | |
687 gcl description | |
688 Prints the description of the specified change to stdout. | |
689 | |
690 gcl changes | |
691 Lists all the the changelists and the files in them. | |
692 | |
693 gcl rename <old-name> <new-name> | |
694 Renames an existing change. | |
695 | |
696 gcl nothave [optional directory] | |
697 Lists files unknown to Subversion. | |
698 | |
699 gcl opened | |
700 Lists modified files in the current directory and subdirectories. | |
701 | |
702 gcl settings | |
703 Print the code review settings for this directory. | |
704 | |
705 gcl status | |
706 Lists modified and unknown files in the current directory and | |
707 subdirectories. | |
708 | |
709 gcl try change_name | |
710 Sends the change to the tryserver so a trybot can do a test run on your | |
711 code. To send multiple changes as one path, use a comma-separated list | |
712 of changenames. | |
713 --> Use 'gcl help try' for more information! | |
714 | |
715 gcl deleteempties | |
716 Deletes all changelists that have no files associated with them. Careful, | |
717 you can lose your descriptions. | |
718 | |
719 gcl help [command] | |
720 Print this help menu, or help for the given command if it exists. | |
721 """) | |
722 return 0 | |
723 | |
724 | |
725 def GetEditor(): | 623 def GetEditor(): |
726 editor = os.environ.get("SVN_EDITOR") | 624 editor = os.environ.get("SVN_EDITOR") |
727 if not editor: | 625 if not editor: |
728 editor = os.environ.get("EDITOR") | 626 editor = os.environ.get("EDITOR") |
729 | 627 |
730 if not editor: | 628 if not editor: |
731 if sys.platform.startswith("win"): | 629 if sys.platform.startswith("win"): |
732 editor = "notepad" | 630 editor = "notepad" |
733 else: | 631 else: |
734 editor = "vi" | 632 editor = "vi" |
735 | 633 |
736 return editor | 634 return editor |
737 | 635 |
738 | 636 |
739 def GenerateDiff(files, root=None): | 637 def GenerateDiff(files, root=None): |
740 return SVN.GenerateDiff(files, root=root) | 638 return SVN.GenerateDiff(files, root=root) |
741 | 639 |
742 | 640 |
743 def OptionallyDoPresubmitChecks(change_info, committing, args): | 641 def OptionallyDoPresubmitChecks(change_info, committing, args): |
744 if FilterFlag(args, "--no_presubmit") or FilterFlag(args, "--force"): | 642 if FilterFlag(args, "--no_presubmit") or FilterFlag(args, "--force"): |
745 return True | 643 return True |
746 return DoPresubmitChecks(change_info, committing, True) | 644 return DoPresubmitChecks(change_info, committing, True) |
747 | 645 |
748 | 646 |
| 647 def defer_attributes(a, b): |
| 648 """Copy attributes from an object (like a function) to another.""" |
| 649 for x in dir(a): |
| 650 if not getattr(b, x, None): |
| 651 setattr(b, x, getattr(a, x)) |
| 652 |
| 653 |
749 def need_change(function): | 654 def need_change(function): |
750 """Converts args -> change_info.""" | 655 """Converts args -> change_info.""" |
751 def hook(args): | 656 def hook(args): |
752 if not len(args) == 1: | 657 if not len(args) == 1: |
753 ErrorExit("You need to pass a change list name") | 658 ErrorExit("You need to pass a change list name") |
754 change_info = ChangeInfo.Load(args[0], GetRepositoryRoot(), True, True) | 659 change_info = ChangeInfo.Load(args[0], GetRepositoryRoot(), True, True) |
755 return function(change_info) | 660 return function(change_info) |
| 661 defer_attributes(function, hook) |
| 662 hook.need_change = True |
| 663 hook.no_args = True |
756 return hook | 664 return hook |
757 | 665 |
758 | 666 |
759 def CMDupload(args): | 667 def need_change_and_args(function): |
760 if not args: | 668 """Converts args -> change_info.""" |
761 ErrorExit("You need to pass a change list name") | 669 def hook(args): |
762 change_info = ChangeInfo.Load(args.pop(0), GetRepositoryRoot(), True, True) | 670 change_info = ChangeInfo.Load(args.pop(0), GetRepositoryRoot(), True, True) |
| 671 return function(change_info, args) |
| 672 defer_attributes(function, hook) |
| 673 hook.need_change = True |
| 674 return hook |
| 675 |
| 676 |
| 677 def no_args(function): |
| 678 """Make sure no args are passed.""" |
| 679 def hook(args): |
| 680 if args: |
| 681 ErrorExit("Doesn't support arguments") |
| 682 return function() |
| 683 defer_attributes(function, hook) |
| 684 hook.no_args = True |
| 685 return hook |
| 686 |
| 687 |
| 688 def attrs(**kwargs): |
| 689 """Decorate a function with new attributes.""" |
| 690 def decorate(function): |
| 691 for k in kwargs: |
| 692 setattr(function, k, kwargs[k]) |
| 693 return function |
| 694 return decorate |
| 695 |
| 696 |
| 697 @no_args |
| 698 def CMDopened(): |
| 699 """Lists modified files in the current directory down.""" |
| 700 return ListFiles(False) |
| 701 |
| 702 |
| 703 @no_args |
| 704 def CMDstatus(): |
| 705 """Lists modified and unknown files in the current directory down.""" |
| 706 return ListFiles(True) |
| 707 |
| 708 |
| 709 @need_change_and_args |
| 710 @attrs(usage='[--no_try] [--no_presubmit] [--clobber]\n' |
| 711 ' [--no_watchlists]') |
| 712 def CMDupload(change_info, args): |
| 713 """Uploads the changelist to the server for review. |
| 714 |
| 715 (You can create the file '.gcl_upload_no_try' in your home dir to |
| 716 skip the automatic tries.) |
| 717 """ |
763 if not change_info.GetFiles(): | 718 if not change_info.GetFiles(): |
764 print "Nothing to upload, changelist is empty." | 719 print "Nothing to upload, changelist is empty." |
765 return 0 | 720 return 0 |
766 if not OptionallyDoPresubmitChecks(change_info, False, args): | 721 if not OptionallyDoPresubmitChecks(change_info, False, args): |
767 return 1 | 722 return 1 |
768 # Might want to support GetInfoDir()/GetRepositoryRoot() like | 723 # Might want to support GetInfoDir()/GetRepositoryRoot() like |
769 # CheckHomeForFile() so the skip of tries can be per tree basis instead | 724 # CheckHomeForFile() so the skip of tries can be per tree basis instead |
770 # of user global. | 725 # of user global. |
771 no_try = FilterFlag(args, "--no_try") or \ | 726 no_try = (FilterFlag(args, "--no_try") or |
772 FilterFlag(args, "--no-try") or \ | 727 FilterFlag(args, "--no-try") or |
773 not (CheckHomeForFile(".gcl_upload_no_try") is None) | 728 not (CheckHomeForFile(".gcl_upload_no_try") is None)) |
774 no_watchlists = FilterFlag(args, "--no_watchlists") or \ | 729 no_watchlists = (FilterFlag(args, "--no_watchlists") or |
775 FilterFlag(args, "--no-watchlists") | 730 FilterFlag(args, "--no-watchlists")) |
776 | 731 |
777 # Map --send-mail to --send_mail | 732 # Map --send-mail to --send_mail |
778 if FilterFlag(args, "--send-mail"): | 733 if FilterFlag(args, "--send-mail"): |
779 args.append("--send_mail") | 734 args.append("--send_mail") |
780 | 735 |
781 # Supports --clobber for the try server. | 736 # Supports --clobber for the try server. |
782 clobber = FilterFlag(args, "--clobber") | 737 clobber = FilterFlag(args, "--clobber") |
783 | 738 |
784 # Disable try when the server is overridden. | 739 # Disable try when the server is overridden. |
785 server_1 = re.compile(r"^-s\b.*") | 740 server_1 = re.compile(r"^-s\b.*") |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
870 if try_on_upload and try_on_upload.lower() == 'true': | 825 if try_on_upload and try_on_upload.lower() == 'true': |
871 trychange_args = [] | 826 trychange_args = [] |
872 if clobber: | 827 if clobber: |
873 trychange_args.append('--clobber') | 828 trychange_args.append('--clobber') |
874 return TryChange(change_info, trychange_args, swallow_exception=True) | 829 return TryChange(change_info, trychange_args, swallow_exception=True) |
875 return 0 | 830 return 0 |
876 | 831 |
877 | 832 |
878 @need_change | 833 @need_change |
879 def CMDpresubmit(change_info): | 834 def CMDpresubmit(change_info): |
880 """Runs presubmit checks on the change.""" | 835 """Runs presubmit checks on the change. |
| 836 |
| 837 The actual presubmit code is implemented in presubmit_support.py and looks |
| 838 for PRESUBMIT.py files.""" |
881 if not change_info.GetFiles(): | 839 if not change_info.GetFiles(): |
882 print "Nothing to presubmit check, changelist is empty." | 840 print "Nothing to presubmit check, changelist is empty." |
883 return 0 | 841 return 0 |
884 | 842 |
885 print "*** Presubmit checks for UPLOAD would report: ***" | 843 print "*** Presubmit checks for UPLOAD would report: ***" |
886 result = DoPresubmitChecks(change_info, False, False) | 844 result = DoPresubmitChecks(change_info, False, False) |
887 | 845 |
888 print "\n*** Presubmit checks for COMMIT would report: ***" | 846 print "\n*** Presubmit checks for COMMIT would report: ***" |
889 result &= DoPresubmitChecks(change_info, True, False) | 847 result &= DoPresubmitChecks(change_info, True, False) |
890 return not result | 848 return not result |
(...skipping 19 matching lines...) Expand all Loading... |
910 file_list = change_info.GetFileNames() | 868 file_list = change_info.GetFileNames() |
911 else: | 869 else: |
912 trychange_args.extend(args) | 870 trychange_args.extend(args) |
913 file_list = None | 871 file_list = None |
914 return trychange.TryChange(trychange_args, | 872 return trychange.TryChange(trychange_args, |
915 file_list=file_list, | 873 file_list=file_list, |
916 swallow_exception=swallow_exception, | 874 swallow_exception=swallow_exception, |
917 prog='gcl try') | 875 prog='gcl try') |
918 | 876 |
919 | 877 |
920 def CMDcommit(args): | 878 @need_change_and_args |
921 if not args: | 879 @attrs(usage='[--no_presubmit]') |
922 ErrorExit("You need to pass a change list name") | 880 def CMDcommit(change_list, args): |
923 change_info = ChangeInfo.Load(args.pop(0), GetRepositoryRoot(), True, True) | 881 """Commits the changelist to the repository.""" |
924 if not change_info.GetFiles(): | 882 if not change_info.GetFiles(): |
925 print "Nothing to commit, changelist is empty." | 883 print "Nothing to commit, changelist is empty." |
926 return 1 | 884 return 1 |
927 if not OptionallyDoPresubmitChecks(change_info, True, args): | 885 if not OptionallyDoPresubmitChecks(change_info, True, args): |
928 return 1 | 886 return 1 |
929 | 887 |
930 # We face a problem with svn here: Let's say change 'bleh' modifies | 888 # We face a problem with svn here: Let's say change 'bleh' modifies |
931 # svn:ignore on dir1\. but another unrelated change 'pouet' modifies | 889 # svn:ignore on dir1\. but another unrelated change 'pouet' modifies |
932 # dir1\foo.cc. When the user `gcl commit bleh`, foo.cc is *also committed*. | 890 # dir1\foo.cc. When the user `gcl commit bleh`, foo.cc is *also committed*. |
933 # The only fix is to use --non-recursive but that has its issues too: | 891 # The only fix is to use --non-recursive but that has its issues too: |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
971 viewvc_url = GetCodeReviewSetting("VIEW_VC") | 929 viewvc_url = GetCodeReviewSetting("VIEW_VC") |
972 change_info.description = change_info.description + '\n' | 930 change_info.description = change_info.description + '\n' |
973 if viewvc_url: | 931 if viewvc_url: |
974 change_info.description += "\nCommitted: " + viewvc_url + revision | 932 change_info.description += "\nCommitted: " + viewvc_url + revision |
975 change_info.CloseIssue() | 933 change_info.CloseIssue() |
976 os.chdir(previous_cwd) | 934 os.chdir(previous_cwd) |
977 return 0 | 935 return 0 |
978 | 936 |
979 | 937 |
980 def CMDchange(args): | 938 def CMDchange(args): |
981 """Creates/edits a changelist.""" | 939 """Creates or edits a changelist. |
| 940 |
| 941 Only scans the current directory and subdirectories.""" |
982 if len(args) == 0: | 942 if len(args) == 0: |
983 # Generate a random changelist name. | 943 # Generate a random changelist name. |
984 changename = GenerateChangeName() | 944 changename = GenerateChangeName() |
985 elif args[0] == '--force': | 945 elif args[0] == '--force': |
986 changename = GenerateChangeName() | 946 changename = GenerateChangeName() |
987 else: | 947 else: |
988 changename = args[0] | 948 changename = args[0] |
989 change_info = ChangeInfo.Load(changename, GetRepositoryRoot(), False, True) | 949 change_info = ChangeInfo.Load(changename, GetRepositoryRoot(), False, True) |
990 silent = FilterFlag(args, "--silent") | 950 silent = FilterFlag(args, "--silent") |
991 | 951 |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1091 Warn("WARNING: " + MISSING_TEST_MSG) | 1051 Warn("WARNING: " + MISSING_TEST_MSG) |
1092 | 1052 |
1093 # Update the Rietveld issue. | 1053 # Update the Rietveld issue. |
1094 if change_info.issue and change_info.NeedsUpload(): | 1054 if change_info.issue and change_info.NeedsUpload(): |
1095 change_info.UpdateRietveldDescription() | 1055 change_info.UpdateRietveldDescription() |
1096 change_info.needs_upload = False | 1056 change_info.needs_upload = False |
1097 change_info.Save() | 1057 change_info.Save() |
1098 return 0 | 1058 return 0 |
1099 | 1059 |
1100 | 1060 |
1101 @need_change | 1061 @need_change_and_args |
1102 def CMDlint(change_info): | 1062 def CMDlint(change_info, args): |
1103 """Runs cpplint.py on all the files in |change_info|""" | 1063 """Runs cpplint.py on all the files in the change list. |
| 1064 |
| 1065 Checks all the files in the changelist for possible style violations. |
| 1066 """ |
1104 try: | 1067 try: |
1105 import cpplint | 1068 import cpplint |
1106 except ImportError: | 1069 except ImportError: |
1107 ErrorExit("You need to install cpplint.py to lint C++ files.") | 1070 ErrorExit("You need to install cpplint.py to lint C++ files.") |
1108 # Change the current working directory before calling lint so that it | 1071 # Change the current working directory before calling lint so that it |
1109 # shows the correct base. | 1072 # shows the correct base. |
1110 previous_cwd = os.getcwd() | 1073 previous_cwd = os.getcwd() |
1111 os.chdir(change_info.GetLocalRoot()) | 1074 os.chdir(change_info.GetLocalRoot()) |
1112 | 1075 |
1113 # Process cpplints arguments if any. | 1076 # Process cpplints arguments if any. |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1151 verbose=False, | 1114 verbose=False, |
1152 output_stream=sys.stdout, | 1115 output_stream=sys.stdout, |
1153 input_stream=sys.stdin, | 1116 input_stream=sys.stdin, |
1154 default_presubmit=root_presubmit, | 1117 default_presubmit=root_presubmit, |
1155 may_prompt=may_prompt) | 1118 may_prompt=may_prompt) |
1156 if not result and may_prompt: | 1119 if not result and may_prompt: |
1157 print "\nPresubmit errors, can't continue (use --no_presubmit to bypass)" | 1120 print "\nPresubmit errors, can't continue (use --no_presubmit to bypass)" |
1158 return result | 1121 return result |
1159 | 1122 |
1160 | 1123 |
1161 def CMDchanges(args): | 1124 @no_args |
| 1125 def CMDchanges(): |
1162 """Lists all the changelists and their files.""" | 1126 """Lists all the changelists and their files.""" |
1163 if args: | |
1164 ErrorExit("Doesn't support arguments") | |
1165 for cl in GetCLs(): | 1127 for cl in GetCLs(): |
1166 change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True) | 1128 change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True) |
1167 print "\n--- Changelist " + change_info.name + ":" | 1129 print "\n--- Changelist " + change_info.name + ":" |
1168 for filename in change_info.GetFiles(): | 1130 for filename in change_info.GetFiles(): |
1169 print "".join(filename) | 1131 print "".join(filename) |
1170 return 0 | 1132 return 0 |
1171 | 1133 |
1172 | 1134 |
1173 def CMDdeleteempties(args): | 1135 @no_args |
| 1136 def CMDdeleteempties(): |
1174 """Delete all changelists that have no files.""" | 1137 """Delete all changelists that have no files.""" |
1175 if args: | |
1176 ErrorExit("Doesn't support arguments") | |
1177 print "\n--- Deleting:" | 1138 print "\n--- Deleting:" |
1178 for cl in GetCLs(): | 1139 for cl in GetCLs(): |
1179 change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True) | 1140 change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True) |
1180 if not len(change_info._files): | 1141 if not len(change_info._files): |
1181 print change_info.name | 1142 print change_info.name |
1182 change_info.Delete() | 1143 change_info.Delete() |
1183 return 0 | 1144 return 0 |
1184 | 1145 |
1185 | 1146 |
1186 def CMDnothave(args): | 1147 @no_args |
| 1148 def CMDnothave(): |
1187 """Lists files unknown to Subversion.""" | 1149 """Lists files unknown to Subversion.""" |
1188 if args: | 1150 for filename in UnknownFiles(): |
1189 ErrorExit("Doesn't support arguments") | |
1190 for filename in UnknownFiles(args): | |
1191 print "? " + "".join(filename) | 1151 print "? " + "".join(filename) |
1192 return 0 | 1152 return 0 |
1193 | 1153 |
1194 | 1154 |
| 1155 @attrs(usage='<svn options>') |
1195 def CMDdiff(args): | 1156 def CMDdiff(args): |
1196 """Diffs all files in the changelist.""" | 1157 """Diffs all files in the changelist or all files that aren't in a CL.""" |
1197 files = None | 1158 files = None |
1198 if args: | 1159 if args: |
1199 change_info = ChangeInfo.Load(args[0], GetRepositoryRoot(), True, True) | 1160 change_info = ChangeInfo.Load(args.pop(0), GetRepositoryRoot(), True, True) |
1200 args.pop(0) | |
1201 files = change_info.GetFileNames() | 1161 files = change_info.GetFileNames() |
1202 else: | 1162 else: |
1203 files = GetFilesNotInCL() | 1163 files = GetFilesNotInCL() |
1204 return RunShellWithReturnCode(['svn', 'diff'] + files + args, | 1164 return RunShellWithReturnCode(['svn', 'diff'] + files + args, |
1205 print_output=True)[1] | 1165 print_output=True)[1] |
1206 | 1166 |
1207 | 1167 |
1208 def CMDsettings(args): | 1168 @no_args |
1209 """Prints code review settings.""" | 1169 def CMDsettings(): |
1210 __pychecker__ = 'unusednames=args' | 1170 """Prints code review settings for this checkout.""" |
1211 # Force load settings | 1171 # Force load settings |
1212 GetCodeReviewSetting("UNKNOWN"); | 1172 GetCodeReviewSetting("UNKNOWN"); |
1213 del CODEREVIEW_SETTINGS['__just_initialized'] | 1173 del CODEREVIEW_SETTINGS['__just_initialized'] |
1214 print '\n'.join(("%s: %s" % (str(k), str(v)) | 1174 print '\n'.join(("%s: %s" % (str(k), str(v)) |
1215 for (k,v) in CODEREVIEW_SETTINGS.iteritems())) | 1175 for (k,v) in CODEREVIEW_SETTINGS.iteritems())) |
1216 return 0 | 1176 return 0 |
1217 | 1177 |
1218 | 1178 |
1219 @need_change | 1179 @need_change |
1220 def CMDdescription(change_info): | 1180 def CMDdescription(change_info): |
1221 """Prints the description of the specified change to stdout.""" | 1181 """Prints the description of the specified change to stdout.""" |
1222 print change_info.description | 1182 print change_info.description |
1223 return 0 | 1183 return 0 |
1224 | 1184 |
1225 | 1185 |
1226 @need_change | 1186 @need_change |
1227 def CMDdelete(change_info): | 1187 def CMDdelete(change_info): |
1228 """Deletes a changelist.""" | 1188 """Deletes a changelist.""" |
1229 change_info.Delete() | 1189 change_info.Delete() |
1230 return 0 | 1190 return 0 |
1231 | 1191 |
1232 | 1192 |
1233 def CMDtry(args): | 1193 def CMDtry(args): |
1234 """Sends the change to the tryserver so a trybot can do a test run on your | 1194 """Sends the change to the tryserver to do a test run on your code. |
1235 code. | |
1236 | 1195 |
1237 To send multiple changes as one path, use a comma-separated list of | 1196 To send multiple changes as one path, use a comma-separated list of |
1238 changenames. Use 'gcl help try' for more information!""" | 1197 changenames. Use 'gcl help try' for more information!""" |
1239 # When the change contains no file, send the "changename" positional | 1198 # When the change contains no file, send the "changename" positional |
1240 # argument to trychange.py. | 1199 # argument to trychange.py. |
1241 # When the command is 'try' and --patchset is used, the patch to try | 1200 # When the command is 'try' and --patchset is used, the patch to try |
1242 # is on the Rietveld server. | 1201 # is on the Rietveld server. |
1243 if not args: | 1202 if not args: |
1244 ErrorExit("You need to pass a change list name") | 1203 ErrorExit("You need to pass a change list name") |
1245 if args[0].find(',') != -1: | 1204 if args[0].find(',') != -1: |
1246 change_info = LoadChangelistInfoForMultiple(args[0], GetRepositoryRoot(), | 1205 change_info = LoadChangelistInfoForMultiple(args[0], GetRepositoryRoot(), |
1247 True, True) | 1206 True, True) |
1248 else: | 1207 else: |
1249 change_info = ChangeInfo.Load(args[0], GetRepositoryRoot(), | 1208 change_info = ChangeInfo.Load(args[0], GetRepositoryRoot(), |
1250 False, True) | 1209 False, True) |
1251 if change_info.GetFiles(): | 1210 if change_info.GetFiles(): |
1252 args = args[1:] | 1211 args = args[1:] |
1253 else: | 1212 else: |
1254 change_info = None | 1213 change_info = None |
1255 return TryChange(change_info, args, swallow_exception=False) | 1214 return TryChange(change_info, args, swallow_exception=False) |
1256 | 1215 |
1257 | 1216 |
| 1217 @attrs(usage='<old-name> <new-name>') |
1258 def CMDrename(args): | 1218 def CMDrename(args): |
1259 """Renames an existing change.""" | 1219 """Renames an existing change.""" |
1260 if len(args) != 2: | 1220 if len(args) != 2: |
1261 ErrorExit("Usage: gcl rename <old-name> <new-name>.") | 1221 ErrorExit("Usage: gcl rename <old-name> <new-name>.") |
1262 src, dst = args | 1222 src, dst = args |
1263 src_file = GetChangelistInfoFile(src) | 1223 src_file = GetChangelistInfoFile(src) |
1264 if not os.path.isfile(src_file): | 1224 if not os.path.isfile(src_file): |
1265 ErrorExit("Change '%s' does not exist." % src) | 1225 ErrorExit("Change '%s' does not exist." % src) |
1266 dst_file = GetChangelistInfoFile(dst) | 1226 dst_file = GetChangelistInfoFile(dst) |
1267 if os.path.isfile(dst_file): | 1227 if os.path.isfile(dst_file): |
1268 ErrorExit("Change '%s' already exists; pick a new name." % dst) | 1228 ErrorExit("Change '%s' already exists; pick a new name." % dst) |
1269 os.rename(src_file, dst_file) | 1229 os.rename(src_file, dst_file) |
1270 print "Change '%s' renamed '%s'." % (src, dst) | 1230 print "Change '%s' renamed '%s'." % (src, dst) |
1271 return 0 | 1231 return 0 |
1272 | 1232 |
1273 | 1233 |
1274 def CMDpassthru(args): | 1234 def CMDpassthru(args): |
1275 # Everything else that is passed into gcl we redirect to svn, after adding | 1235 """Everything else that is passed into gcl we redirect to svn. |
1276 # the files. | 1236 |
1277 # args is guaranteed to be len(args) >= 1 | 1237 It assumes a change list name is passed and is converted with the files names. |
| 1238 """ |
1278 args = ["svn", args[0]] | 1239 args = ["svn", args[0]] |
1279 if len(args) > 1: | 1240 if len(args) > 1: |
1280 root = GetRepositoryRoot() | 1241 root = GetRepositoryRoot() |
1281 change_info = ChangeInfo.Load(args[1], root, True, True) | 1242 change_info = ChangeInfo.Load(args[1], root, True, True) |
1282 args.extend([os.path.join(root, x) for x in change_info.GetFileNames()]) | 1243 args.extend([os.path.join(root, x) for x in change_info.GetFileNames()]) |
1283 return RunShellWithReturnCode(args, print_output=True)[1] | 1244 return RunShellWithReturnCode(args, print_output=True)[1] |
1284 | 1245 |
1285 | 1246 |
| 1247 def Command(name): |
| 1248 return getattr(sys.modules[__name__], 'CMD' + name, None) |
| 1249 |
| 1250 |
| 1251 def GenUsage(command): |
| 1252 """Modify an OptParse object with the function's documentation.""" |
| 1253 obj = Command(command) |
| 1254 display = command |
| 1255 more = getattr(obj, 'usage', '') |
| 1256 if command == 'help': |
| 1257 display = '<command>' |
| 1258 need_change = '' |
| 1259 if getattr(obj, 'need_change', None): |
| 1260 need_change = ' <change_list>' |
| 1261 options = ' [options]' |
| 1262 if getattr(obj, 'no_args', None): |
| 1263 options = '' |
| 1264 res = 'Usage: gcl %s%s%s %s\n\n' % (display, need_change, options, more) |
| 1265 res += re.sub('\n ', '\n', obj.__doc__) |
| 1266 return res |
| 1267 |
| 1268 |
| 1269 def CMDhelp(args): |
| 1270 """Prints this help or help for the given command.""" |
| 1271 if args and 'CMD' + args[0] in dir(sys.modules[__name__]): |
| 1272 print GenUsage(args[0]) |
| 1273 |
| 1274 # These commands defer to external tools so give this info too. |
| 1275 if args[0] == 'try': |
| 1276 TryChange(None, ['--help'], swallow_exception=False) |
| 1277 if args[0] == 'upload': |
| 1278 upload.RealMain(['upload.py', '--help']) |
| 1279 return 0 |
| 1280 |
| 1281 print GenUsage('help') |
| 1282 print sys.modules[__name__].__doc__ |
| 1283 print 'version ' + __version__ + '\n' |
| 1284 |
| 1285 print('Commands are:\n' + '\n'.join([ |
| 1286 ' %-12s %s' % (fn[3:], Command(fn[3:]).__doc__.split('\n')[0].strip()) |
| 1287 for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')])) |
| 1288 return 0 |
| 1289 |
| 1290 |
1286 def main(argv): | 1291 def main(argv): |
1287 __pychecker__ = 'maxreturns=0' | |
1288 try: | 1292 try: |
1289 # Create the directories where we store information about changelists if it | 1293 GetRepositoryRoot() |
1290 # doesn't exist. | |
1291 if not os.path.exists(GetInfoDir()): | |
1292 os.mkdir(GetInfoDir()) | |
1293 if not os.path.exists(GetChangesDir()): | |
1294 os.mkdir(GetChangesDir()) | |
1295 if not os.path.exists(GetCacheDir()): | |
1296 os.mkdir(GetCacheDir()) | |
1297 except gclient_utils.Error: | 1294 except gclient_utils.Error: |
1298 # Will throw an exception if not run in a svn checkout. | 1295 print('To use gcl, you need to be in a subversion checkout.') |
1299 pass | 1296 return 1 |
| 1297 |
| 1298 # Create the directories where we store information about changelists if it |
| 1299 # doesn't exist. |
| 1300 if not os.path.exists(GetInfoDir()): |
| 1301 os.mkdir(GetInfoDir()) |
| 1302 if not os.path.exists(GetChangesDir()): |
| 1303 os.mkdir(GetChangesDir()) |
| 1304 if not os.path.exists(GetCacheDir()): |
| 1305 os.mkdir(GetCacheDir()) |
1300 | 1306 |
1301 if not argv: | 1307 if not argv: |
1302 argv = ['help'] | 1308 argv = ['help'] |
1303 # Commands that don't require an argument. | 1309 command = Command(argv[0]) |
1304 command = argv[0] | 1310 if command: |
1305 if command == "opened": | 1311 return command(argv[1:]) |
1306 return CMDopened(argv[1:]) | 1312 # Unknown command, try to pass that to svn |
1307 if command == "status": | 1313 return CMDpassthru(argv) |
1308 return CMDstatus(argv[1:]) | |
1309 if command == "nothave": | |
1310 return CMDnothave(argv[1:]) | |
1311 if command == "changes": | |
1312 return CMDchanges(argv[1:]) | |
1313 if command == "help": | |
1314 return CMDhelp(argv[1:]) | |
1315 if command == "diff": | |
1316 return CMDdiff(argv[1:]) | |
1317 if command == "settings": | |
1318 return CMDsettings(argv[1:]) | |
1319 if command == "deleteempties": | |
1320 return CMDdeleteempties(argv[1:]) | |
1321 if command == "rename": | |
1322 return CMDrename(argv[1:]) | |
1323 elif command == "change": | |
1324 return CMDchange(argv[1:]) | |
1325 elif command == "description": | |
1326 return CMDdescription(argv[1:]) | |
1327 elif command == "lint": | |
1328 return CMDlint(argv[1:]) | |
1329 elif command == "upload": | |
1330 return CMDupload(argv[1:]) | |
1331 elif command == "presubmit": | |
1332 return CMDpresubmit(argv[1:]) | |
1333 elif command in ("commit", "submit"): | |
1334 return CMDcommit(argv[1:]) | |
1335 elif command == "delete": | |
1336 return CMDdelete(argv[1:]) | |
1337 elif command == "try": | |
1338 return CMDtry(argv[1:]) | |
1339 else: | |
1340 return CMDpassthru(argv) | |
1341 | 1314 |
1342 | 1315 |
1343 if __name__ == "__main__": | 1316 if __name__ == "__main__": |
1344 sys.exit(main(sys.argv[1:])) | 1317 sys.exit(main(sys.argv[1:])) |
OLD | NEW |