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

Side by Side Diff: git_cl.py

Issue 10537117: Add support for new repo topology, with submodule-specific merge commits (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools/
Patch Set: Created 8 years, 6 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 | Annotate | Revision Log
« 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 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
62 return e.stdout 62 return e.stdout
63 63
64 64
65 def RunGit(args, **kwargs): 65 def RunGit(args, **kwargs):
66 """Returns stdout.""" 66 """Returns stdout."""
67 return RunCommand(['git'] + args, **kwargs) 67 return RunCommand(['git'] + args, **kwargs)
68 68
69 69
70 def RunGitWithCode(args): 70 def RunGitWithCode(args):
71 """Returns return code and stdout.""" 71 """Returns return code and stdout."""
72 out, code = subprocess2.communicate(['git'] + args, stdout=subprocess2.PIPE) 72 try:
73 return code, out[0] 73 out, code = subprocess2.communicate(['git'] + args, stdout=subprocess2.PIPE)
74 return code, out[0]
75 except ValueError:
76 # When the subprocess fails, it returns None. That triggers a ValueError
77 # when trying to unpack the return value into (out, code).
78 return 1, ''
74 79
75 80
76 def usage(more): 81 def usage(more):
77 def hook(fn): 82 def hook(fn):
78 fn.usage_more = more 83 fn.usage_more = more
79 return fn 84 return fn
80 return hook 85 return hook
81 86
82 87
83 def ask_for_data(prompt): 88 def ask_for_data(prompt):
(...skipping 1044 matching lines...) Expand 10 before | Expand all | Expand 10 after
1128 if 'GIT_EXTERNAL_DIFF' in env: 1133 if 'GIT_EXTERNAL_DIFF' in env:
1129 del env['GIT_EXTERNAL_DIFF'] 1134 del env['GIT_EXTERNAL_DIFF']
1130 subprocess2.call( 1135 subprocess2.call(
1131 ['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, env=env) 1136 ['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, env=env)
1132 1137
1133 if settings.GetIsGerrit(): 1138 if settings.GetIsGerrit():
1134 return GerritUpload(options, args, cl) 1139 return GerritUpload(options, args, cl)
1135 return RietveldUpload(options, args, cl) 1140 return RietveldUpload(options, args, cl)
1136 1141
1137 1142
1143 def IsSubmoduleMergeCommit(ref):
1144 # When submodules are added to the repo, we expect there to be a single
1145 # non-git-svn merge commit at remote HEAD with a signature comment.
1146 pattern = '^SVN changes up to revision [0-9]*$'
1147 cmd = ['rev-list', '--merges', '--grep="%s"' % pattern, '%s^!' % ref]
1148 return RunGit(cmd) != ''
1149
1150
1138 def SendUpstream(parser, args, cmd): 1151 def SendUpstream(parser, args, cmd):
1139 """Common code for CmdPush and CmdDCommit 1152 """Common code for CmdPush and CmdDCommit
1140 1153
1141 Squashed commit into a single. 1154 Squashed commit into a single.
1142 Updates changelog with metadata (e.g. pointer to review). 1155 Updates changelog with metadata (e.g. pointer to review).
1143 Pushes/dcommits the code upstream. 1156 Pushes/dcommits the code upstream.
1144 Updates review and closes. 1157 Updates review and closes.
1145 """ 1158 """
1146 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', 1159 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
1147 help='bypass upload presubmit hook') 1160 help='bypass upload presubmit hook')
(...skipping 11 matching lines...) Expand all
1159 if not args or cmd == 'push': 1172 if not args or cmd == 'push':
1160 # Default to merging against our best guess of the upstream branch. 1173 # Default to merging against our best guess of the upstream branch.
1161 args = [cl.GetUpstreamBranch()] 1174 args = [cl.GetUpstreamBranch()]
1162 1175
1163 if options.contributor: 1176 if options.contributor:
1164 if not re.match('^.*\s<\S+@\S+>$', options.contributor): 1177 if not re.match('^.*\s<\S+@\S+>$', options.contributor):
1165 print "Please provide contibutor as 'First Last <email@example.com>'" 1178 print "Please provide contibutor as 'First Last <email@example.com>'"
1166 return 1 1179 return 1
1167 1180
1168 base_branch = args[0] 1181 base_branch = args[0]
1182 base_has_submodules = IsSubmoduleMergeCommit(base_branch)
1169 1183
1170 # Make sure index is up-to-date before running diff-index. 1184 # Make sure index is up-to-date before running diff-index.
1171 RunGit(['update-index', '--refresh', '-q'], error_ok=True) 1185 RunGit(['update-index', '--refresh', '-q'], error_ok=True)
1172 if RunGit(['diff-index', 'HEAD']): 1186 if RunGit(['diff-index', 'HEAD']):
1173 print 'Cannot %s with a dirty tree. You must commit locally first.' % cmd 1187 print 'Cannot %s with a dirty tree. You must commit locally first.' % cmd
1174 return 1 1188 return 1
1175 1189
1176 # This rev-list syntax means "show all commits not in my branch that 1190 # This rev-list syntax means "show all commits not in my branch that
1177 # are in base_branch". 1191 # are in base_branch".
1178 upstream_commits = RunGit(['rev-list', '^' + cl.GetBranchRef(), 1192 upstream_commits = RunGit(['rev-list', '^' + cl.GetBranchRef(),
1179 base_branch]).splitlines() 1193 base_branch]).splitlines()
1180 if upstream_commits: 1194 if upstream_commits:
1181 print ('Base branch "%s" has %d commits ' 1195 print ('Base branch "%s" has %d commits '
1182 'not in this branch.' % (base_branch, len(upstream_commits))) 1196 'not in this branch.' % (base_branch, len(upstream_commits)))
1183 print 'Run "git merge %s" before attempting to %s.' % (base_branch, cmd) 1197 print 'Run "git merge %s" before attempting to %s.' % (base_branch, cmd)
1184 return 1 1198 return 1
1185 1199
1200 # This is the revision `svn dcommit` will commit on top of.
1201 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1',
1202 '--pretty=format:%H'])
1203
1186 if cmd == 'dcommit': 1204 if cmd == 'dcommit':
1187 # This is the revision `svn dcommit` will commit on top of. 1205 # If the base_head is a submodule merge commit, the first parent of the
1188 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1', 1206 # base_head should be a git-svn commit, which is what we're interested in.
1189 '--pretty=format:%H']) 1207 base_svn_head = base_branch
1190 extra_commits = RunGit(['rev-list', '^' + svn_head, base_branch]) 1208 if base_has_submodules:
1209 base_svn_head += '^1'
1210
1211 extra_commits = RunGit(['rev-list', '^' + svn_head, base_svn_head])
1191 if extra_commits: 1212 if extra_commits:
1192 print ('This branch has %d additional commits not upstreamed yet.' 1213 print ('This branch has %d additional commits not upstreamed yet.'
1193 % len(extra_commits.splitlines())) 1214 % len(extra_commits.splitlines()))
1194 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' 1215 print ('Upstream "%s" or rebase this branch on top of the upstream trunk '
1195 'before attempting to %s.' % (base_branch, cmd)) 1216 'before attempting to %s.' % (base_branch, cmd))
1196 return 1 1217 return 1
1197 1218
1198 if not options.bypass_hooks: 1219 if not options.bypass_hooks:
1199 author = None 1220 author = None
1200 if options.contributor: 1221 if options.contributor:
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
1239 1260
1240 if options.contributor: 1261 if options.contributor:
1241 description += "\nPatch from %s." % options.contributor 1262 description += "\nPatch from %s." % options.contributor
1242 print 'Description:', repr(description) 1263 print 'Description:', repr(description)
1243 1264
1244 branches = [base_branch, cl.GetBranchRef()] 1265 branches = [base_branch, cl.GetBranchRef()]
1245 if not options.force: 1266 if not options.force:
1246 subprocess2.call(['git', 'diff', '--stat'] + branches) 1267 subprocess2.call(['git', 'diff', '--stat'] + branches)
1247 ask_for_data('About to commit; enter to confirm.') 1268 ask_for_data('About to commit; enter to confirm.')
1248 1269
1249 # We want to squash all this branch's commits into one commit with the 1270 # We want to squash all this branch's commits into one commit with the proper
1250 # proper description. 1271 # description. We do this by doing a "reset --soft" to the base branch (which
1251 # We do this by doing a "reset --soft" to the base branch (which keeps 1272 # keeps the working copy the same), then dcommitting that. If origin/master
1252 # the working copy the same), then dcommitting that. 1273 # has a submodule merge commit, we'll also need to cherry-pick the squashed
1274 # commit onto a branch based on the git-svn head.
1253 MERGE_BRANCH = 'git-cl-commit' 1275 MERGE_BRANCH = 'git-cl-commit'
1254 # Delete the merge branch if it already exists. 1276 CHERRY_PICK_BRANCH = 'git-cl-cherry-pick'
1255 if RunGitWithCode(['show-ref', '--quiet', '--verify', 1277 # Delete the branches if they exist.
1256 'refs/heads/' + MERGE_BRANCH])[0] == 0: 1278 for branch in [MERGE_BRANCH, CHERRY_PICK_BRANCH]:
1257 RunGit(['branch', '-D', MERGE_BRANCH]) 1279 showref_cmd = ['show-ref', '--quiet', '--verify', 'refs/heads/%s' % branch]
1280 result = RunGitWithCode(showref_cmd)
1281 if result[0] == 0:
1282 RunGit(['branch', '-D', branch])
1258 1283
1259 # We might be in a directory that's present in this branch but not in the 1284 # We might be in a directory that's present in this branch but not in the
1260 # trunk. Move up to the top of the tree so that git commands that expect a 1285 # trunk. Move up to the top of the tree so that git commands that expect a
1261 # valid CWD won't fail after we check out the merge branch. 1286 # valid CWD won't fail after we check out the merge branch.
1262 rel_base_path = RunGit(['rev-parse', '--show-cdup']).strip() 1287 rel_base_path = RunGit(['rev-parse', '--show-cdup']).strip()
1263 if rel_base_path: 1288 if rel_base_path:
1264 os.chdir(rel_base_path) 1289 os.chdir(rel_base_path)
1265 1290
1266 # Stuff our change into the merge branch. 1291 # Stuff our change into the merge branch.
1267 # We wrap in a try...finally block so if anything goes wrong, 1292 # We wrap in a try...finally block so if anything goes wrong,
1268 # we clean up the branches. 1293 # we clean up the branches.
1269 retcode = -1 1294 retcode = -1
1270 try: 1295 try:
1271 RunGit(['checkout', '-q', '-b', MERGE_BRANCH]) 1296 RunGit(['checkout', '-q', '-b', MERGE_BRANCH])
1272 RunGit(['reset', '--soft', base_branch]) 1297 RunGit(['reset', '--soft', base_branch])
1273 if options.contributor: 1298 if options.contributor:
1274 RunGit(['commit', '--author', options.contributor, '-m', description]) 1299 RunGit(['commit', '--author', options.contributor, '-m', description])
1275 else: 1300 else:
1276 RunGit(['commit', '-m', description]) 1301 RunGit(['commit', '-m', description])
1302 if base_has_submodules:
1303 cherry_pick_commit = RunGit(['rev-list', 'HEAD^!']).rstrip()
1304 RunGit(['branch', CHERRY_PICK_BRANCH, svn_head])
1305 RunGit(['checkout', CHERRY_PICK_BRANCH])
1306 RunGit(['cherry-pick', cherry_pick_commit])
1277 if cmd == 'push': 1307 if cmd == 'push':
1278 # push the merge branch. 1308 # push the merge branch.
1279 remote, branch = cl.FetchUpstreamTuple() 1309 remote, branch = cl.FetchUpstreamTuple()
1280 retcode, output = RunGitWithCode( 1310 retcode, output = RunGitWithCode(
1281 ['push', '--porcelain', remote, 'HEAD:%s' % branch]) 1311 ['push', '--porcelain', remote, 'HEAD:%s' % branch])
1282 logging.debug(output) 1312 logging.debug(output)
1283 else: 1313 else:
1284 # dcommit the merge branch. 1314 # dcommit the merge branch.
1285 retcode, output = RunGitWithCode(['svn', 'dcommit', 1315 retcode, output = RunGitWithCode(['svn', 'dcommit',
1286 '--no-rebase', '--rmdir']) 1316 '--no-rebase', '--rmdir'])
1287 finally: 1317 finally:
1288 # And then swap back to the original branch and clean up. 1318 # And then swap back to the original branch and clean up.
1289 RunGit(['checkout', '-q', cl.GetBranch()]) 1319 RunGit(['checkout', '-q', cl.GetBranch()])
1290 RunGit(['branch', '-D', MERGE_BRANCH]) 1320 RunGit(['branch', '-D', MERGE_BRANCH])
1321 if base_has_submodules:
1322 RunGit(['branch', '-D', CHERRY_PICK_BRANCH])
1291 1323
1292 if cl.GetIssue(): 1324 if cl.GetIssue():
1293 if cmd == 'dcommit' and 'Committed r' in output: 1325 if cmd == 'dcommit' and 'Committed r' in output:
1294 revision = re.match('.*?\nCommitted r(\\d+)', output, re.DOTALL).group(1) 1326 revision = re.match('.*?\nCommitted r(\\d+)', output, re.DOTALL).group(1)
1295 elif cmd == 'push' and retcode == 0: 1327 elif cmd == 'push' and retcode == 0:
1296 match = (re.match(r'.*?([a-f0-9]{7})\.\.([a-f0-9]{7})$', l) 1328 match = (re.match(r'.*?([a-f0-9]{7})\.\.([a-f0-9]{7})$', l)
1297 for l in output.splitlines(False)) 1329 for l in output.splitlines(False))
1298 match = filter(None, match) 1330 match = filter(None, match)
1299 if len(match) != 1: 1331 if len(match) != 1:
1300 DieWithError("Couldn't parse ouput to extract the committed hash:\n%s" % 1332 DieWithError("Couldn't parse ouput to extract the committed hash:\n%s" %
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after
1567 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) 1599 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e)))
1568 1600
1569 # Not a known command. Default to help. 1601 # Not a known command. Default to help.
1570 GenUsage(parser, 'help') 1602 GenUsage(parser, 'help')
1571 return CMDhelp(parser, argv) 1603 return CMDhelp(parser, argv)
1572 1604
1573 1605
1574 if __name__ == '__main__': 1606 if __name__ == '__main__':
1575 fix_encoding.fix_encoding() 1607 fix_encoding.fix_encoding()
1576 sys.exit(main(sys.argv[1:])) 1608 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