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

Side by Side Diff: git_cl.py

Issue 10945002: Add option to specify similarity level for git diff operations on commandline (Closed) Base URL: https://git.chromium.org/chromium/tools/depot_tools.git@master
Patch Set: rebase (which includes upstreamed upload.py, thanks m-a :)) Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 add_git_similarity(parser):
96 parser.add_option(
97 '--similarity', metavar='SIM', type='int', action='store', default=None,
98 help='Sets the percentage that a pair of files need to match in order to'
99 ' be considered copies (default 50)')
100
101 old_parser_args = parser.parse_args
102 def Parse(args):
103 options, args = old_parser_args(args)
104
105 branch = Changelist().GetBranch()
106 key = 'branch.%s.git-cl-similarity' % branch
107 if options.similarity is None:
108 if branch:
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:
116 if branch:
117 print('Note: Saving similarity of %d%% in git config.'
118 % options.similarity)
119 RunGit(['config', '--int', key, str(options.similarity)])
120
121 options.similarity = max(1, min(options.similarity, 100))
122
123 print('Using %d%% similarity for rename/copy detection. '
124 'Override with --similarity.' % options.similarity)
125
126 return options, args
127 parser.parse_args = Parse
128
129
95 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): 130 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards):
96 """Return the corresponding git ref if |base_url| together with |glob_spec| 131 """Return the corresponding git ref if |base_url| together with |glob_spec|
97 matches the full |url|. 132 matches the full |url|.
98 133
99 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). 134 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below).
100 """ 135 """
101 fetch_suburl, as_ref = glob_spec.split(':') 136 fetch_suburl, as_ref = glob_spec.split(':')
102 if allow_wildcards: 137 if allow_wildcards:
103 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl) 138 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl)
104 if glob_match: 139 if glob_match:
(...skipping 22 matching lines...) Expand all
127 # Parse specs like "trunk/src:refs/remotes/origin/trunk". 162 # Parse specs like "trunk/src:refs/remotes/origin/trunk".
128 if fetch_suburl: 163 if fetch_suburl:
129 full_url = base_url + '/' + fetch_suburl 164 full_url = base_url + '/' + fetch_suburl
130 else: 165 else:
131 full_url = base_url 166 full_url = base_url
132 if full_url == url: 167 if full_url == url:
133 return as_ref 168 return as_ref
134 return None 169 return None
135 170
136 171
137 def print_stats(args): 172 def print_stats(similarity, args):
138 """Prints statistics about the change to the user.""" 173 """Prints statistics about the change to the user."""
139 # --no-ext-diff is broken in some versions of Git, so try to work around 174 # --no-ext-diff is broken in some versions of Git, so try to work around
140 # this by overriding the environment (but there is still a problem if the 175 # this by overriding the environment (but there is still a problem if the
141 # git config key "diff.external" is used). 176 # git config key "diff.external" is used).
142 env = os.environ.copy() 177 env = os.environ.copy()
143 if 'GIT_EXTERNAL_DIFF' in env: 178 if 'GIT_EXTERNAL_DIFF' in env:
144 del env['GIT_EXTERNAL_DIFF'] 179 del env['GIT_EXTERNAL_DIFF']
145 return subprocess2.call( 180 return subprocess2.call(
146 ['git', 'diff', '--no-ext-diff', '--stat', '--find-copies-harder', 181 ['git', 'diff', '--no-ext-diff', '--stat', '--find-copies-harder',
147 '-l100000'] + args, env=env) 182 '-C%s' % similarity, '-l100000'] + args, env=env)
148 183
149 184
150 class Settings(object): 185 class Settings(object):
151 def __init__(self): 186 def __init__(self):
152 self.default_server = None 187 self.default_server = None
153 self.cc = None 188 self.cc = None
154 self.root = None 189 self.root = None
155 self.is_git_svn = None 190 self.is_git_svn = None
156 self.svn_branch = None 191 self.svn_branch = None
157 self.tree_status_url = None 192 self.tree_status_url = None
(...skipping 872 matching lines...) Expand 10 before | Expand all | Expand 10 after
1030 if change_desc.reviewers: 1065 if change_desc.reviewers:
1031 upload_args.extend(['--reviewers', change_desc.reviewers]) 1066 upload_args.extend(['--reviewers', change_desc.reviewers])
1032 if options.send_mail: 1067 if options.send_mail:
1033 if not change_desc.reviewers: 1068 if not change_desc.reviewers:
1034 DieWithError("Must specify reviewers to send email.") 1069 DieWithError("Must specify reviewers to send email.")
1035 upload_args.append('--send_mail') 1070 upload_args.append('--send_mail')
1036 cc = ','.join(filter(None, (cl.GetCCList(), options.cc))) 1071 cc = ','.join(filter(None, (cl.GetCCList(), options.cc)))
1037 if cc: 1072 if cc:
1038 upload_args.extend(['--cc', cc]) 1073 upload_args.extend(['--cc', cc])
1039 1074
1075 upload_args.extend(['--git_similarity', str(options.similarity)])
1076
1040 # Include the upstream repo's URL in the change -- this is useful for 1077 # Include the upstream repo's URL in the change -- this is useful for
1041 # projects that have their source spread across multiple repos. 1078 # projects that have their source spread across multiple repos.
1042 remote_url = cl.GetGitBaseUrlFromConfig() 1079 remote_url = cl.GetGitBaseUrlFromConfig()
1043 if not remote_url: 1080 if not remote_url:
1044 if settings.GetIsGitSvn(): 1081 if settings.GetIsGitSvn():
1045 # URL is dependent on the current directory. 1082 # URL is dependent on the current directory.
1046 data = RunGit(['svn', 'info'], cwd=settings.GetRoot()) 1083 data = RunGit(['svn', 'info'], cwd=settings.GetRoot())
1047 if data: 1084 if data:
1048 keys = dict(line.split(': ', 1) for line in data.splitlines() 1085 keys = dict(line.split(': ', 1) for line in data.splitlines()
1049 if ': ' in line) 1086 if ': ' in line)
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
1096 parser.add_option('--send-mail', action='store_true', 1133 parser.add_option('--send-mail', action='store_true',
1097 help='send email to reviewer immediately') 1134 help='send email to reviewer immediately')
1098 parser.add_option("--emulate_svn_auto_props", action="store_true", 1135 parser.add_option("--emulate_svn_auto_props", action="store_true",
1099 dest="emulate_svn_auto_props", 1136 dest="emulate_svn_auto_props",
1100 help="Emulate Subversion's auto properties feature.") 1137 help="Emulate Subversion's auto properties feature.")
1101 parser.add_option('-c', '--use-commit-queue', action='store_true', 1138 parser.add_option('-c', '--use-commit-queue', action='store_true',
1102 help='tell the commit queue to commit this patchset') 1139 help='tell the commit queue to commit this patchset')
1103 if settings.GetIsGerrit(): 1140 if settings.GetIsGerrit():
1104 parser.add_option('--target_branch', dest='target_branch', default='master', 1141 parser.add_option('--target_branch', dest='target_branch', default='master',
1105 help='target branch to upload') 1142 help='target branch to upload')
1143 add_git_similarity(parser)
1106 (options, args) = parser.parse_args(args) 1144 (options, args) = parser.parse_args(args)
1107 1145
1108 # Print warning if the user used the -m/--message argument. This will soon 1146 # Print warning if the user used the -m/--message argument. This will soon
1109 # change to -t/--title. 1147 # change to -t/--title.
1110 if options.message: 1148 if options.message:
1111 print >> sys.stderr, ( 1149 print >> sys.stderr, (
1112 '\nWARNING: Use -t or --title to set the title of the patchset.\n' 1150 '\nWARNING: Use -t or --title to set the title of the patchset.\n'
1113 'In the near future, -m or --message will send a message instead.\n' 1151 'In the near future, -m or --message will send a message instead.\n'
1114 'See http://goo.gl/JGg0Z for details.\n') 1152 'See http://goo.gl/JGg0Z for details.\n')
1115 1153
(...skipping 15 matching lines...) Expand all
1131 if not options.bypass_hooks: 1169 if not options.bypass_hooks:
1132 hook_results = cl.RunHook(committing=False, upstream_branch=base_branch, 1170 hook_results = cl.RunHook(committing=False, upstream_branch=base_branch,
1133 may_prompt=not options.force, 1171 may_prompt=not options.force,
1134 verbose=options.verbose, 1172 verbose=options.verbose,
1135 author=None) 1173 author=None)
1136 if not hook_results.should_continue(): 1174 if not hook_results.should_continue():
1137 return 1 1175 return 1
1138 if not options.reviewers and hook_results.reviewers: 1176 if not options.reviewers and hook_results.reviewers:
1139 options.reviewers = hook_results.reviewers 1177 options.reviewers = hook_results.reviewers
1140 1178
1141 print_stats(args) 1179 print_stats(options.similarity, args)
1142 if settings.GetIsGerrit(): 1180 if settings.GetIsGerrit():
1143 return GerritUpload(options, args, cl) 1181 return GerritUpload(options, args, cl)
1144 return RietveldUpload(options, args, cl) 1182 return RietveldUpload(options, args, cl)
1145 1183
1146 1184
1147 def IsSubmoduleMergeCommit(ref): 1185 def IsSubmoduleMergeCommit(ref):
1148 # When submodules are added to the repo, we expect there to be a single 1186 # When submodules are added to the repo, we expect there to be a single
1149 # non-git-svn merge commit at remote HEAD with a signature comment. 1187 # non-git-svn merge commit at remote HEAD with a signature comment.
1150 pattern = '^SVN changes up to revision [0-9]*$' 1188 pattern = '^SVN changes up to revision [0-9]*$'
1151 cmd = ['rev-list', '--merges', '--grep=%s' % pattern, '%s^!' % ref] 1189 cmd = ['rev-list', '--merges', '--grep=%s' % pattern, '%s^!' % ref]
(...skipping 11 matching lines...) Expand all
1163 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', 1201 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
1164 help='bypass upload presubmit hook') 1202 help='bypass upload presubmit hook')
1165 parser.add_option('-m', dest='message', 1203 parser.add_option('-m', dest='message',
1166 help="override review description") 1204 help="override review description")
1167 parser.add_option('-f', action='store_true', dest='force', 1205 parser.add_option('-f', action='store_true', dest='force',
1168 help="force yes to questions (don't prompt)") 1206 help="force yes to questions (don't prompt)")
1169 parser.add_option('-c', dest='contributor', 1207 parser.add_option('-c', dest='contributor',
1170 help="external contributor for patch (appended to " + 1208 help="external contributor for patch (appended to " +
1171 "description and used as author for git). Should be " + 1209 "description and used as author for git). Should be " +
1172 "formatted as 'First Last <email@example.com>'") 1210 "formatted as 'First Last <email@example.com>'")
1211 add_git_similarity(parser)
1173 (options, args) = parser.parse_args(args) 1212 (options, args) = parser.parse_args(args)
1174 cl = Changelist() 1213 cl = Changelist()
1175 1214
1176 if not args or cmd == 'push': 1215 if not args or cmd == 'push':
1177 # Default to merging against our best guess of the upstream branch. 1216 # Default to merging against our best guess of the upstream branch.
1178 args = [cl.GetUpstreamBranch()] 1217 args = [cl.GetUpstreamBranch()]
1179 1218
1180 if options.contributor: 1219 if options.contributor:
1181 if not re.match('^.*\s<\S+@\S+>$', options.contributor): 1220 if not re.match('^.*\s<\S+@\S+>$', options.contributor):
1182 print "Please provide contibutor as 'First Last <email@example.com>'" 1221 print "Please provide contibutor as 'First Last <email@example.com>'"
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
1264 1303
1265 if cl.GetIssue(): 1304 if cl.GetIssue():
1266 description += "\n\nReview URL: %s" % cl.GetIssueURL() 1305 description += "\n\nReview URL: %s" % cl.GetIssueURL()
1267 1306
1268 if options.contributor: 1307 if options.contributor:
1269 description += "\nPatch from %s." % options.contributor 1308 description += "\nPatch from %s." % options.contributor
1270 print 'Description:', repr(description) 1309 print 'Description:', repr(description)
1271 1310
1272 branches = [base_branch, cl.GetBranchRef()] 1311 branches = [base_branch, cl.GetBranchRef()]
1273 if not options.force: 1312 if not options.force:
1274 print_stats(branches) 1313 print_stats(options.similarity, branches)
1275 ask_for_data('About to commit; enter to confirm.') 1314 ask_for_data('About to commit; enter to confirm.')
1276 1315
1277 # We want to squash all this branch's commits into one commit with the proper 1316 # We want to squash all this branch's commits into one commit with the proper
1278 # description. We do this by doing a "reset --soft" to the base branch (which 1317 # description. We do this by doing a "reset --soft" to the base branch (which
1279 # keeps the working copy the same), then dcommitting that. If origin/master 1318 # keeps the working copy the same), then dcommitting that. If origin/master
1280 # has a submodule merge commit, we'll also need to cherry-pick the squashed 1319 # has a submodule merge commit, we'll also need to cherry-pick the squashed
1281 # commit onto a branch based on the git-svn head. 1320 # commit onto a branch based on the git-svn head.
1282 MERGE_BRANCH = 'git-cl-commit' 1321 MERGE_BRANCH = 'git-cl-commit'
1283 CHERRY_PICK_BRANCH = 'git-cl-cherry-pick' 1322 CHERRY_PICK_BRANCH = 'git-cl-cherry-pick'
1284 # Delete the branches if they exist. 1323 # Delete the branches if they exist.
(...skipping 28 matching lines...) Expand all
1313 RunGit(['cherry-pick', cherry_pick_commit]) 1352 RunGit(['cherry-pick', cherry_pick_commit])
1314 if cmd == 'push': 1353 if cmd == 'push':
1315 # push the merge branch. 1354 # push the merge branch.
1316 remote, branch = cl.FetchUpstreamTuple() 1355 remote, branch = cl.FetchUpstreamTuple()
1317 retcode, output = RunGitWithCode( 1356 retcode, output = RunGitWithCode(
1318 ['push', '--porcelain', remote, 'HEAD:%s' % branch]) 1357 ['push', '--porcelain', remote, 'HEAD:%s' % branch])
1319 logging.debug(output) 1358 logging.debug(output)
1320 else: 1359 else:
1321 # dcommit the merge branch. 1360 # dcommit the merge branch.
1322 retcode, output = RunGitWithCode(['svn', 'dcommit', 1361 retcode, output = RunGitWithCode(['svn', 'dcommit',
1362 '-C%s' % options.similarity,
1323 '--no-rebase', '--rmdir']) 1363 '--no-rebase', '--rmdir'])
1324 finally: 1364 finally:
1325 # And then swap back to the original branch and clean up. 1365 # And then swap back to the original branch and clean up.
1326 RunGit(['checkout', '-q', cl.GetBranch()]) 1366 RunGit(['checkout', '-q', cl.GetBranch()])
1327 RunGit(['branch', '-D', MERGE_BRANCH]) 1367 RunGit(['branch', '-D', MERGE_BRANCH])
1328 if base_has_submodules: 1368 if base_has_submodules:
1329 RunGit(['branch', '-D', CHERRY_PICK_BRANCH]) 1369 RunGit(['branch', '-D', CHERRY_PICK_BRANCH])
1330 1370
1331 if cl.GetIssue(): 1371 if cl.GetIssue():
1332 if cmd == 'dcommit' and 'Committed r' in output: 1372 if cmd == 'dcommit' and 'Committed r' in output:
(...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after
1716 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) 1756 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e)))
1717 1757
1718 # Not a known command. Default to help. 1758 # Not a known command. Default to help.
1719 GenUsage(parser, 'help') 1759 GenUsage(parser, 'help')
1720 return CMDhelp(parser, argv) 1760 return CMDhelp(parser, argv)
1721 1761
1722 1762
1723 if __name__ == '__main__': 1763 if __name__ == '__main__':
1724 fix_encoding.fix_encoding() 1764 fix_encoding.fix_encoding()
1725 sys.exit(main(sys.argv[1:])) 1765 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698