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

Unified Diff: trychange_git.py

Issue 1003733006: Splitting trychange for git_try and removing no longer needed support for: (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 5 years, 9 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tests/trychange_git_unittest.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: trychange_git.py
diff --git a/trychange.py b/trychange_git.py
similarity index 66%
copy from trychange.py
copy to trychange_git.py
index 719275a12c9e355a44654ec3e90c06a7d0b58731..8b02aaa48fe1affff267a57f1bede0d62e927947 100755
--- a/trychange.py
+++ b/trychange_git.py
@@ -26,12 +26,8 @@ import urllib
import urllib2
import urlparse
-import breakpad # pylint: disable=W0611
-
-import fix_encoding
-import gcl
import gclient_utils
-import gerrit_util
+import git_cl
import scm
import subprocess2
@@ -44,8 +40,7 @@ HELP_STRING = "Sorry, Tryserver is not available."
USAGE = r"""%prog [options]
Client-side script to send a try job to the try server. It communicates to
-the try server by either writting to a svn repository or by directly connecting
-to the server by HTTP."""
+the try server by writting to a svn repository."""
EPILOG = """
Examples:
@@ -75,7 +70,7 @@ Examples:
GIT_PATCH_DIR_BASENAME = os.path.join('git-try', 'patches-git')
GIT_BRANCH_FILE = 'ref'
-_GIT_PUSH_ATTEMPTS = 3
+
def DieWithError(message):
print >> sys.stderr, message
@@ -97,21 +92,19 @@ def RunGit(args, **kwargs):
"""Returns stdout."""
return RunCommand(['git'] + args, **kwargs)
+
class Error(Exception):
"""An error during a try job submission.
For this error, trychange.py does not display stack trace, only message
"""
-class InvalidScript(Error):
- def __str__(self):
- return self.args[0] + '\n' + HELP_STRING
-
class NoTryServerAccess(Error):
def __str__(self):
return self.args[0] + '\n' + HELP_STRING
+
def Escape(name):
"""Escapes characters that could interfere with the file system or try job
parsing.
@@ -119,8 +112,9 @@ def Escape(name):
return re.sub(r'[^\w#-]', '_', name)
-class SCM(object):
- """Simplistic base class to implement one function: ProcessOptions."""
+class GIT(object):
+
+ """Gathers the options and diff for a git checkout."""
def __init__(self, options, path, file_list):
items = path.split('@')
assert len(items) <= 2
@@ -138,20 +132,21 @@ class SCM(object):
self.codereview_settings = None
self.codereview_settings_file = 'codereview.settings'
self.toplevel_root = None
-
- def GetFileNames(self):
- """Return the list of files in the diff."""
- return self.files
+ self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root)
+ if not self.options.name:
+ self.options.name = scm.GIT.GetPatchName(self.checkout_root)
+ if not self.options.email:
+ self.options.email = scm.GIT.GetEmail(self.checkout_root)
+ if not self.diff_against:
+ self.diff_against = scm.GIT.GetUpstreamBranch(self.checkout_root)
+ if not self.diff_against:
+ raise NoTryServerAccess(
+ "Unable to determine default branch to diff against. "
+ "Verify this branch is set up to track another"
+ "(via the --track argument to \"git checkout -b ...\"")
+ logging.info("GIT(%s)" % self.checkout_root)
def GetCodeReviewSetting(self, key):
- """Returns a value for the given key for this repository.
-
- Uses gcl-style settings from the repository.
- """
- if gcl:
- gcl_setting = gcl.GetCodeReviewSetting(key)
- if gcl_setting != '':
- return gcl_setting
if self.codereview_settings is None:
self.codereview_settings = {}
settings_file = self.ReadRootFile(self.codereview_settings_file)
@@ -171,11 +166,7 @@ class SCM(object):
settings in codereview.settings).
"""
settings = {
- 'port': self.GetCodeReviewSetting('TRYSERVER_HTTP_PORT'),
- 'host': self.GetCodeReviewSetting('TRYSERVER_HTTP_HOST'),
'svn_repo': self.GetCodeReviewSetting('TRYSERVER_SVN_URL'),
- 'gerrit_url': self.GetCodeReviewSetting('TRYSERVER_GERRIT_URL'),
- 'git_repo': self.GetCodeReviewSetting('TRYSERVER_GIT_URL'),
'project': self.GetCodeReviewSetting('TRYSERVER_PROJECT'),
# Primarily for revision=auto
'revision': self.GetCodeReviewSetting('TRYSERVER_REVISION'),
@@ -244,12 +235,6 @@ class SCM(object):
self._file_tuples = [f for f in file_tuples if not Excluded(f)]
self._files = [f[1] for f in self._file_tuples]
- def CaptureStatus(self):
- """Returns the 'svn status' emulated output as an array of (status, file)
- tuples."""
- raise NotImplementedError(
- "abstract method -- subclass %s must override" % self.__class__)
-
@property
def files(self):
if self._files is None:
@@ -262,60 +247,6 @@ class SCM(object):
self._SetFileTuples(self.CaptureStatus())
return self._file_tuples
-
-class SVN(SCM):
- """Gathers the options and diff for a subversion checkout."""
- def __init__(self, *args, **kwargs):
- SCM.__init__(self, *args, **kwargs)
- self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root)
- if not self.options.email:
- # Assumes the svn credential is an email address.
- self.options.email = scm.SVN.GetEmail(self.checkout_root)
- logging.info("SVN(%s)" % self.checkout_root)
-
- def ReadRootFile(self, filename):
- data = SCM.ReadRootFile(self, filename)
- if data:
- return data
-
- # Try to search on the subversion repository for the file.
- if not gcl:
- return None
- data = gcl.GetCachedFile(filename)
- logging.debug('%s:\n%s' % (filename, data))
- return data
-
- def CaptureStatus(self):
- return scm.SVN.CaptureStatus(None, self.checkout_root)
-
- def GenerateDiff(self):
- """Returns a string containing the diff for the given file list.
-
- The files in the list should either be absolute paths or relative to the
- given root.
- """
- return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True,
- revision=self.diff_against)
-
-
-class GIT(SCM):
- """Gathers the options and diff for a git checkout."""
- def __init__(self, *args, **kwargs):
- SCM.__init__(self, *args, **kwargs)
- self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root)
- if not self.options.name:
- self.options.name = scm.GIT.GetPatchName(self.checkout_root)
- if not self.options.email:
- self.options.email = scm.GIT.GetEmail(self.checkout_root)
- if not self.diff_against:
- self.diff_against = scm.GIT.GetUpstreamBranch(self.checkout_root)
- if not self.diff_against:
- raise NoTryServerAccess(
- "Unable to determine default branch to diff against. "
- "Verify this branch is set up to track another"
- "(via the --track argument to \"git checkout -b ...\"")
- logging.info("GIT(%s)" % self.checkout_root)
-
def CaptureStatus(self):
return scm.GIT.CaptureStatus(
[],
@@ -392,7 +323,7 @@ def _GenTSBotSpec(checkouts, change, changed_files, options):
options.email)
masters = presubmit_support.DoGetTryMasters(
change,
- checkouts[0].GetFileNames(),
+ checkouts[0].files,
checkouts[0].checkout_root,
root_presubmit,
options.project,
@@ -458,48 +389,6 @@ def _ParseSendChangeOptions(bot_spec, options):
return values
-def _SendChangeHTTP(bot_spec, options):
- """Send a change to the try server using the HTTP protocol."""
- if not options.host:
- raise NoTryServerAccess('Please use the --host option to specify the try '
- 'server host to connect to.')
- if not options.port:
- raise NoTryServerAccess('Please use the --port option to specify the try '
- 'server port to connect to.')
-
- values = _ParseSendChangeOptions(bot_spec, options)
- values.append(('patch', options.diff))
-
- url = 'http://%s:%s/send_try_patch' % (options.host, options.port)
-
- logging.info('Sending by HTTP')
- logging.info(''.join("%s=%s\n" % (k, v) for k, v in values))
- logging.info(url)
- logging.info(options.diff)
- if options.dry_run:
- return
-
- try:
- logging.info('Opening connection...')
- connection = urllib2.urlopen(url, urllib.urlencode(values))
- logging.info('Done')
- except IOError, e:
- logging.info(str(e))
- if bot_spec and len(e.args) > 2 and e.args[2] == 'got a bad status line':
- raise NoTryServerAccess('%s is unaccessible. Bad --bot argument?' % url)
- else:
- raise NoTryServerAccess('%s is unaccessible. Reason: %s' % (url,
- str(e.args)))
- if not connection:
- raise NoTryServerAccess('%s is unaccessible.' % url)
- logging.info('Reading response...')
- response = connection.read()
- logging.info('Done')
- if response != 'OK':
- raise NoTryServerAccess('%s is unaccessible. Got:\n%s' % (url, response))
-
- PrintSuccess(bot_spec, options)
-
@contextlib.contextmanager
def _TempFilename(name, contents=None):
"""Create a temporary directory, append the specified name and yield.
@@ -578,6 +467,7 @@ def _SendChangeSVN(bot_spec, options):
PrintSuccess(bot_spec, options)
+
def _GetPatchGitRepo(git_url):
"""Gets a path to a Git repo with patches.
@@ -619,187 +509,6 @@ def _GetPatchGitRepo(git_url):
return os.path.abspath(patch_dir)
-def _SendChangeGit(bot_spec, options):
- """Sends a change to the try server by committing a diff file to a GIT repo.
-
- Creates a temp orphan branch, commits patch.diff, creates a ref pointing to
- that commit, deletes the temp branch, checks master out, adds 'ref' file
- containing the name of the new ref, pushes master and the ref to the origin.
-
- TODO: instead of creating a temp branch, use git-commit-tree.
- """
-
- if not options.git_repo:
- raise NoTryServerAccess('Please use the --git_repo option to specify the '
- 'try server git repository to connect to.')
-
- values = _ParseSendChangeOptions(bot_spec, options)
- comment_subject = '%s.%s' % (options.user, options.name)
- comment_body = ''.join("%s=%s\n" % (k, v) for k, v in values)
- description = '%s\n\n%s' % (comment_subject, comment_body)
- logging.info('Sending by GIT')
- logging.info(description)
- logging.info(options.git_repo)
- logging.info(options.diff)
- if options.dry_run:
- return
-
- patch_dir = _GetPatchGitRepo(options.git_repo)
- def patch_git(*args):
- return scm.GIT.Capture(list(args), cwd=patch_dir)
- def add_and_commit(filename, comment_filename):
- patch_git('add', filename)
- patch_git('commit', '-F', comment_filename)
-
- assert scm.GIT.IsInsideWorkTree(patch_dir)
- assert not scm.GIT.IsWorkTreeDirty(patch_dir)
-
- with _PrepareDescriptionAndPatchFiles(description, options) as (
- patch_filename, description_filename):
- logging.info('Committing patch')
-
- temp_branch = 'tmp_patch'
- target_ref = 'refs/patches/%s/%s' % (
- Escape(options.user),
- os.path.basename(patch_filename).replace(' ','_'))
- target_filename = os.path.join(patch_dir, 'patch.diff')
- branch_file = os.path.join(patch_dir, GIT_BRANCH_FILE)
-
- patch_git('checkout', 'master')
- try:
- # Try deleting an existing temp branch, if any.
- try:
- patch_git('branch', '-D', temp_branch)
- logging.debug('Deleted an existing temp branch.')
- except subprocess2.CalledProcessError:
- pass
- # Create a new branch and put the patch there.
- patch_git('checkout', '--orphan', temp_branch)
- patch_git('reset')
- patch_git('clean', '-f')
- shutil.copyfile(patch_filename, target_filename)
- add_and_commit(target_filename, description_filename)
- assert not scm.GIT.IsWorkTreeDirty(patch_dir)
-
- # Create a ref and point it to the commit referenced by temp_branch.
- patch_git('update-ref', target_ref, temp_branch)
-
- # Delete the temp ref.
- patch_git('checkout', 'master')
- patch_git('branch', '-D', temp_branch)
-
- # Update the branch file in the master.
- def update_branch():
- with open(branch_file, 'w') as f:
- f.write(target_ref)
- add_and_commit(branch_file, description_filename)
-
- update_branch()
-
- # Push master and target_ref to origin.
- logging.info('Pushing patch')
- for attempt in xrange(_GIT_PUSH_ATTEMPTS):
- try:
- patch_git('push', 'origin', 'master', target_ref)
- except subprocess2.CalledProcessError as e:
- is_last = attempt == _GIT_PUSH_ATTEMPTS - 1
- if is_last:
- raise NoTryServerAccess(str(e))
- # Fetch, reset, update branch file again.
- patch_git('fetch', 'origin')
- patch_git('reset', '--hard', 'origin/master')
- update_branch()
- except subprocess2.CalledProcessError, e:
- # Restore state.
- patch_git('checkout', 'master')
- patch_git('reset', '--hard', 'origin/master')
- raise
-
- PrintSuccess(bot_spec, options)
-
-def _SendChangeGerrit(bot_spec, options):
- """Posts a try job to a Gerrit change.
-
- Reads Change-Id from the HEAD commit, resolves the current revision, checks
- that local revision matches the uploaded one, posts a try job in form of a
- message, sets Tryjob-Request label to 1.
-
- Gerrit message format: starts with !tryjob, optionally followed by a tryjob
- definition in JSON format:
- buildNames: list of strings specifying build names.
- """
-
- logging.info('Sending by Gerrit')
- if not options.gerrit_url:
- raise NoTryServerAccess('Please use --gerrit_url option to specify the '
- 'Gerrit instance url to connect to')
- gerrit_host = urlparse.urlparse(options.gerrit_url).hostname
- logging.debug('Gerrit host: %s' % gerrit_host)
-
- def GetChangeId(commmitish):
- """Finds Change-ID of the HEAD commit."""
- CHANGE_ID_RGX = '^Change-Id: (I[a-f0-9]{10,})'
- comment = scm.GIT.Capture(['log', '-1', commmitish, '--format=%b'],
- cwd=os.getcwd())
- change_id_match = re.search(CHANGE_ID_RGX, comment, re.I | re.M)
- if not change_id_match:
- raise Error('Change-Id was not found in the HEAD commit. Make sure you '
- 'have a Git hook installed that generates and inserts a '
- 'Change-Id into a commit message automatically.')
- change_id = change_id_match.group(1)
- return change_id
-
- def FormatMessage():
- # Build job definition.
- job_def = {}
- builderNames = [builder for builder, _ in bot_spec]
- if builderNames:
- job_def['builderNames'] = builderNames
-
- # Format message.
- msg = '!tryjob'
- if job_def:
- msg = '%s %s' % (msg, json.dumps(job_def, sort_keys=True))
- return msg
-
- def PostTryjob(message):
- logging.info('Posting gerrit message: %s' % message)
- if not options.dry_run:
- # Post a message and set TryJob=1 label.
- try:
- gerrit_util.SetReview(gerrit_host, change_id, msg=message,
- labels={'Tryjob-Request': 1})
- except gerrit_util.GerritError, e:
- if e.http_status == 400:
- raise Error(e.message)
- else:
- raise
-
- head_sha = scm.GIT.Capture(['log', '-1', '--format=%H'], cwd=os.getcwd())
-
- change_id = GetChangeId(head_sha)
-
- try:
- # Check that the uploaded revision matches the local one.
- changes = gerrit_util.GetChangeCurrentRevision(gerrit_host, change_id)
- except gerrit_util.GerritAuthenticationError, e:
- raise NoTryServerAccess(e.message)
-
- assert len(changes) <= 1, 'Multiple changes with id %s' % change_id
- if not changes:
- raise Error('A change %s was not found on the server. Was it uploaded?' %
- change_id)
- logging.debug('Found Gerrit change: %s' % changes[0])
- if changes[0]['current_revision'] != head_sha:
- raise Error('Please upload your latest local changes to Gerrit.')
-
- # Post a try job.
- message = FormatMessage()
- PostTryjob(message)
- change_url = urlparse.urljoin(options.gerrit_url,
- '/#/c/%s' % changes[0]['_number'])
- print('A tryjob was posted on change %s' % change_url)
-
def PrintSuccess(bot_spec, options):
if not options.dry_run:
text = 'Patch \'%s\' sent to try server' % options.name
@@ -809,25 +518,15 @@ def PrintSuccess(bot_spec, options):
print(text)
-def GuessVCS(options, path, file_list):
- """Helper to guess the version control system.
-
- NOTE: Very similar to upload.GuessVCS. Doesn't look for hg since we don't
- support it yet.
-
- This examines the path directory, guesses which SCM we're using, and
- returns an instance of the appropriate class. Exit with an error if we can't
- figure it out.
+def DetectGit(options, path, file_list):
+ """Instantiates the GIT class if a git repo is detected.
Returns:
- A SCM instance. Exits if the SCM can't be guessed.
+ A GIT instance. None if repo not found.
"""
__pychecker__ = 'no-returnvalues'
real_path = path.split('@')[0]
- logging.info("GuessVCS(%s)" % path)
- # Subversion has a .svn in all working directories.
- if os.path.isdir(os.path.join(real_path, '.svn')):
- return SVN(options, path, file_list)
+ logging.info("DetectGit(%s)" % path)
# Git has a command to test if you're in a git tree.
# Try running it, but don't die if we don't have git installed.
@@ -845,9 +544,7 @@ def GuessVCS(options, path, file_list):
# 128 = git error code when not in a repo.
logging.warning('Unexpected error code: %s' % e.returncode)
raise
- raise NoTryServerAccess(
- ( 'Could not guess version control system for %s.\n'
- 'Are you in a working copy directory?') % path)
+ return None
def GetMungedDiff(path_diff, diff):
@@ -978,59 +675,17 @@ def gen_parser(prog):
"main checkout")
parser.add_option_group(group)
- group = optparse.OptionGroup(parser, "Access the try server by HTTP")
- group.add_option("--use_http",
- action="store_const",
- const=_SendChangeHTTP,
- dest="send_patch",
- help="Use HTTP to talk to the try server [default]")
- group.add_option("-H", "--host",
- help="Host address")
- group.add_option("-P", "--port", type="int",
- help="HTTP port")
- parser.add_option_group(group)
-
group = optparse.OptionGroup(parser, "Access the try server with SVN")
- group.add_option("--use_svn",
- action="store_const",
- const=_SendChangeSVN,
- dest="send_patch",
- help="Use SVN to talk to the try server")
group.add_option("-S", "--svn_repo",
metavar="SVN_URL",
help="SVN url to use to write the changes in; --use_svn is "
"implied when using --svn_repo")
parser.add_option_group(group)
- group = optparse.OptionGroup(parser, "Access the try server with Git")
- group.add_option("--use_git",
- action="store_const",
- const=_SendChangeGit,
- dest="send_patch",
- help="Use GIT to talk to the try server")
- group.add_option("-G", "--git_repo",
- metavar="GIT_URL",
- help="GIT url to use to write the changes in; --use_git is "
- "implied when using --git_repo")
- parser.add_option_group(group)
-
- group = optparse.OptionGroup(parser, "Access the try server with Gerrit")
- group.add_option("--use_gerrit",
- action="store_const",
- const=_SendChangeGerrit,
- dest="send_patch",
- help="Use Gerrit to talk to the try server")
- group.add_option("--gerrit_url",
- metavar="GERRIT_URL",
- help="Gerrit url to post a tryjob to; --use_gerrit is "
- "implied when using --gerrit_url")
- parser.add_option_group(group)
-
return parser
-def TryChange(argv,
- change,
+def TryChange_Git(argv,
swallow_exception,
prog=None,
extra_epilog=None):
@@ -1099,7 +754,10 @@ def TryChange(argv,
file_list = []
if options.files:
file_list = options.files
- elif change:
+ elif (not options.email or not options.diff or not options.revision
+ or options.revision.lower() == 'auto'):
+ changelist = git_cl.Changelist()
+ change = changelist.GetChange(changelist.GetUpstreamBranch(), None)
file_list = [f.LocalPath() for f in change.AffectedFiles()]
if options.upstream_branch:
@@ -1108,43 +766,32 @@ def TryChange(argv,
# upstream branch.
file_list = []
- current_vcs = GuessVCS(options, path, file_list)
- current_vcs.AutomagicalSettings()
- options = current_vcs.options
- vcs_is_git = type(current_vcs) is GIT
-
- # So far, git_repo doesn't work with SVN
- if options.git_repo and not vcs_is_git:
- parser.error('--git_repo option is supported only for GIT repositories')
+ detected_git = DetectGit(options, path, file_list)
+ if detected_git:
+ detected_git.AutomagicalSettings()
+ options = detected_git.options
# If revision==auto, resolve it
if options.revision and options.revision.lower() == 'auto':
- if not vcs_is_git:
- parser.error('--revision=auto is supported only for GIT repositories')
options.revision = scm.GIT.Capture(
- ['rev-parse', current_vcs.diff_against],
+ ['rev-parse', detected_git.diff_against],
cwd=path)
- checkouts = [current_vcs]
+ checkouts = []
+ if detected_git:
+ checkouts.append(detected_git)
for item in options.sub_rep:
# Pass file_list=None because we don't know the sub repo's file list.
- checkout = GuessVCS(options,
- os.path.join(current_vcs.checkout_root, item),
+ checkout = DetectGit(options,
+ os.path.join(detected_git.checkout_root, item),
None)
if checkout.checkout_root in [c.checkout_root for c in checkouts]:
parser.error('Specified the root %s two times.' %
checkout.checkout_root)
checkouts.append(checkout)
- can_http = options.port and options.host
- can_svn = options.svn_repo
- can_git = options.git_repo
- can_gerrit = options.gerrit_url
- can_something = can_http or can_svn or can_git or can_gerrit
- # If there was no transport selected yet, now we must have enough data to
- # select one.
- if not options.send_patch and not can_something:
- parser.error('Please specify an access method.')
+ if not options.svn_repo:
+ parser.error('A SVN repo is required to send the job.')
# Convert options.diff into the content of the diff.
if options.url:
@@ -1167,7 +814,7 @@ def TryChange(argv,
diff = GetMungedDiff('', urllib2.urlopen(diff_url).readlines())
options.diff = ''.join(diff[0])
changed_files = diff[1]
- else:
+ elif checkouts:
# Use this as the base.
root = checkouts[0].checkout_root
diffs = []
@@ -1183,6 +830,8 @@ def TryChange(argv,
logging.error('Empty or non-existant diff, exiting.')
return 1
options.diff = ''.join(diffs)
+ else:
+ raise RuntimeError('Nothing to send')
if not options.name:
if options.issue:
@@ -1221,30 +870,7 @@ def TryChange(argv,
print ' %s' % (bot[0])
return 0
- # Determine sending protocol
- if options.send_patch:
- # If forced.
- senders = [options.send_patch]
- else:
- # Try sending patch using avaialble protocols
- all_senders = [
- (_SendChangeHTTP, can_http),
- (_SendChangeSVN, can_svn),
- (_SendChangeGerrit, can_gerrit),
- (_SendChangeGit, can_git),
- ]
- senders = [sender for sender, can in all_senders if can]
-
- # Send the patch.
- for sender in senders:
- try:
- sender(bot_spec, options)
- return 0
- except NoTryServerAccess:
- is_last = sender == senders[-1]
- if is_last:
- raise
- assert False, "Unreachable code"
+ _SendChangeSVN(bot_spec, options)
except Error, e:
if swallow_exception:
return 1
@@ -1254,8 +880,3 @@ def TryChange(argv,
print >> sys.stderr, e
return 1
return 0
-
-
-if __name__ == "__main__":
- fix_encoding.fix_encoding()
- sys.exit(TryChange(None, None, False))
« no previous file with comments | « tests/trychange_git_unittest.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698