Changeset 63004 in webkit
- Timestamp:
- Jul 9, 2010 3:43:00 PM (14 years ago)
- Location:
- trunk/WebKitTools
- Files:
-
- 1 deleted
- 22 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebKitTools/ChangeLog
r62999 r63004 1 2010-07-09 Ojan Vafai <ojan@chromium.org> 2 3 Reviewed by Adam Barth. 4 5 --squash should go away and become the default 6 https://bugs.webkit.org/show_bug.cgi?id=39624 7 8 If there are local commits and working copy changes, then prompt the user 9 whether to continue. Setting git config webkit-patch.commit_should_always_squash 10 true bypasses the prompt. 11 12 --git-commit=HEAD.. operates on working copy changes only. 13 --git-commit=committish operates on a range of commits as a single commit. 14 e.g. --git-commit=HEAD only operates on the HEAD commit. 15 --git-commit=HEAD~4..HEAD~2 will operate on HEAD~3 and HEAD~2 as a single commit. 16 17 --no-squash and --squash are left in with descriptive error messages if used. 18 19 * Scripts/check-webkit-style: 20 * Scripts/webkitpy/common/checkout/api.py: 21 * Scripts/webkitpy/common/checkout/api_unittest.py: 22 * Scripts/webkitpy/common/checkout/scm.py: 23 * Scripts/webkitpy/common/checkout/scm_unittest.py: 24 * Scripts/webkitpy/common/net/rietveld.py: 25 * Scripts/webkitpy/common/system/user.py: 26 * Scripts/webkitpy/style/optparser.py: 27 * Scripts/webkitpy/style_references.py: 28 * Scripts/webkitpy/tool/commands/download.py: 29 * Scripts/webkitpy/tool/commands/upload.py: 30 * Scripts/webkitpy/tool/mocktool.py: 31 * Scripts/webkitpy/tool/steps/abstractstep.py: 32 * Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py: 33 * Scripts/webkitpy/tool/steps/checkstyle.py: 34 * Scripts/webkitpy/tool/steps/checkstyle_unittest.py: Removed. 35 * Scripts/webkitpy/tool/steps/commit.py: 36 * Scripts/webkitpy/tool/steps/options.py: 37 * Scripts/webkitpy/tool/steps/preparechangelog.py: 38 * Scripts/webkitpy/tool/steps/preparechangelogforrevert.py: 39 * Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py: 40 * Scripts/webkitpy/tool/steps/validatereviewer.py: 41 1 42 2010-07-09 Mark Rowe <mrowe@apple.com> 2 43 -
trunk/WebKitTools/Scripts/check-webkit-style
r60053 r63004 116 116 file_reader.process_paths(paths) 117 117 else: 118 patch = checkout.create_patch(options.git_commit , options.squash)118 patch = checkout.create_patch(options.git_commit) 119 119 patch_checker = PatchReader(file_reader) 120 120 patch_checker.check(patch) -
trunk/WebKitTools/Scripts/webkitpy/common/checkout/api.py
r58434 r63004 84 84 return self.commit_info_for_revision(revision).bug_id() 85 85 86 def modified_changelogs(self, git_commit , squash):86 def modified_changelogs(self, git_commit): 87 87 # SCM returns paths relative to scm.checkout_root 88 88 # Callers (especially those using the ChangeLog class) may 89 89 # expect absolute paths, so this method returns absolute paths. 90 changed_files = self._scm.changed_files(git_commit , squash)90 changed_files = self._scm.changed_files(git_commit) 91 91 absolute_paths = [os.path.join(self._scm.checkout_root, path) for path in changed_files] 92 92 return [path for path in absolute_paths if self._is_path_to_changelog(path)] 93 93 94 def commit_message_for_this_commit(self, git_commit , squash):95 changelog_paths = self.modified_changelogs(git_commit , squash)94 def commit_message_for_this_commit(self, git_commit): 95 changelog_paths = self.modified_changelogs(git_commit) 96 96 if not len(changelog_paths): 97 97 raise ScriptError(message="Found no modified ChangeLogs, cannot create a commit message.\n" … … 110 110 return CommitMessage("".join(changelog_messages).splitlines()) 111 111 112 def bug_id_for_this_commit(self, git_commit , squash):112 def bug_id_for_this_commit(self, git_commit): 113 113 try: 114 return parse_bug_id(self.commit_message_for_this_commit(git_commit , squash).message())114 return parse_bug_id(self.commit_message_for_this_commit(git_commit).message()) 115 115 except ScriptError, e: 116 116 pass # We might not have ChangeLogs. … … 132 132 # We revert the ChangeLogs because removing lines from a ChangeLog 133 133 # doesn't make sense. ChangeLogs are append only. 134 changelog_paths = self.modified_changelogs(git_commit=None , squash=False)134 changelog_paths = self.modified_changelogs(git_commit=None) 135 135 if len(changelog_paths): 136 136 self._scm.revert_files(changelog_paths) -
trunk/WebKitTools/Scripts/webkitpy/common/checkout/api_unittest.py
r58261 r63004 115 115 def test_commit_message_for_this_commit(self): 116 116 checkout = Checkout(None) 117 checkout.modified_changelogs = lambda git_commit , squash: ["ChangeLog1", "ChangeLog2"]117 checkout.modified_changelogs = lambda git_commit: ["ChangeLog1", "ChangeLog2"] 118 118 output = OutputCapture() 119 119 expected_stderr = "Parsing ChangeLog: ChangeLog1\nParsing ChangeLog: ChangeLog2\n" 120 120 commit_message = output.assert_outputs(self, checkout.commit_message_for_this_commit, 121 kwargs={"git_commit": None , "squash": False}, expected_stderr=expected_stderr)121 kwargs={"git_commit": None}, expected_stderr=expected_stderr) 122 122 self.assertEqual(commit_message.message(), self.expected_commit_message) 123 123 … … 164 164 scm = Mock() 165 165 checkout = Checkout(scm) 166 checkout.commit_message_for_this_commit = lambda git_commit , squash: CommitMessage(ChangeLogEntry(_changelog1entry1).contents().splitlines())167 self.assertEqual(checkout.bug_id_for_this_commit(git_commit=None , squash=False), 36629)166 checkout.commit_message_for_this_commit = lambda git_commit: CommitMessage(ChangeLogEntry(_changelog1entry1).contents().splitlines()) 167 self.assertEqual(checkout.bug_id_for_this_commit(git_commit=None), 36629) 168 168 169 169 def test_modified_changelogs(self): 170 170 scm = Mock() 171 171 scm.checkout_root = "/foo/bar" 172 scm.changed_files = lambda git_commit , squash: ["file1", "ChangeLog", "relative/path/ChangeLog"]172 scm.changed_files = lambda git_commit: ["file1", "ChangeLog", "relative/path/ChangeLog"] 173 173 checkout = Checkout(scm) 174 174 expected_changlogs = ["/foo/bar/ChangeLog", "/foo/bar/relative/path/ChangeLog"] 175 self.assertEqual(checkout.modified_changelogs(git_commit=None , squash=False), expected_changlogs)175 self.assertEqual(checkout.modified_changelogs(git_commit=None), expected_changlogs) -
trunk/WebKitTools/Scripts/webkitpy/common/checkout/scm.py
r62480 r63004 36 36 37 37 from webkitpy.common.system.executive import Executive, run_command, ScriptError 38 from webkitpy.common.system.user import User39 38 from webkitpy.common.system.deprecated_logging import error, log 40 39 … … 93 92 raise CheckoutNeedsUpdate(script_args=error.script_args, exit_code=error.exit_code, output=error.output, cwd=error.cwd) 94 93 Executive.default_error_handler(error) 94 95 96 class AuthenticationError(Exception): 97 def __init__(self, server_host): 98 self.server_host = server_host 99 100 101 class AmbiguousCommitError(Exception): 102 def __init__(self, num_local_commits, working_directory_is_clean): 103 self.num_local_commits = num_local_commits 104 self.working_directory_is_clean = working_directory_is_clean 95 105 96 106 … … 199 209 self._subclass_must_implement() 200 210 201 def changed_files(self, git_commit=None , squash=None):211 def changed_files(self, git_commit=None): 202 212 self._subclass_must_implement() 203 213 … … 214 224 self._subclass_must_implement() 215 225 216 def create_patch(self, git_commit=None , squash=None):226 def create_patch(self, git_commit=None): 217 227 self._subclass_must_implement() 218 228 … … 238 248 self._subclass_must_implement() 239 249 240 def should_squash(self, squash): 241 self._subclass_must_implement() 242 243 def commit_with_message(self, message, username=None, git_commit=None, squash=None): 250 def commit_with_message(self, message, username=None, git_commit=None, force_squash=False): 244 251 self._subclass_must_implement() 245 252 … … 378 385 return self.run(["svn", "delete", "--force", base], cwd=parent) 379 386 380 def changed_files(self, git_commit=None , squash=None):387 def changed_files(self, git_commit=None): 381 388 return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("ACDMR")) 382 389 … … 404 411 405 412 # FIXME: This method should be on Checkout. 406 def create_patch(self, git_commit=None , squash=None):413 def create_patch(self, git_commit=None): 407 414 """Returns a byte array (str()) representing the patch file. 408 415 Patch files are effectively binary since they may contain … … 478 485 self.run(['svn', 'revert'] + file_paths) 479 486 480 def should_squash(self, squash): 481 # SVN doesn't support the concept of squashing. 482 return False 483 484 def commit_with_message(self, message, username=None, git_commit=None, squash=None): 485 # squash and git-commit are not used by SVN. 487 def commit_with_message(self, message, username=None, git_commit=None, force_squash=False): 488 # git-commit and force are not used by SVN. 486 489 if self.dryrun: 487 490 # Return a string which looks like a commit so that things which parse this output will succeed. 488 491 return "Dry run, no commit.\nCommitted revision 0." 492 489 493 svn_commit_args = ["svn", "commit"] 494 490 495 if not username and not self.has_authorization_for_realm(): 491 username = User.prompt("%s login: " % self.svn_server_host, repeat=5) 492 if not username: 493 raise Exception("You need to specify the username on %s to perform the commit as." % self.svn_server_host) 496 raise AuthenticationError(self.svn_server_host) 494 497 if username: 495 498 svn_commit_args.extend(["--username", username]) 499 496 500 svn_commit_args.extend(["-m", message]) 497 501 # FIXME: Should this use cwd=self.checkout_root? … … 585 589 return self.run(["git", "rm", "-f", path]) 586 590 587 def _merge_base(self, git_commit, squash): 591 def _assert_synced(self): 592 if len(run_command(['git', 'rev-list', '--max-count=1', self.remote_branch_ref(), '^HEAD'])): 593 raise ScriptError(message="Not fully merged/rebased to %s. This branch needs to be synced first." % self.remote_branch_ref()) 594 595 def merge_base(self, git_commit): 588 596 if git_commit: 589 # FIXME: Calling code should turn commit ranges into a list of commit IDs 590 # and then treat each commit separately. 597 # Special-case HEAD.. to mean working-copy changes only. 598 if git_commit.upper() == 'HEAD..': 599 return 'HEAD' 600 591 601 if '..' not in git_commit: 592 602 git_commit = git_commit + "^.." + git_commit 593 603 return git_commit 594 604 595 if self.should_squash(squash): 596 return self.remote_merge_base() 597 598 # FIXME: Non-squash behavior should match commit_with_message. It raises an error 599 # if there are working copy changes and --squash or --no-squash wasn't passed in. 600 # If --no-squash, then it should proceed with each local commit as a separate patch. 601 return 'HEAD' 602 603 def changed_files(self, git_commit=None, squash=None): 604 status_command = ['git', 'diff', '-r', '--name-status', '-C', '-M', "--no-ext-diff", "--full-index", self._merge_base(git_commit, squash)] 605 self._assert_synced() 606 return self.remote_merge_base() 607 608 def changed_files(self, git_commit=None): 609 status_command = ['git', 'diff', '-r', '--name-status', '-C', '-M', "--no-ext-diff", "--full-index", self.merge_base(git_commit)] 605 610 return self.run_status_and_extract_filenames(status_command, self._status_regexp("ADM")) 606 611 … … 634 639 return "git" 635 640 636 def create_patch(self, git_commit=None , squash=None):641 def create_patch(self, git_commit=None): 637 642 """Returns a byte array (str()) representing the patch file. 638 643 Patch files are effectively binary since they may contain 639 644 files of multiple different encodings.""" 640 645 # FIXME: This should probably use cwd=self.checkout_root 641 return self.run(['git', 'diff', '--binary', "--no-ext-diff", "--full-index", "-M", self. _merge_base(git_commit, squash)], decode_output=False)646 return self.run(['git', 'diff', '--binary', "--no-ext-diff", "--full-index", "-M", self.merge_base(git_commit)], decode_output=False) 642 647 643 648 @classmethod … … 680 685 self.run(['git', 'checkout', 'HEAD'] + file_paths) 681 686 682 def _get_squash_error_message(self, num_local_commits): 683 working_directory_message = "" if self.working_directory_is_clean() else " and working copy changes" 684 return ("""There are %s local commits%s. Do one of the following: 685 1) Use --squash or --no-squash 686 2) git config webkit-patch.squash true/false 687 """ % (num_local_commits, working_directory_message)) 688 689 def should_squash(self, squash): 690 if squash is None: 691 config_squash = Git.read_git_config('webkit-patch.squash') 692 if (config_squash and config_squash is not ""): 693 squash = config_squash.lower() == "true" 694 else: 695 # Only raise an error if there are actually multiple commits to squash. 696 num_local_commits = len(self.local_commits()) 697 if num_local_commits > 1 or (num_local_commits > 0 and not self.working_directory_is_clean()): 698 raise ScriptError(message=self._get_squash_error_message(num_local_commits)) 699 700 if squash and self._remote_branch_has_extra_commits(): 701 raise ScriptError(message="Cannot use --squash when HEAD is not fully merged/rebased to %s. " 702 "This branch needs to be synced first." % self.remote_branch_ref()) 703 704 return squash 705 706 def _remote_branch_has_extra_commits(self): 707 return len(run_command(['git', 'rev-list', '--max-count=1', self.remote_branch_ref(), '^HEAD'])) 708 709 def commit_with_message(self, message, username=None, git_commit=None, squash=None): 687 def _assert_can_squash(self, working_directory_is_clean): 688 squash = Git.read_git_config('webkit-patch.commit_should_always_squash') 689 should_squash = squash and squash.lower() == "true" 690 691 if not should_squash: 692 # Only warn if there are actually multiple commits to squash. 693 num_local_commits = len(self.local_commits()) 694 if num_local_commits > 1 or (num_local_commits > 0 and not working_directory_is_clean): 695 raise AmbiguousCommitError(num_local_commits, working_directory_is_clean) 696 697 def commit_with_message(self, message, username=None, git_commit=None, force_squash=False): 710 698 # Username is ignored during Git commits. 699 working_directory_is_clean = self.working_directory_is_clean() 700 711 701 if git_commit: 702 # Special-case HEAD.. to mean working-copy changes only. 703 if git_commit.upper() == 'HEAD..': 704 if working_directory_is_clean: 705 raise ScriptError(message="The working copy is not modified. --git-commit=HEAD.. only commits working copy changes.") 706 self.commit_locally_with_message(message) 707 return self._commit_on_branch(message, 'HEAD') 708 712 709 # Need working directory changes to be committed so we can checkout the merge branch. 713 if not self.working_directory_is_clean():710 if not working_directory_is_clean: 714 711 # FIXME: webkit-patch land will modify the ChangeLogs to correct the reviewer. 715 712 # That will modify the working-copy and cause us to hit this error. 716 # The ChangeLog modification could be made to modify the existing local commit ?713 # The ChangeLog modification could be made to modify the existing local commit. 717 714 raise ScriptError(message="Working copy is modified. Cannot commit individual git_commits.") 718 715 return self._commit_on_branch(message, git_commit) 719 716 720 squash = self.should_squash(squash) 721 if squash: 722 self.run(['git', 'reset', '--soft', self.remote_branch_ref()]) 723 self.commit_locally_with_message(message) 724 elif not self.working_directory_is_clean(): 725 if not len(self.local_commits()): 726 # There are only working copy changes. Assume they should be committed. 727 self.commit_locally_with_message(message) 728 elif squash is None: 729 # The user didn't explicitly say to squash or not squash. There are local commits 730 # and working copy changes. Not clear what the user wants. 731 raise ScriptError(message="""There are local commits and working copy changes. Do one of the following: 732 1) Commit/revert working copy changes. 733 2) Use --squash or --no-squash 734 3) git config webkit-patch.squash true/false 735 """) 736 737 # FIXME: This will commit all local commits, each with it's own message. We should restructure 738 # so that each local commit has the appropriate commit message based off it's ChangeLogs. 717 if not force_squash: 718 self._assert_can_squash(working_directory_is_clean) 719 self._assert_synced() 720 self.run(['git', 'reset', '--soft', self.remote_branch_ref()]) 721 self.commit_locally_with_message(message) 739 722 return self.push_local_commits_to_server() 740 723 -
trunk/WebKitTools/Scripts/webkitpy/common/checkout/scm_unittest.py
r62480 r63004 45 45 from datetime import date 46 46 from webkitpy.common.checkout.api import Checkout 47 from webkitpy.common.checkout.scm import detect_scm_system, SCM, SVN, CheckoutNeedsUpdate, commit_error_handler 47 from webkitpy.common.checkout.scm import detect_scm_system, SCM, SVN, CheckoutNeedsUpdate, commit_error_handler, AuthenticationError, AmbiguousCommitError 48 48 from webkitpy.common.config.committers import Committer # FIXME: This should not be needed 49 49 from webkitpy.common.net.bugzilla import Attachment # FIXME: This should not be needed … … 598 598 self._shared_test_commit_with_message("dbates@webkit.org") 599 599 600 def test_commit_without_authorization(self): 601 self.scm.has_authorization_for_realm = lambda: False 602 self.assertRaises(AuthenticationError, self._shared_test_commit_with_message) 603 600 604 def test_has_authorization_for_realm(self): 601 605 scm = detect_scm_system(self.svn_checkout_path) … … 868 872 def test_commit_text_parsing(self): 869 873 write_into_file_at_path('test_file', 'more test content') 870 self.scm.commit_locally_with_message("another test commit")871 874 commit_text = self.scm.commit_with_message("another test commit") 872 875 self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6') … … 874 877 self.scm.dryrun = True 875 878 write_into_file_at_path('test_file', 'still more test content') 876 self.scm.commit_locally_with_message("yet another test commit")877 879 commit_text = self.scm.commit_with_message("yet another test commit") 878 880 self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '0') 879 881 880 def _one_local_commit_plus_working_copy_changes(self): 882 def test_commit_with_message_working_copy_only(self): 883 write_into_file_at_path('test_file_commit1', 'more test content') 884 run_command(['git', 'add', 'test_file_commit1']) 885 scm = detect_scm_system(self.git_checkout_path) 886 commit_text = scm.commit_with_message("yet another test commit") 887 888 self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6') 889 svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose']) 890 self.assertTrue(re.search(r'test_file_commit1', svn_log)) 891 892 def _one_local_commit(self): 881 893 write_into_file_at_path('test_file_commit1', 'more test content') 882 894 run_command(['git', 'add', 'test_file_commit1']) 883 895 self.scm.commit_locally_with_message("another test commit") 884 896 897 def _one_local_commit_plus_working_copy_changes(self): 898 self._one_local_commit() 885 899 write_into_file_at_path('test_file_commit2', 'still more test content') 886 900 run_command(['git', 'add', 'test_file_commit2']) 887 901 888 def test_commit_with_message_working_copy_only(self): 889 write_into_file_at_path('test_file_commit1', 'more test content') 890 run_command(['git', 'add', 'test_file_commit1']) 891 scm = detect_scm_system(self.git_checkout_path) 892 commit_text = scm.commit_with_message("yet another test commit") 893 894 self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6') 895 svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose']) 896 self.assertTrue(re.search(r'test_file_commit1', svn_log)) 897 898 def test_commit_with_message_squashed(self): 902 def _two_local_commits(self): 903 self._one_local_commit() 904 write_into_file_at_path('test_file_commit2', 'still more test content') 905 run_command(['git', 'add', 'test_file_commit2']) 906 self.scm.commit_locally_with_message("yet another test commit") 907 908 def _three_local_commits(self): 909 write_into_file_at_path('test_file_commit0', 'more test content') 910 run_command(['git', 'add', 'test_file_commit0']) 911 self.scm.commit_locally_with_message("another test commit") 912 self._two_local_commits() 913 914 def test_commit_with_message(self): 899 915 self._one_local_commit_plus_working_copy_changes() 900 916 scm = detect_scm_system(self.git_checkout_path) 901 commit_text = scm.commit_with_message("yet another test commit", squash=True) 917 self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "yet another test commit") 918 commit_text = scm.commit_with_message("yet another test commit", force_squash=True) 902 919 903 920 self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6') … … 906 923 self.assertTrue(re.search(r'test_file_commit1', svn_log)) 907 924 908 def _two_local_commits(self):909 write_into_file_at_path('test_file_commit1', 'more test content')910 run_command(['git', 'add', 'test_file_commit1'])911 self.scm.commit_locally_with_message("another test commit")912 913 write_into_file_at_path('test_file_commit2', 'still more test content')914 run_command(['git', 'add', 'test_file_commit2'])915 self.scm.commit_locally_with_message("yet another test commit")916 917 def _three_local_commits(self):918 write_into_file_at_path('test_file_commit0', 'more test content')919 run_command(['git', 'add', 'test_file_commit0'])920 self.scm.commit_locally_with_message("another test commit")921 self._two_local_commits()922 923 925 def test_commit_with_message_git_commit(self): 924 926 self._two_local_commits() … … 944 946 self.assertTrue(re.search(r'test_file_commit2', svn_log)) 945 947 946 def test_commit_with_message_multiple_local_commits(self): 947 self._two_local_commits() 948 scm = detect_scm_system(self.git_checkout_path) 949 self.assertRaises(ScriptError, scm.commit_with_message, ["another test commit"]) 948 def test_changed_files_working_copy_only(self): 949 self._one_local_commit_plus_working_copy_changes() 950 scm = detect_scm_system(self.git_checkout_path) 951 commit_text = scm.commit_with_message("another test commit", git_commit="HEAD..") 952 self.assertFalse(re.search(r'test_file_commit1', svn_log)) 953 self.assertTrue(re.search(r'test_file_commit2', svn_log)) 954 955 def test_commit_with_message_only_local_commit(self): 956 self._one_local_commit() 957 scm = detect_scm_system(self.git_checkout_path) 958 commit_text = scm.commit_with_message("another test commit") 959 svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose']) 960 self.assertTrue(re.search(r'test_file_commit1', svn_log)) 950 961 951 962 def test_commit_with_message_multiple_local_commits_and_working_copy(self): … … 953 964 write_into_file_at_path('test_file_commit1', 'working copy change') 954 965 scm = detect_scm_system(self.git_checkout_path) 955 self.assertRaises(ScriptError, scm.commit_with_message, ["another test commit"]) 956 957 def test_commit_with_message_git_commit_and_working_copy(self): 958 self._two_local_commits() 959 write_into_file_at_path('test_file_commit1', 'working copy change') 960 scm = detect_scm_system(self.git_checkout_path) 961 self.assertRaises(ScriptError, scm.commit_with_message, ["another test commit", 'git_commit="HEAD^"']) 962 963 def test_commit_with_message_multiple_local_commits_no_squash(self): 964 self._two_local_commits() 965 scm = detect_scm_system(self.git_checkout_path) 966 commit_text = scm.commit_with_message("yet another test commit", squash=False) 966 967 self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "another test commit") 968 commit_text = scm.commit_with_message("another test commit", force_squash=True) 969 967 970 self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6') 968 969 svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])970 self.assertTrue(re.search(r'test_file_commit2', svn_log))971 self.assertFalse(re.search(r'test_file_commit1', svn_log))972 973 svn_log = run_command(['git', 'svn', 'log', '--limit=2', '--verbose'])974 self.assertTrue(re.search(r'test_file_commit1', svn_log))975 976 def test_commit_with_message_multiple_local_commits_squash(self):977 self._two_local_commits()978 scm = detect_scm_system(self.git_checkout_path)979 commit_text = scm.commit_with_message("yet another test commit", squash=True)980 self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')981 982 971 svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose']) 983 972 self.assertTrue(re.search(r'test_file_commit2', svn_log)) 984 973 self.assertTrue(re.search(r'test_file_commit1', svn_log)) 985 974 986 def test_commit_with_message_not_synced_squash(self): 975 def test_commit_with_message_git_commit_and_working_copy(self): 976 self._two_local_commits() 977 write_into_file_at_path('test_file_commit1', 'working copy change') 978 scm = detect_scm_system(self.git_checkout_path) 979 self.assertRaises(ScriptError, scm.commit_with_message, "another test commit", git_commit="HEAD^") 980 981 def test_commit_with_message_multiple_local_commits_always_squash(self): 982 self._two_local_commits() 983 scm = detect_scm_system(self.git_checkout_path) 984 scm._assert_can_squash = lambda working_directory_is_clean: True 985 commit_text = scm.commit_with_message("yet another test commit") 986 self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6') 987 988 svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose']) 989 self.assertTrue(re.search(r'test_file_commit2', svn_log)) 990 self.assertTrue(re.search(r'test_file_commit1', svn_log)) 991 992 def test_commit_with_message_multiple_local_commits(self): 993 self._two_local_commits() 994 scm = detect_scm_system(self.git_checkout_path) 995 self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "yet another test commit") 996 commit_text = scm.commit_with_message("yet another test commit", force_squash=True) 997 998 self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6') 999 1000 svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose']) 1001 self.assertTrue(re.search(r'test_file_commit2', svn_log)) 1002 self.assertTrue(re.search(r'test_file_commit1', svn_log)) 1003 1004 def test_commit_with_message_not_synced(self): 987 1005 run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3']) 988 1006 self._two_local_commits() 989 1007 scm = detect_scm_system(self.git_checkout_path) 990 self.assertRaises(ScriptError, scm.commit_with_message, "another test commit", squash=True) 1008 self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "another test commit") 1009 self.assertRaises(ScriptError, scm.commit_with_message, "another test commit", force_squash=True) 991 1010 992 1011 def test_remote_branch_ref(self): … … 1005 1024 self._one_local_commit_plus_working_copy_changes() 1006 1025 scm = detect_scm_system(self.git_checkout_path) 1007 self.assertRaises(ScriptError, scm.create_patch) 1008 1009 def test_create_patch_multiple_local_commits(self): 1010 self._two_local_commits() 1011 scm = detect_scm_system(self.git_checkout_path) 1012 self.assertRaises(ScriptError, scm.create_patch) 1013 1014 def test_create_patch_squashed(self): 1026 patch = scm.create_patch() 1027 self.assertTrue(re.search(r'test_file_commit1', patch)) 1028 self.assertTrue(re.search(r'test_file_commit2', patch)) 1029 1030 def test_create_patch(self): 1015 1031 self._one_local_commit_plus_working_copy_changes() 1016 1032 scm = detect_scm_system(self.git_checkout_path) 1017 patch = scm.create_patch( squash=True)1033 patch = scm.create_patch() 1018 1034 self.assertTrue(re.search(r'test_file_commit2', patch)) 1019 1035 self.assertTrue(re.search(r'test_file_commit1', patch)) 1020 1021 def test_create_patch_not_squashed(self):1022 self._one_local_commit_plus_working_copy_changes()1023 scm = detect_scm_system(self.git_checkout_path)1024 patch = scm.create_patch(squash=False)1025 self.assertTrue(re.search(r'test_file_commit2', patch))1026 self.assertFalse(re.search(r'test_file_commit1', patch))1027 1036 1028 1037 def test_create_patch_git_commit(self): … … 1041 1050 self.assertTrue(re.search(r'test_file_commit1', patch)) 1042 1051 1043 def test_create_patch_multiple_local_commits_no_squash(self): 1044 self._two_local_commits() 1045 scm = detect_scm_system(self.git_checkout_path) 1046 patch = scm.create_patch(squash=False) 1047 # FIXME: It's weird that with squash=False, create_patch/changed_files ignores local commits, 1048 # but commit_with_message commits them. 1049 self.assertTrue(patch == "") 1050 1051 def test_create_patch_multiple_local_commits_squash(self): 1052 self._two_local_commits() 1053 scm = detect_scm_system(self.git_checkout_path) 1054 patch = scm.create_patch(squash=True) 1052 def test_create_patch_working_copy_only(self): 1053 self._one_local_commit_plus_working_copy_changes() 1054 scm = detect_scm_system(self.git_checkout_path) 1055 patch = scm.create_patch(git_commit="HEAD..") 1056 self.assertFalse(re.search(r'test_file_commit1', patch)) 1057 self.assertTrue(re.search(r'test_file_commit2', patch)) 1058 1059 def test_create_patch_multiple_local_commits(self): 1060 self._two_local_commits() 1061 scm = detect_scm_system(self.git_checkout_path) 1062 patch = scm.create_patch() 1055 1063 self.assertTrue(re.search(r'test_file_commit2', patch)) 1056 1064 self.assertTrue(re.search(r'test_file_commit1', patch)) 1057 1065 1058 def test_create_patch_not_synced _squash(self):1066 def test_create_patch_not_synced(self): 1059 1067 run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3']) 1060 1068 self._two_local_commits() 1061 1069 scm = detect_scm_system(self.git_checkout_path) 1062 self.assertRaises(ScriptError, scm.create_patch , squash=True)1070 self.assertRaises(ScriptError, scm.create_patch) 1063 1071 1064 1072 def test_create_binary_patch(self): … … 1091 1099 self._one_local_commit_plus_working_copy_changes() 1092 1100 scm = detect_scm_system(self.git_checkout_path) 1093 self.assertRaises(ScriptError, scm.changed_files) 1094 1095 def test_changed_files_multiple_local_commits(self): 1096 self._two_local_commits() 1097 scm = detect_scm_system(self.git_checkout_path) 1098 self.assertRaises(ScriptError, scm.changed_files) 1099 1100 def test_changed_files_squashed(self): 1101 self._one_local_commit_plus_working_copy_changes() 1102 scm = detect_scm_system(self.git_checkout_path) 1103 files = scm.changed_files(squash=True) 1101 files = scm.changed_files() 1102 self.assertTrue('test_file_commit1' in files) 1104 1103 self.assertTrue('test_file_commit2' in files) 1105 self.assertTrue('test_file_commit1' in files)1106 1107 def test_changed_files_not_squashed(self):1108 self._one_local_commit_plus_working_copy_changes()1109 scm = detect_scm_system(self.git_checkout_path)1110 files = scm.changed_files(squash=False)1111 self.assertTrue('test_file_commit2' in files)1112 self.assertFalse('test_file_commit1' in files)1113 1104 1114 1105 def test_changed_files_git_commit(self): … … 1127 1118 self.assertTrue('test_file_commit2' in files) 1128 1119 1129 def test_changed_files_multiple_local_commits_no_squash(self): 1130 self._two_local_commits() 1131 scm = detect_scm_system(self.git_checkout_path) 1132 files = scm.changed_files(squash=False) 1133 # FIXME: It's weird that with squash=False, create_patch/changed_files ignores local commits, 1134 # but commit_with_message commits them. 1135 self.assertTrue(len(files) == 0) 1136 1137 def test_changed_files_multiple_local_commits_squash(self): 1138 self._two_local_commits() 1139 scm = detect_scm_system(self.git_checkout_path) 1140 files = scm.changed_files(squash=True) 1120 def test_changed_files_working_copy_only(self): 1121 self._one_local_commit_plus_working_copy_changes() 1122 scm = detect_scm_system(self.git_checkout_path) 1123 files = scm.changed_files(git_commit="HEAD..") 1124 self.assertFalse('test_file_commit1' in files) 1125 self.assertTrue('test_file_commit2' in files) 1126 1127 def test_changed_files_multiple_local_commits(self): 1128 self._two_local_commits() 1129 scm = detect_scm_system(self.git_checkout_path) 1130 files = scm.changed_files() 1141 1131 self.assertTrue('test_file_commit2' in files) 1142 1132 self.assertTrue('test_file_commit1' in files) 1143 1133 1144 def test_changed_files_not_synced _squash(self):1134 def test_changed_files_not_synced(self): 1145 1135 run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3']) 1146 1136 self._two_local_commits() 1147 1137 scm = detect_scm_system(self.git_checkout_path) 1148 self.assertRaises(ScriptError, scm.changed_files , squash=True)1138 self.assertRaises(ScriptError, scm.changed_files) 1149 1139 1150 1140 def test_changed_files(self): -
trunk/WebKitTools/Scripts/webkitpy/common/net/rietveld.py
r61024 r63004 74 74 # Use RealMain instead of calling upload from the commandline so that 75 75 # we can pass in the diff ourselves. Otherwise, upload will just use 76 # git diff for git checkouts, which doesn't respect -- squash and --git-commit.76 # git diff for git checkouts, which doesn't respect --git-commit. 77 77 issue, patchset = upload.RealMain(args, data=diff) 78 78 return issue -
trunk/WebKitTools/Scripts/webkitpy/common/system/user.py
r60666 r63004 52 52 53 53 class User(object): 54 # FIXME: These are @classmethods because scm.py and bugzilla.py don't have a Tool object (thus no User instance).54 # FIXME: These are @classmethods because bugzilla.py doesn't have a Tool object (thus no User instance). 55 55 @classmethod 56 56 def prompt(cls, message, repeat=1, raw_input=raw_input): -
trunk/WebKitTools/Scripts/webkitpy/style/optparser.py
r58261 r63004 148 148 is_verbose=False, 149 149 min_confidence=1, 150 output_format="emacs", 151 squash=False): 150 output_format="emacs"): 152 151 if filter_rules is None: 153 152 filter_rules = [] … … 168 167 self.min_confidence = min_confidence 169 168 self.output_format = output_format 170 self.squash = squash171 169 172 170 # Useful for unit testing. … … 183 181 if self.output_format != other.output_format: 184 182 return False 185 if self.squash != other.squash:186 return False187 183 188 184 return True … … 219 215 if options.git_commit: 220 216 flags['git-commit'] = options.git_commit 221 if options.squash:222 flags['squash'] = options.squash223 217 224 218 flag_string = '' … … 310 304 dest="filter_value", help=filter_help) 311 305 312 git_commit_help = ("check all changes in the given gitcommit. "306 git_commit_help = ("check all changes in the given commit. " 313 307 "Use 'commit_id..' to check all changes after commmit_id") 314 308 parser.add_option("-g", "--git-diff", "--git-commit", … … 330 324 dest="output_format", default=default_output_format, 331 325 help=output_format_help) 332 333 squash_help = ("All diffs from the remote branch are checked."334 "If excluded, prompts whether to squash when there are multiple commits.")335 parser.add_option("-s", "--squash", action="store_true", dest="squash", help=squash_help)336 337 squash_help = ("Only working copy diffs are checked."338 "If excluded, prompts whether to squash when there are multiple commits.")339 parser.add_option("--no-squash", action="store_false", dest="squash", help=squash_help)340 326 341 327 verbose_help = "enable verbose logging." … … 459 445 is_verbose=is_verbose, 460 446 min_confidence=min_confidence, 461 output_format=output_format, 462 squash=options.squash) 447 output_format=output_format) 463 448 464 449 return (paths, options) -
trunk/WebKitTools/Scripts/webkitpy/style_references.py
r60053 r63004 68 68 return self._scm.checkout_root 69 69 70 def create_patch(self, git_commit , squash):71 return self._scm.create_patch(git_commit , squash)70 def create_patch(self, git_commit): 71 return self._scm.create_patch(git_commit) 72 72 -
trunk/WebKitTools/Scripts/webkitpy/tool/commands/download.py
r62170 r63004 94 94 def _prepare_state(self, options, args, tool): 95 95 return { 96 "bug_id": (args and args[0]) or tool.checkout().bug_id_for_this_commit(options.git_commit , options.squash),96 "bug_id": (args and args[0]) or tool.checkout().bug_id_for_this_commit(options.git_commit), 97 97 } 98 98 -
trunk/WebKitTools/Scripts/webkitpy/tool/commands/upload.py
r62245 r63004 54 54 def __init__(self): 55 55 options = [ 56 steps.Options.squash,57 56 steps.Options.git_commit, 58 57 ] … … 62 61 # This command is a useful test to make sure commit_message_for_this_commit 63 62 # always returns the right value regardless of the current working directory. 64 print "%s" % tool.checkout().commit_message_for_this_commit(options.git_commit , options.squash).message()63 print "%s" % tool.checkout().commit_message_for_this_commit(options.git_commit).message() 65 64 66 65 … … 154 153 bug_id = args and args[0] 155 154 if not bug_id: 156 bug_id = tool.checkout().bug_id_for_this_commit(options.git_commit , options.squash)155 bug_id = tool.checkout().bug_id_for_this_commit(options.git_commit) 157 156 return bug_id 158 157 … … 424 423 (bug_title, comment_text) = self.prompt_for_bug_title_and_comment() 425 424 else: 426 commit_message = tool.checkout().commit_message_for_this_commit(options.git_commit , options.squash)425 commit_message = tool.checkout().commit_message_for_this_commit(options.git_commit) 427 426 bug_title = commit_message.description(lstrip=True, strip_url=True) 428 427 comment_text = commit_message.body(lstrip=True) 429 428 430 diff = tool.scm().create_patch(options.git_commit , options.squash)429 diff = tool.scm().create_patch(options.git_commit) 431 430 bug_id = tool.bugs.create_bug(bug_title, comment_text, options.component, diff, "Patch", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) 432 431 -
trunk/WebKitTools/Scripts/webkitpy/tool/mocktool.py
r60976 r63004 398 398 self.checkout_root = self.fake_checkout_root 399 399 400 def create_patch(self, git_commit , squash):400 def create_patch(self, git_commit): 401 401 return "Patch1" 402 402 … … 438 438 return 12345 439 439 440 def modified_changelogs(self, git_commit , squash):440 def modified_changelogs(self, git_commit): 441 441 # Ideally we'd return something more interesting here. The problem is 442 442 # that LandDiff will try to actually read the patch from disk! 443 443 return [] 444 444 445 def commit_message_for_this_commit(self, git_commit , squash):445 def commit_message_for_this_commit(self, git_commit): 446 446 commit_message = Mock() 447 447 commit_message.message = lambda:"This is a fake commit message that is at least 50 characters." -
trunk/WebKitTools/Scripts/webkitpy/tool/steps/abstractstep.py
r61098 r63004 28 28 29 29 from webkitpy.common.system.deprecated_logging import log 30 from webkitpy.common.system.executive import ScriptError 30 31 from webkitpy.common.config.ports import WebKitPort 31 32 from webkitpy.tool.steps.options import Options … … 35 36 def __init__(self, tool, options): 36 37 self._tool = tool 38 if options.no_squash: 39 raise ScriptError('--no-squash has been removed. Use "--git-commit=HEAD.." or "-g HEAD.." to operate on the working copy.') 40 if options.squash: 41 raise ScriptError('--squash has been removed. It is now the default behavior if --git-commit is omitted.') 37 42 self._options = options 38 43 self._port = None … … 54 59 55 60 _well_known_keys = { 56 "diff": lambda self, state: self._tool.scm().create_patch(self._options.git_commit , self._options.squash),57 "changelogs": lambda self, state: self._tool.checkout().modified_changelogs(self._options.git_commit , self._options.squash),61 "diff": lambda self, state: self._tool.scm().create_patch(self._options.git_commit), 62 "changelogs": lambda self, state: self._tool.checkout().modified_changelogs(self._options.git_commit), 58 63 "bug_title": lambda self, state: self._tool.bugs.fetch_bug(state["bug_id"]).title(), 59 64 } … … 70 75 def options(cls): 71 76 return [ 72 # We need th ese options here because cached_lookup uses them. :(77 # We need this option here because cached_lookup uses it. :( 73 78 Options.git_commit, 79 # FIXME: Get rid of these. 74 80 Options.no_squash, 75 81 Options.squash, -
trunk/WebKitTools/Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py
r58328 r63004 40 40 ApplyPatch.run(self, state) 41 41 if self._options.local_commit: 42 commit_message = self._tool.checkout().commit_message_for_this_commit(git_commit=None , squash=False)42 commit_message = self._tool.checkout().commit_message_for_this_commit(git_commit=None) 43 43 self._tool.scm().commit_locally_with_message(commit_message.message() or state["patch"].name()) -
trunk/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py
r59168 r63004 41 41 Options.check_style, 42 42 Options.git_commit, 43 Options.no_squash,44 Options.squash,45 43 ] 46 44 … … 54 52 args.append("--git-commit") 55 53 args.append(self._options.git_commit) 56 if self._tool.scm().should_squash(self._options.squash):57 args.append("--squash")58 else:59 args.append("--no-squash")60 54 61 55 try: -
trunk/WebKitTools/Scripts/webkitpy/tool/steps/commit.py
r58328 r63004 27 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 28 29 from webkitpy.common.checkout.scm import AuthenticationError, AmbiguousCommitError 30 from webkitpy.common.system.executive import ScriptError 31 from webkitpy.common.system.user import User 29 32 from webkitpy.tool.steps.abstractstep import AbstractStep 30 33 from webkitpy.tool.steps.options import Options … … 36 39 return AbstractStep.options() + [ 37 40 Options.git_commit, 38 Options.no_squash,39 Options.squash,40 41 ] 41 42 43 def _commit_warning(self, error): 44 working_directory_message = "" if error.working_directory_is_clean else " and working copy changes" 45 return ('There are %s local commits%s. Everything will be committed as a single commit. ' 46 'To avoid this prompt, set "git config webkit-patch.squash true".' % ( 47 error.num_local_commits, working_directory_message)) 48 42 49 def run(self, state): 43 commit_message = self._tool.checkout().commit_message_for_this_commit(self._options.git_commit, self._options.squash)44 if len( commit_message.message()) < 50:50 self._commit_message = self._tool.checkout().commit_message_for_this_commit(self._options.git_commit).message() 51 if len(self._commit_message) < 50: 45 52 raise Exception("Attempted to commit with a commit message shorter than 50 characters. Either your patch is missing a ChangeLog or webkit-patch may have a bug.") 46 state["commit_text"] = self._tool.scm().commit_with_message(commit_message.message(), 47 git_commit=self._options.git_commit, squash=self._options.squash) 53 54 self._state = state 55 56 username = None 57 force_squash = False 58 59 num_tries = 0 60 while num_tries < 3: 61 num_tries += 1 62 63 try: 64 self._state["commit_text"] = self._tool.scm().commit_with_message(self._commit_message, git_commit=self._options.git_commit, username=username, force_squash=force_squash) 65 break; 66 except AmbiguousCommitError, e: 67 if self._tool.user.confirm(self._commit_warning(e)): 68 force_squash = True 69 else: 70 # This will correctly interrupt the rest of the commit process. 71 raise ScriptError(message="Did not commit") 72 except AuthenticationError, e: 73 username = self._tool.user.prompt("%s login: " % e.server_host, repeat=5) 74 if not username: 75 raise ScriptError("You need to specify the username on %s to perform the commit as." % self.svn_server_host) -
trunk/WebKitTools/Scripts/webkitpy/tool/steps/options.py
r62518 r63004 44 44 email = make_option("--email", action="store", type="string", dest="email", help="Email address to use in ChangeLogs.") 45 45 force_clean = make_option("--force-clean", action="store_true", dest="force_clean", default=False, help="Clean working directory before applying patches (removes local changes and commits)") 46 # FIXME: Make commit ranges treat each commit separately instead of squashing them into one. 47 git_commit = make_option("--git-commit", action="store", dest="git_commit", help="Local git commit to upload/land. If a range, the commits are squashed into one.") 46 git_commit = make_option("-g", "--git-commit", action="store", dest="git_commit", help="Operate on a local commit. If a range, the commits are squashed into one. HEAD.. operates on working copy changes only.") 48 47 local_commit = make_option("--local-commit", action="store_true", dest="local_commit", default=False, help="Make a local commit for each applied patch") 49 no_squash = make_option("--no-squash", action="store_false", dest="squash", help="Don't squash local commits into one on upload/land (git-only).")50 48 non_interactive = make_option("--non-interactive", action="store_true", dest="non_interactive", default=False, help="Never prompt the user, fail as fast as possible.") 49 # FIXME: Remove --no-squash, once people have adjusted to using --git-commit. 50 no_squash = make_option("--no-squash", action="store_true", dest="no_squash", default=False, help="Obsolete. Use --git-commit=HEAD.. instead.") 51 51 obsolete_patches = make_option("--no-obsolete", action="store_false", dest="obsolete_patches", default=True, help="Do not obsolete old patches before posting this one.") 52 52 open_bug = make_option("--open-bug", action="store_true", dest="open_bug", default=False, help="Opens the associated bug in a browser.") … … 57 57 review = make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review.") 58 58 reviewer = make_option("-r", "--reviewer", action="store", type="string", dest="reviewer", help="Update ChangeLogs to say Reviewed by REVIEWER.") 59 squash = make_option("-s", "--squash", action="store_true", dest="squash", help="Squash all local commits into one on upload/land (git-only).") 59 # FIXME: Remove --squash, once people have adjusted to using --git-commit. 60 squash = make_option("-s", "--squash", action="store_true", dest="squash", default=False, help="Obsolete. This is now the default behavior.") 60 61 test = make_option("--test", action="store_true", dest="test", default=False, help="Run run-webkit-tests before committing.") 61 62 update = make_option("--no-update", action="store_false", dest="update", default=True, help="Don't update the working directory.") -
trunk/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog.py
r61287 r63004 44 44 Options.email, 45 45 Options.git_commit, 46 Options.no_squash,47 Options.squash,48 46 ] 49 47 … … 70 68 if self._options.email: 71 69 args.append("--email=%s" % self._options.email) 72 if self._tool.scm().should_squash(self._options.squash): 73 args.append("--merge-base=%s" % self._tool.scm().remote_merge_base()) 74 if self._options.git_commit: 75 args.append("--git-commit=%s" % self._options.git_commit) 70 71 if self._tool.scm().supports_local_commits(): 72 args.append("--merge-base=%s" % self._tool.scm().merge_base(self._options.git_commit)) 76 73 77 74 try: -
trunk/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py
r58261 r63004 37 37 # This could move to prepare-ChangeLog by adding a --revert= option. 38 38 self._run_script("prepare-ChangeLog") 39 changelog_paths = self._tool.checkout().modified_changelogs(git_commit=None , squash=False)39 changelog_paths = self._tool.checkout().modified_changelogs(git_commit=None) 40 40 bug_url = self._tool.bugs.bug_url_for_bug_id(state["bug_id"]) if state["bug_id"] else None 41 41 for changelog_path in changelog_paths: -
trunk/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py
r60124 r63004 41 41 Options.git_commit, 42 42 Options.reviewer, 43 Options.no_squash,44 Options.squash,45 43 ] 46 44 -
trunk/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer.py
r60124 r63004 42 42 return AbstractStep.options() + [ 43 43 Options.git_commit, 44 Options.no_squash,45 Options.squash,46 44 ] 47 45
Note: See TracChangeset
for help on using the changeset viewer.