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

Side by Side Diff: tools/push-to-trunk/common_includes.py

Issue 171423013: Refactoring: Extract low-level git from push and merge scripts. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Correct copyright year. Created 6 years, 10 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 | « tools/push-to-trunk/auto_roll.py ('k') | tools/push-to-trunk/git_recipes.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 2013 the V8 project authors. All rights reserved. 2 # Copyright 2013 the V8 project authors. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without 3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are 4 # modification, are permitted provided that the following conditions are
5 # met: 5 # met:
6 # 6 #
7 # * Redistributions of source code must retain the above copyright 7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer. 8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above 9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following 10 # copyright notice, this list of conditions and the following
(...skipping 18 matching lines...) Expand all
29 import datetime 29 import datetime
30 import json 30 import json
31 import os 31 import os
32 import re 32 import re
33 import subprocess 33 import subprocess
34 import sys 34 import sys
35 import textwrap 35 import textwrap
36 import time 36 import time
37 import urllib2 37 import urllib2
38 38
39 from git_recipes import GitRecipesMixin
40
39 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME" 41 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME"
40 TEMP_BRANCH = "TEMP_BRANCH" 42 TEMP_BRANCH = "TEMP_BRANCH"
41 BRANCHNAME = "BRANCHNAME" 43 BRANCHNAME = "BRANCHNAME"
42 DOT_GIT_LOCATION = "DOT_GIT_LOCATION" 44 DOT_GIT_LOCATION = "DOT_GIT_LOCATION"
43 VERSION_FILE = "VERSION_FILE" 45 VERSION_FILE = "VERSION_FILE"
44 CHANGELOG_FILE = "CHANGELOG_FILE" 46 CHANGELOG_FILE = "CHANGELOG_FILE"
45 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE" 47 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE"
46 COMMITMSG_FILE = "COMMITMSG_FILE" 48 COMMITMSG_FILE = "COMMITMSG_FILE"
47 PATCH_FILE = "PATCH_FILE" 49 PATCH_FILE = "PATCH_FILE"
48 50
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after
225 227
226 228
227 class CommonOptions(object): 229 class CommonOptions(object):
228 def __init__(self, options, manual=True): 230 def __init__(self, options, manual=True):
229 self.requires_editor = True 231 self.requires_editor = True
230 self.wait_for_lgtm = True 232 self.wait_for_lgtm = True
231 self.s = options.s 233 self.s = options.s
232 self.force_readline_defaults = not manual 234 self.force_readline_defaults = not manual
233 self.force_upload = not manual 235 self.force_upload = not manual
234 self.manual = manual 236 self.manual = manual
235 self.reviewer = getattr(options, 'reviewer', None) 237 self.reviewer = getattr(options, 'reviewer', "")
236 self.author = getattr(options, 'a', None) 238 self.author = getattr(options, 'a', "")
237 239
238 240
239 class Step(object): 241 class Step(GitRecipesMixin):
240 def __init__(self, text, requires, number, config, state, options, handler): 242 def __init__(self, text, requires, number, config, state, options, handler):
241 self._text = text 243 self._text = text
242 self._requires = requires 244 self._requires = requires
243 self._number = number 245 self._number = number
244 self._config = config 246 self._config = config
245 self._state = state 247 self._state = state
246 self._options = options 248 self._options = options
247 self._side_effect_handler = handler 249 self._side_effect_handler = handler
248 assert self._number >= 0 250 assert self._number >= 0
249 assert self._config is not None 251 assert self._config is not None
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 if not self._options.manual: 358 if not self._options.manual:
357 msg = msg or "Only available in manual mode." 359 msg = msg or "Only available in manual mode."
358 self.Die(msg) 360 self.Die(msg)
359 361
360 def Confirm(self, msg): 362 def Confirm(self, msg):
361 print "%s [Y/n] " % msg, 363 print "%s [Y/n] " % msg,
362 answer = self.ReadLine(default="Y") 364 answer = self.ReadLine(default="Y")
363 return answer == "" or answer == "Y" or answer == "y" 365 return answer == "" or answer == "Y" or answer == "y"
364 366
365 def DeleteBranch(self, name): 367 def DeleteBranch(self, name):
366 git_result = self.Git("branch").strip() 368 for line in self.GitBranch().splitlines():
367 for line in git_result.splitlines():
368 if re.match(r".*\s+%s$" % name, line): 369 if re.match(r".*\s+%s$" % name, line):
369 msg = "Branch %s exists, do you want to delete it?" % name 370 msg = "Branch %s exists, do you want to delete it?" % name
370 if self.Confirm(msg): 371 if self.Confirm(msg):
371 self.Git("branch -D %s" % name) 372 self.GitDeleteBranch(name)
372 print "Branch %s deleted." % name 373 print "Branch %s deleted." % name
373 else: 374 else:
374 msg = "Can't continue. Please delete branch %s and try again." % name 375 msg = "Can't continue. Please delete branch %s and try again." % name
375 self.Die(msg) 376 self.Die(msg)
376 377
377 def InitialEnvironmentChecks(self): 378 def InitialEnvironmentChecks(self):
378 # Cancel if this is not a git checkout. 379 # Cancel if this is not a git checkout.
379 if not os.path.exists(self._config[DOT_GIT_LOCATION]): 380 if not os.path.exists(self._config[DOT_GIT_LOCATION]):
380 self.Die("This is not a git checkout, this script won't work for you.") 381 self.Die("This is not a git checkout, this script won't work for you.")
381 382
382 # Cancel if EDITOR is unset or not executable. 383 # Cancel if EDITOR is unset or not executable.
383 if (self._options.requires_editor and (not os.environ.get("EDITOR") or 384 if (self._options.requires_editor and (not os.environ.get("EDITOR") or
384 Command("which", os.environ["EDITOR"]) is None)): 385 Command("which", os.environ["EDITOR"]) is None)):
385 self.Die("Please set your EDITOR environment variable, you'll need it.") 386 self.Die("Please set your EDITOR environment variable, you'll need it.")
386 387
387 def CommonPrepare(self): 388 def CommonPrepare(self):
388 # Check for a clean workdir. 389 # Check for a clean workdir.
389 if self.Git("status -s -uno").strip() != "": 390 if not self.GitIsWorkdirClean():
390 self.Die("Workspace is not clean. Please commit or undo your changes.") 391 self.Die("Workspace is not clean. Please commit or undo your changes.")
391 392
392 # Persist current branch. 393 # Persist current branch.
393 self["current_branch"] = "" 394 self["current_branch"] = self.GitCurrentBranch()
394 git_result = self.Git("status -s -b -uno").strip()
395 for line in git_result.splitlines():
396 match = re.match(r"^## (.+)", line)
397 if match:
398 self["current_branch"] = match.group(1)
399 break
400 395
401 # Fetch unfetched revisions. 396 # Fetch unfetched revisions.
402 self.Git("svn fetch") 397 self.GitSVNFetch()
403 398
404 def PrepareBranch(self): 399 def PrepareBranch(self):
405 # Get ahold of a safe temporary branch and check it out. 400 # Get ahold of a safe temporary branch and check it out.
406 if self["current_branch"] != self._config[TEMP_BRANCH]: 401 if self["current_branch"] != self._config[TEMP_BRANCH]:
407 self.DeleteBranch(self._config[TEMP_BRANCH]) 402 self.DeleteBranch(self._config[TEMP_BRANCH])
408 self.Git("checkout -b %s" % self._config[TEMP_BRANCH]) 403 self.GitCreateBranch(self._config[TEMP_BRANCH])
409 404
410 # Delete the branch that will be created later if it exists already. 405 # Delete the branch that will be created later if it exists already.
411 self.DeleteBranch(self._config[BRANCHNAME]) 406 self.DeleteBranch(self._config[BRANCHNAME])
412 407
413 def CommonCleanup(self): 408 def CommonCleanup(self):
414 self.Git("checkout -f %s" % self["current_branch"]) 409 self.GitCheckout(self["current_branch"])
415 if self._config[TEMP_BRANCH] != self["current_branch"]: 410 if self._config[TEMP_BRANCH] != self["current_branch"]:
416 self.Git("branch -D %s" % self._config[TEMP_BRANCH]) 411 self.GitDeleteBranch(self._config[TEMP_BRANCH])
417 if self._config[BRANCHNAME] != self["current_branch"]: 412 if self._config[BRANCHNAME] != self["current_branch"]:
418 self.Git("branch -D %s" % self._config[BRANCHNAME]) 413 self.GitDeleteBranch(self._config[BRANCHNAME])
419 414
420 # Clean up all temporary files. 415 # Clean up all temporary files.
421 Command("rm", "-f %s*" % self._config[PERSISTFILE_BASENAME]) 416 Command("rm", "-f %s*" % self._config[PERSISTFILE_BASENAME])
422 417
423 def ReadAndPersistVersion(self, prefix=""): 418 def ReadAndPersistVersion(self, prefix=""):
424 def ReadAndPersist(var_name, def_name): 419 def ReadAndPersist(var_name, def_name):
425 match = re.match(r"^#define %s\s+(\d*)" % def_name, line) 420 match = re.match(r"^#define %s\s+(\d*)" % def_name, line)
426 if match: 421 if match:
427 value = match.group(1) 422 value = match.group(1)
428 self["%s%s" % (prefix, var_name)] = value 423 self["%s%s" % (prefix, var_name)] = value
(...skipping 24 matching lines...) Expand all
453 answer = "" 448 answer = ""
454 while answer != "RESOLVED": 449 while answer != "RESOLVED":
455 if answer == "ABORT": 450 if answer == "ABORT":
456 self.Die("Applying the patch failed.") 451 self.Die("Applying the patch failed.")
457 if answer != "": 452 if answer != "":
458 print "That was not 'RESOLVED' or 'ABORT'." 453 print "That was not 'RESOLVED' or 'ABORT'."
459 print "> ", 454 print "> ",
460 answer = self.ReadLine() 455 answer = self.ReadLine()
461 456
462 # Takes a file containing the patch to apply as first argument. 457 # Takes a file containing the patch to apply as first argument.
463 def ApplyPatch(self, patch_file, reverse_patch=""): 458 def ApplyPatch(self, patch_file, revert=False):
464 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file)
465 try: 459 try:
466 self.Git(args) 460 self.GitApplyPatch(patch_file, revert)
467 except GitFailedException: 461 except GitFailedException:
468 self.WaitForResolvingConflicts(patch_file) 462 self.WaitForResolvingConflicts(patch_file)
469 463
470 def FindLastTrunkPush(self): 464 def FindLastTrunkPush(self, parent_hash=""):
471 push_pattern = "^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]* (based" 465 push_pattern = "^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]* (based"
472 args = "log -1 --format=%%H --grep=\"%s\" svn/trunk" % push_pattern 466 branch = "" if parent_hash else "svn/trunk"
473 return self.Git(args).strip() 467 return self.GitLog(n=1, format="%H", grep=push_pattern,
468 parent_hash=parent_hash, branch=branch)
474 469
475 470
476 class UploadStep(Step): 471 class UploadStep(Step):
477 MESSAGE = "Upload for code review." 472 MESSAGE = "Upload for code review."
478 473
479 def RunStep(self): 474 def RunStep(self):
480 if self._options.reviewer: 475 if self._options.reviewer:
481 print "Using account %s for review." % self._options.reviewer 476 print "Using account %s for review." % self._options.reviewer
482 reviewer = self._options.reviewer 477 reviewer = self._options.reviewer
483 else: 478 else:
484 print "Please enter the email address of a V8 reviewer for your patch: ", 479 print "Please enter the email address of a V8 reviewer for your patch: ",
485 self.DieNoManualMode("A reviewer must be specified in forced mode.") 480 self.DieNoManualMode("A reviewer must be specified in forced mode.")
486 reviewer = self.ReadLine() 481 reviewer = self.ReadLine()
487 author_option = self._options.author 482 self.GitUpload(reviewer, self._options.author, self._options.force_upload)
488 author = " --email \"%s\"" % author_option if author_option else ""
489 force_flag = " -f" if self._options.force_upload else ""
490 args = ("cl upload%s -r \"%s\" --send-mail%s"
491 % (author, reviewer, force_flag))
492 # TODO(machenbach): Check output in forced mode. Verify that all required
493 # base files were uploaded, if not retry.
494 self.Git(args, pipe=False)
495 483
496 484
497 def MakeStep(step_class=Step, number=0, state=None, config=None, 485 def MakeStep(step_class=Step, number=0, state=None, config=None,
498 options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): 486 options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER):
499 # Allow to pass in empty dictionaries. 487 # Allow to pass in empty dictionaries.
500 state = state if state is not None else {} 488 state = state if state is not None else {}
501 config = config if config is not None else {} 489 config = config if config is not None else {}
502 490
503 try: 491 try:
504 message = step_class.MESSAGE 492 message = step_class.MESSAGE
(...skipping 17 matching lines...) Expand all
522 if options.s == 0 and os.path.exists(state_file): 510 if options.s == 0 and os.path.exists(state_file):
523 os.remove(state_file) 511 os.remove(state_file)
524 state = {} 512 state = {}
525 steps = [] 513 steps = []
526 for (number, step_class) in enumerate(step_classes): 514 for (number, step_class) in enumerate(step_classes):
527 steps.append(MakeStep(step_class, number, state, config, 515 steps.append(MakeStep(step_class, number, state, config,
528 options, side_effect_handler)) 516 options, side_effect_handler))
529 517
530 for step in steps[options.s:]: 518 for step in steps[options.s:]:
531 step.Run() 519 step.Run()
OLDNEW
« no previous file with comments | « tools/push-to-trunk/auto_roll.py ('k') | tools/push-to-trunk/git_recipes.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698