OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 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 """git drover: A tool for merging changes to release branches.""" | 5 """git drover: A tool for merging changes to release branches.""" |
6 | 6 |
7 import argparse | 7 import argparse |
8 import cPickle | 8 import cPickle |
9 import functools | 9 import functools |
10 import logging | 10 import logging |
(...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
258 cPickle.dump(self, f) | 258 cPickle.dump(self, f) |
259 | 259 |
260 def _prepare_manual_resolve(self): | 260 def _prepare_manual_resolve(self): |
261 """Prepare the workdir for the user to manually resolve the cherry-pick.""" | 261 """Prepare the workdir for the user to manually resolve the cherry-pick.""" |
262 # Files that have been deleted between branch and cherry-pick will not have | 262 # Files that have been deleted between branch and cherry-pick will not have |
263 # their skip-worktree bit set so set it manually for those files to avoid | 263 # their skip-worktree bit set so set it manually for those files to avoid |
264 # git status incorrectly listing them as unstaged deletes. | 264 # git status incorrectly listing them as unstaged deletes. |
265 repo_status = self._run_git_command(['status', '--porcelain']).splitlines() | 265 repo_status = self._run_git_command(['status', '--porcelain']).splitlines() |
266 extra_files = [f[3:] for f in repo_status if f[:2] == ' D'] | 266 extra_files = [f[3:] for f in repo_status if f[:2] == ' D'] |
267 if extra_files: | 267 if extra_files: |
268 self._run_git_command(['update-index', '--skip-worktree', '--'] + | 268 extra_files_on_stdin = '\n'.join(extra_files) + '\n' |
269 extra_files) | 269 self._run_git_command_with_stdin( |
270 ['update-index', '--skip-worktree', '--stdin'], | |
271 stdin=extra_files_on_stdin) | |
iannucci
2016/06/14 00:03:38
I would inline extra_files_on_stdin, but that's ju
scottmg
2016/06/14 00:13:16
Done.
| |
270 | 272 |
271 def _upload_and_land(self): | 273 def _upload_and_land(self): |
272 if self._dry_run: | 274 if self._dry_run: |
273 logging.info('--dry_run enabled; not landing.') | 275 logging.info('--dry_run enabled; not landing.') |
274 return True | 276 return True |
275 | 277 |
276 self._run_git_command(['reset', '--hard']) | 278 self._run_git_command(['reset', '--hard']) |
277 self._run_git_command(['cl', 'upload'], | 279 self._run_git_command(['cl', 'upload'], |
278 error_message='Upload failed', | 280 error_message='Upload failed', |
279 interactive=True) | 281 interactive=True) |
(...skipping 27 matching lines...) Expand all Loading... | |
307 stderr = None if self._verbose else _DEV_NULL_FILE | 309 stderr = None if self._verbose else _DEV_NULL_FILE |
308 | 310 |
309 try: | 311 try: |
310 return run(['git'] + args, shell=False, cwd=cwd, stderr=stderr) | 312 return run(['git'] + args, shell=False, cwd=cwd, stderr=stderr) |
311 except (OSError, subprocess.CalledProcessError) as e: | 313 except (OSError, subprocess.CalledProcessError) as e: |
312 if error_message: | 314 if error_message: |
313 raise Error(error_message) | 315 raise Error(error_message) |
314 else: | 316 else: |
315 raise Error('Command %r failed: %s' % (' '.join(args), e)) | 317 raise Error('Command %r failed: %s' % (' '.join(args), e)) |
316 | 318 |
319 def _run_git_command_with_stdin(self, args, stdin): | |
320 """Runs a git command with a provided stdin. | |
321 | |
322 Args: | |
323 args: A list of strings containing the args to pass to git. | |
324 stdin: A string to provide on stdin. | |
325 | |
326 Raises: | |
327 Error: The command failed to complete successfully. | |
328 """ | |
329 cwd = self._workdir if self._workdir else self._parent_repo | |
330 logging.debug('Running git %s (cwd %r)', ' '.join('%s' % arg | |
331 for arg in args), cwd) | |
332 | |
333 # Discard stderr unless verbose is enabled. | |
334 stderr = None if self._verbose else _DEV_NULL_FILE | |
335 | |
336 try: | |
337 popen = subprocess.Popen(['git'] + args, shell=False, cwd=cwd, | |
338 stderr=stderr, stdin=subprocess.PIPE) | |
339 popen.communicate(stdin) | |
340 popen.wait() | |
iannucci
2016/06/14 00:03:38
communicate implies wait
scottmg
2016/06/14 00:13:16
Done.
| |
341 if popen.returncode != 0: | |
342 raise Error('Command %r failed' % ' '.join(args)) | |
343 except OSError as e: | |
344 raise Error('Command %r failed: %s' % (' '.join(args), e)) | |
345 | |
317 | 346 |
318 def cherry_pick_change(branch, revision, parent_repo, dry_run, verbose=False): | 347 def cherry_pick_change(branch, revision, parent_repo, dry_run, verbose=False): |
319 """Cherry-picks a change into a branch. | 348 """Cherry-picks a change into a branch. |
320 | 349 |
321 Args: | 350 Args: |
322 branch: A string containing the release branch number to which to | 351 branch: A string containing the release branch number to which to |
323 cherry-pick. | 352 cherry-pick. |
324 revision: A string containing the revision to cherry-pick. It can be any | 353 revision: A string containing the revision to cherry-pick. It can be any |
325 string that git-rev-parse can identify as referring to a single | 354 string that git-rev-parse can identify as referring to a single |
326 revision. | 355 revision. |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
415 cherry_pick_change(options.branch, options.cherry_pick, | 444 cherry_pick_change(options.branch, options.cherry_pick, |
416 options.parent_checkout, options.dry_run, | 445 options.parent_checkout, options.dry_run, |
417 options.verbose) | 446 options.verbose) |
418 except Error as e: | 447 except Error as e: |
419 print 'Error:', e.message | 448 print 'Error:', e.message |
420 sys.exit(128) | 449 sys.exit(128) |
421 | 450 |
422 | 451 |
423 if __name__ == '__main__': | 452 if __name__ == '__main__': |
424 main() | 453 main() |
OLD | NEW |