| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 json | 10 import json |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 85 | 85 |
| 86 | 86 |
| 87 def ask_for_data(prompt): | 87 def ask_for_data(prompt): |
| 88 try: | 88 try: |
| 89 return raw_input(prompt) | 89 return raw_input(prompt) |
| 90 except KeyboardInterrupt: | 90 except KeyboardInterrupt: |
| 91 # Hide the exception. | 91 # Hide the exception. |
| 92 sys.exit(1) | 92 sys.exit(1) |
| 93 | 93 |
| 94 | 94 |
| 95 def git_set_branch_value(key, value): |
| 96 branch = Changelist().GetBranch() |
| 97 if branch: |
| 98 git_key = 'branch.%s.%s' % (branch, key) |
| 99 RunGit(['config', '--int', git_key, "%d" % value]) |
| 100 |
| 101 |
| 102 def git_get_branch_default(key, default): |
| 103 branch = Changelist().GetBranch() |
| 104 if branch: |
| 105 git_key = 'branch.%s.%s' % (branch, key) |
| 106 (_, stdout) = RunGitWithCode(['config', '--int', '--get', git_key]) |
| 107 try: |
| 108 return int(stdout.strip()) |
| 109 except ValueError: |
| 110 pass |
| 111 return default |
| 112 |
| 113 |
| 95 def add_git_similarity(parser): | 114 def add_git_similarity(parser): |
| 96 parser.add_option( | 115 parser.add_option( |
| 97 '--similarity', metavar='SIM', type='int', action='store', default=None, | 116 '--similarity', metavar='SIM', type='int', action='store', |
| 98 help='Sets the percentage that a pair of files need to match in order to' | 117 help='Sets the percentage that a pair of files need to match in order to' |
| 99 ' be considered copies (default 50)') | 118 ' be considered copies (default 50)') |
| 119 parser.add_option( |
| 120 '--find-copies', action='store_true', |
| 121 help='Allows git to look for copies.') |
| 122 parser.add_option( |
| 123 '--no-find-copies', action='store_false', dest='find_copies', |
| 124 help='Disallows git from looking for copies.') |
| 100 | 125 |
| 101 old_parser_args = parser.parse_args | 126 old_parser_args = parser.parse_args |
| 102 def Parse(args): | 127 def Parse(args): |
| 103 options, args = old_parser_args(args) | 128 options, args = old_parser_args(args) |
| 104 | 129 |
| 105 branch = Changelist().GetBranch() | |
| 106 key = 'branch.%s.git-cl-similarity' % branch | |
| 107 if options.similarity is None: | 130 if options.similarity is None: |
| 108 if branch: | 131 options.similarity = git_get_branch_default('git-cl-similarity', 50) |
| 109 (_, stdout) = RunGitWithCode(['config', '--int', '--get', key]) | |
| 110 try: | |
| 111 options.similarity = int(stdout.strip()) | |
| 112 except ValueError: | |
| 113 pass | |
| 114 options.similarity = options.similarity or 50 | |
| 115 else: | 132 else: |
| 116 if branch: | 133 print('Note: Saving similarity of %d%% in git config.' |
| 117 print('Note: Saving similarity of %d%% in git config.' | 134 % options.similarity) |
| 118 % options.similarity) | 135 git_set_branch_value('git-cl-similarity', options.similarity) |
| 119 RunGit(['config', '--int', key, str(options.similarity)]) | |
| 120 | 136 |
| 121 options.similarity = max(1, min(options.similarity, 100)) | 137 options.similarity = max(0, min(options.similarity, 100)) |
| 138 |
| 139 if options.find_copies is None: |
| 140 options.find_copies = bool( |
| 141 git_get_branch_default('git-find-copies', True)) |
| 142 else: |
| 143 git_set_branch_value('git-find-copies', int(options.find_copies)) |
| 122 | 144 |
| 123 print('Using %d%% similarity for rename/copy detection. ' | 145 print('Using %d%% similarity for rename/copy detection. ' |
| 124 'Override with --similarity.' % options.similarity) | 146 'Override with --similarity.' % options.similarity) |
| 125 | 147 |
| 126 return options, args | 148 return options, args |
| 127 parser.parse_args = Parse | 149 parser.parse_args = Parse |
| 128 | 150 |
| 129 | 151 |
| 130 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): | 152 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): |
| 131 """Return the corresponding git ref if |base_url| together with |glob_spec| | 153 """Return the corresponding git ref if |base_url| together with |glob_spec| |
| (...skipping 30 matching lines...) Expand all Loading... |
| 162 # Parse specs like "trunk/src:refs/remotes/origin/trunk". | 184 # Parse specs like "trunk/src:refs/remotes/origin/trunk". |
| 163 if fetch_suburl: | 185 if fetch_suburl: |
| 164 full_url = base_url + '/' + fetch_suburl | 186 full_url = base_url + '/' + fetch_suburl |
| 165 else: | 187 else: |
| 166 full_url = base_url | 188 full_url = base_url |
| 167 if full_url == url: | 189 if full_url == url: |
| 168 return as_ref | 190 return as_ref |
| 169 return None | 191 return None |
| 170 | 192 |
| 171 | 193 |
| 172 def print_stats(similarity, args): | 194 def print_stats(similarity, find_copies, args): |
| 173 """Prints statistics about the change to the user.""" | 195 """Prints statistics about the change to the user.""" |
| 174 # --no-ext-diff is broken in some versions of Git, so try to work around | 196 # --no-ext-diff is broken in some versions of Git, so try to work around |
| 175 # this by overriding the environment (but there is still a problem if the | 197 # this by overriding the environment (but there is still a problem if the |
| 176 # git config key "diff.external" is used). | 198 # git config key "diff.external" is used). |
| 177 env = os.environ.copy() | 199 env = os.environ.copy() |
| 178 if 'GIT_EXTERNAL_DIFF' in env: | 200 if 'GIT_EXTERNAL_DIFF' in env: |
| 179 del env['GIT_EXTERNAL_DIFF'] | 201 del env['GIT_EXTERNAL_DIFF'] |
| 202 |
| 203 if find_copies: |
| 204 similarity_options = ['--find-copies-harder', '-l100000', |
| 205 '-C%s' % similarity] |
| 206 else: |
| 207 similarity_options = ['-M%s' % similarity] |
| 208 |
| 180 return subprocess2.call( | 209 return subprocess2.call( |
| 181 ['git', 'diff', '--no-ext-diff', '--stat', '--find-copies-harder', | 210 ['git', 'diff', '--no-ext-diff', '--stat'] + similarity_options + args, |
| 182 '-C%s' % similarity, '-l100000'] + args, env=env) | 211 env=env) |
| 183 | 212 |
| 184 | 213 |
| 185 class Settings(object): | 214 class Settings(object): |
| 186 def __init__(self): | 215 def __init__(self): |
| 187 self.default_server = None | 216 self.default_server = None |
| 188 self.cc = None | 217 self.cc = None |
| 189 self.root = None | 218 self.root = None |
| 190 self.is_git_svn = None | 219 self.is_git_svn = None |
| 191 self.svn_branch = None | 220 self.svn_branch = None |
| 192 self.tree_status_url = None | 221 self.tree_status_url = None |
| (...skipping 873 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1066 upload_args.extend(['--reviewers', change_desc.reviewers]) | 1095 upload_args.extend(['--reviewers', change_desc.reviewers]) |
| 1067 if options.send_mail: | 1096 if options.send_mail: |
| 1068 if not change_desc.reviewers: | 1097 if not change_desc.reviewers: |
| 1069 DieWithError("Must specify reviewers to send email.") | 1098 DieWithError("Must specify reviewers to send email.") |
| 1070 upload_args.append('--send_mail') | 1099 upload_args.append('--send_mail') |
| 1071 cc = ','.join(filter(None, (cl.GetCCList(), options.cc))) | 1100 cc = ','.join(filter(None, (cl.GetCCList(), options.cc))) |
| 1072 if cc: | 1101 if cc: |
| 1073 upload_args.extend(['--cc', cc]) | 1102 upload_args.extend(['--cc', cc]) |
| 1074 | 1103 |
| 1075 upload_args.extend(['--git_similarity', str(options.similarity)]) | 1104 upload_args.extend(['--git_similarity', str(options.similarity)]) |
| 1105 if not options.find_copies: |
| 1106 upload_args.extend(['--git_no_find_copies']) |
| 1076 | 1107 |
| 1077 # Include the upstream repo's URL in the change -- this is useful for | 1108 # Include the upstream repo's URL in the change -- this is useful for |
| 1078 # projects that have their source spread across multiple repos. | 1109 # projects that have their source spread across multiple repos. |
| 1079 remote_url = cl.GetGitBaseUrlFromConfig() | 1110 remote_url = cl.GetGitBaseUrlFromConfig() |
| 1080 if not remote_url: | 1111 if not remote_url: |
| 1081 if settings.GetIsGitSvn(): | 1112 if settings.GetIsGitSvn(): |
| 1082 # URL is dependent on the current directory. | 1113 # URL is dependent on the current directory. |
| 1083 data = RunGit(['svn', 'info'], cwd=settings.GetRoot()) | 1114 data = RunGit(['svn', 'info'], cwd=settings.GetRoot()) |
| 1084 if data: | 1115 if data: |
| 1085 keys = dict(line.split(': ', 1) for line in data.splitlines() | 1116 keys = dict(line.split(': ', 1) for line in data.splitlines() |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1169 if not options.bypass_hooks: | 1200 if not options.bypass_hooks: |
| 1170 hook_results = cl.RunHook(committing=False, upstream_branch=base_branch, | 1201 hook_results = cl.RunHook(committing=False, upstream_branch=base_branch, |
| 1171 may_prompt=not options.force, | 1202 may_prompt=not options.force, |
| 1172 verbose=options.verbose, | 1203 verbose=options.verbose, |
| 1173 author=None) | 1204 author=None) |
| 1174 if not hook_results.should_continue(): | 1205 if not hook_results.should_continue(): |
| 1175 return 1 | 1206 return 1 |
| 1176 if not options.reviewers and hook_results.reviewers: | 1207 if not options.reviewers and hook_results.reviewers: |
| 1177 options.reviewers = hook_results.reviewers | 1208 options.reviewers = hook_results.reviewers |
| 1178 | 1209 |
| 1179 print_stats(options.similarity, args) | 1210 print_stats(options.similarity, options.find_copies, args) |
| 1180 if settings.GetIsGerrit(): | 1211 if settings.GetIsGerrit(): |
| 1181 return GerritUpload(options, args, cl) | 1212 return GerritUpload(options, args, cl) |
| 1182 return RietveldUpload(options, args, cl) | 1213 return RietveldUpload(options, args, cl) |
| 1183 | 1214 |
| 1184 | 1215 |
| 1185 def IsSubmoduleMergeCommit(ref): | 1216 def IsSubmoduleMergeCommit(ref): |
| 1186 # When submodules are added to the repo, we expect there to be a single | 1217 # When submodules are added to the repo, we expect there to be a single |
| 1187 # non-git-svn merge commit at remote HEAD with a signature comment. | 1218 # non-git-svn merge commit at remote HEAD with a signature comment. |
| 1188 pattern = '^SVN changes up to revision [0-9]*$' | 1219 pattern = '^SVN changes up to revision [0-9]*$' |
| 1189 cmd = ['rev-list', '--merges', '--grep=%s' % pattern, '%s^!' % ref] | 1220 cmd = ['rev-list', '--merges', '--grep=%s' % pattern, '%s^!' % ref] |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1303 | 1334 |
| 1304 if cl.GetIssue(): | 1335 if cl.GetIssue(): |
| 1305 description += "\n\nReview URL: %s" % cl.GetIssueURL() | 1336 description += "\n\nReview URL: %s" % cl.GetIssueURL() |
| 1306 | 1337 |
| 1307 if options.contributor: | 1338 if options.contributor: |
| 1308 description += "\nPatch from %s." % options.contributor | 1339 description += "\nPatch from %s." % options.contributor |
| 1309 print 'Description:', repr(description) | 1340 print 'Description:', repr(description) |
| 1310 | 1341 |
| 1311 branches = [base_branch, cl.GetBranchRef()] | 1342 branches = [base_branch, cl.GetBranchRef()] |
| 1312 if not options.force: | 1343 if not options.force: |
| 1313 print_stats(options.similarity, branches) | 1344 print_stats(options.similarity, options.find_copies, branches) |
| 1314 ask_for_data('About to commit; enter to confirm.') | 1345 ask_for_data('About to commit; enter to confirm.') |
| 1315 | 1346 |
| 1316 # We want to squash all this branch's commits into one commit with the proper | 1347 # We want to squash all this branch's commits into one commit with the proper |
| 1317 # description. We do this by doing a "reset --soft" to the base branch (which | 1348 # description. We do this by doing a "reset --soft" to the base branch (which |
| 1318 # keeps the working copy the same), then dcommitting that. If origin/master | 1349 # keeps the working copy the same), then dcommitting that. If origin/master |
| 1319 # has a submodule merge commit, we'll also need to cherry-pick the squashed | 1350 # has a submodule merge commit, we'll also need to cherry-pick the squashed |
| 1320 # commit onto a branch based on the git-svn head. | 1351 # commit onto a branch based on the git-svn head. |
| 1321 MERGE_BRANCH = 'git-cl-commit' | 1352 MERGE_BRANCH = 'git-cl-commit' |
| 1322 CHERRY_PICK_BRANCH = 'git-cl-cherry-pick' | 1353 CHERRY_PICK_BRANCH = 'git-cl-cherry-pick' |
| 1323 # Delete the branches if they exist. | 1354 # Delete the branches if they exist. |
| (...skipping 434 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1758 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 1789 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) |
| 1759 | 1790 |
| 1760 # Not a known command. Default to help. | 1791 # Not a known command. Default to help. |
| 1761 GenUsage(parser, 'help') | 1792 GenUsage(parser, 'help') |
| 1762 return CMDhelp(parser, argv) | 1793 return CMDhelp(parser, argv) |
| 1763 | 1794 |
| 1764 | 1795 |
| 1765 if __name__ == '__main__': | 1796 if __name__ == '__main__': |
| 1766 fix_encoding.fix_encoding() | 1797 fix_encoding.fix_encoding() |
| 1767 sys.exit(main(sys.argv[1:])) | 1798 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |