mirror of
https://chromium.googlesource.com/chromium/tools/depot_tools.git
synced 2026-01-11 18:51:29 +00:00
Group SCM-specific functions in classes to simplify generalization of the interface.
TEST=none BUG=none Move scm functions into a class to make it simpler to manage. Review URL: http://codereview.chromium.org/391052 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@32057 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
57
gcl.py
57
gcl.py
@@ -19,10 +19,10 @@ import upload
|
||||
import urllib2
|
||||
|
||||
# gcl now depends on gclient.
|
||||
import gclient_scm
|
||||
from scm import SVN
|
||||
import gclient_utils
|
||||
|
||||
__version__ = '1.1.1'
|
||||
__version__ = '1.1.2'
|
||||
|
||||
|
||||
CODEREVIEW_SETTINGS = {
|
||||
@@ -46,43 +46,13 @@ MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!"
|
||||
FILES_CACHE = {}
|
||||
|
||||
|
||||
### SVN Functions
|
||||
|
||||
def IsSVNMoved(filename):
|
||||
"""Determine if a file has been added through svn mv"""
|
||||
info = gclient_scm.CaptureSVNInfo(filename)
|
||||
return (info.get('Copied From URL') and
|
||||
info.get('Copied From Rev') and
|
||||
info.get('Schedule') == 'add')
|
||||
|
||||
|
||||
def GetSVNFileProperty(file, property_name):
|
||||
"""Returns the value of an SVN property for the given file.
|
||||
|
||||
Args:
|
||||
file: The file to check
|
||||
property_name: The name of the SVN property, e.g. "svn:mime-type"
|
||||
|
||||
Returns:
|
||||
The value of the property, which will be the empty string if the property
|
||||
is not set on the file. If the file is not under version control, the
|
||||
empty string is also returned.
|
||||
"""
|
||||
output = RunShell(["svn", "propget", property_name, file])
|
||||
if (output.startswith("svn: ") and
|
||||
output.endswith("is not under version control")):
|
||||
return ""
|
||||
else:
|
||||
return output
|
||||
|
||||
|
||||
def UnknownFiles(extra_args):
|
||||
"""Runs svn status and prints unknown files.
|
||||
|
||||
Any args in |extra_args| are passed to the tool to support giving alternate
|
||||
code locations.
|
||||
"""
|
||||
return [item[1] for item in gclient_scm.CaptureSVNStatus(extra_args)
|
||||
return [item[1] for item in SVN.CaptureStatus(extra_args)
|
||||
if item[0][0] == '?']
|
||||
|
||||
|
||||
@@ -93,7 +63,7 @@ def GetRepositoryRoot():
|
||||
"""
|
||||
global REPOSITORY_ROOT
|
||||
if not REPOSITORY_ROOT:
|
||||
infos = gclient_scm.CaptureSVNInfo(os.getcwd(), print_error=False)
|
||||
infos = SVN.CaptureInfo(os.getcwd(), print_error=False)
|
||||
cur_dir_repo_root = infos.get("Repository Root")
|
||||
if not cur_dir_repo_root:
|
||||
raise gclient_utils.Error("gcl run outside of repository")
|
||||
@@ -101,7 +71,7 @@ def GetRepositoryRoot():
|
||||
REPOSITORY_ROOT = os.getcwd()
|
||||
while True:
|
||||
parent = os.path.dirname(REPOSITORY_ROOT)
|
||||
if (gclient_scm.CaptureSVNInfo(parent, print_error=False).get(
|
||||
if (SVN.CaptureInfo(parent, print_error=False).get(
|
||||
"Repository Root") != cur_dir_repo_root):
|
||||
break
|
||||
REPOSITORY_ROOT = parent
|
||||
@@ -146,7 +116,7 @@ def GetCachedFile(filename, max_age=60*60*24*3, use_root=False):
|
||||
os.stat(cached_file).st_mtime > max_age):
|
||||
local_dir = os.path.dirname(os.path.abspath(filename))
|
||||
local_base = os.path.basename(filename)
|
||||
dir_info = gclient_scm.CaptureSVNInfo(".")
|
||||
dir_info = SVN.CaptureInfo(".")
|
||||
repo_root = dir_info["Repository Root"]
|
||||
if use_root:
|
||||
url_path = repo_root
|
||||
@@ -158,7 +128,7 @@ def GetCachedFile(filename, max_age=60*60*24*3, use_root=False):
|
||||
r = ""
|
||||
if not use_root:
|
||||
local_path = os.path.join(local_dir, local_base)
|
||||
r = gclient_scm.CaptureSVNStatus((local_path,))
|
||||
r = SVN.CaptureStatus((local_path,))
|
||||
rc = -1
|
||||
if r:
|
||||
status = r[0][0]
|
||||
@@ -478,7 +448,7 @@ class ChangeInfo(object):
|
||||
if update_status:
|
||||
for item in files:
|
||||
filename = os.path.join(local_root, item[1])
|
||||
status_result = gclient_scm.CaptureSVNStatus(filename)
|
||||
status_result = SVN.CaptureStatus(filename)
|
||||
if not status_result or not status_result[0][0]:
|
||||
# File has been reverted.
|
||||
save = True
|
||||
@@ -562,7 +532,7 @@ def GetModifiedFiles():
|
||||
files_in_cl[filename] = change_info.name
|
||||
|
||||
# Get all the modified files.
|
||||
status_result = gclient_scm.CaptureSVNStatus(None)
|
||||
status_result = SVN.CaptureStatus(None)
|
||||
for line in status_result:
|
||||
status = line[0]
|
||||
filename = line[1]
|
||||
@@ -751,8 +721,7 @@ def GenerateDiff(files, root=None):
|
||||
for filename in files:
|
||||
# Use svn info output instead of os.path.isdir because the latter fails
|
||||
# when the file is deleted.
|
||||
if gclient_scm.CaptureSVNInfo(filename).get("Node Kind") in ("dir",
|
||||
"directory"):
|
||||
if SVN.CaptureInfo(file).get('Node Kind') == 'directory':
|
||||
continue
|
||||
# If the user specified a custom diff command in their svn config file,
|
||||
# then it'll be used when we do svn diff, which we don't want to happen
|
||||
@@ -770,7 +739,7 @@ def GenerateDiff(files, root=None):
|
||||
output = RunShell(["svn", "diff", "--config-dir", bogus_dir, filename])
|
||||
if output:
|
||||
diff.append(output)
|
||||
elif IsSVNMoved(filename):
|
||||
elif SVN.IsMoved(filename):
|
||||
# svn diff on a mv/cp'd file outputs nothing.
|
||||
# We put in an empty Index entry so upload.py knows about them.
|
||||
diff.append("\nIndex: %s\n" % filename)
|
||||
@@ -996,7 +965,7 @@ def Change(change_info, args):
|
||||
silent = FilterFlag(args, "--silent")
|
||||
|
||||
# Verify the user is running the change command from a read-write checkout.
|
||||
svn_info = gclient_scm.CaptureSVNInfo('.')
|
||||
svn_info = SVN.CaptureInfo('.')
|
||||
if not svn_info:
|
||||
ErrorExit("Current checkout is unversioned. Please retry with a versioned "
|
||||
"directory.")
|
||||
@@ -1008,7 +977,7 @@ def Change(change_info, args):
|
||||
f.close()
|
||||
else:
|
||||
override_description = None
|
||||
|
||||
|
||||
if change_info.issue:
|
||||
try:
|
||||
description = GetIssueDescription(change_info.issue)
|
||||
|
||||
@@ -66,7 +66,7 @@ Hooks
|
||||
"""
|
||||
|
||||
__author__ = "darinf@gmail.com (Darin Fisher)"
|
||||
__version__ = "0.3.3"
|
||||
__version__ = "0.3.4"
|
||||
|
||||
import errno
|
||||
import logging
|
||||
@@ -747,9 +747,9 @@ class GClient(object):
|
||||
# Use entry and not entry_fixed there.
|
||||
if entry not in entries and os.path.exists(e_dir):
|
||||
modified_files = False
|
||||
if isinstance(prev_entries,list):
|
||||
if isinstance(prev_entries, list):
|
||||
# old .gclient_entries format was list, now dict
|
||||
modified_files = gclient_scm.CaptureSVNStatus(e_dir)
|
||||
modified_files = gclient_scm.scm.SVN.CaptureStatus(e_dir)
|
||||
else:
|
||||
file_list = []
|
||||
scm = gclient_scm.CreateSCM(prev_entries[entry], self._root_dir,
|
||||
@@ -830,7 +830,7 @@ class GClient(object):
|
||||
(url, rev) = GetURLAndRev(name, solution["url"])
|
||||
entries[name] = "%s@%s" % (url, rev)
|
||||
# TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset)
|
||||
entries_deps_content[name] = gclient_scm.CaptureSVN(
|
||||
entries_deps_content[name] = gclient_scm.scm.SVN.Capture(
|
||||
["cat",
|
||||
"%s/%s@%s" % (url,
|
||||
self._options.deps_file,
|
||||
|
||||
109
gclient_scm.py
109
gclient_scm.py
@@ -1,16 +1,6 @@
|
||||
# Copyright 2009 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""Gclient-specific SCM-specific operations."""
|
||||
|
||||
@@ -18,19 +8,13 @@ import logging
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import xml.dom.minidom
|
||||
|
||||
import scm
|
||||
import gclient_utils
|
||||
# TODO(maruel): Temporary.
|
||||
from scm import CaptureGit, CaptureGitStatus, CaptureSVN
|
||||
from scm import CaptureSVNHeadRevision, CaptureSVNInfo, CaptureSVNStatus
|
||||
from scm import RunSVN, RunSVNAndFilterOutput, RunSVNAndGetFileList
|
||||
|
||||
|
||||
### SCM abstraction layer
|
||||
|
||||
|
||||
# Factory Method for SCM wrapper creation
|
||||
|
||||
def CreateSCM(url=None, root_dir=None, relpath=None, scm_name='svn'):
|
||||
@@ -93,20 +77,20 @@ class SCMWrapper(object):
|
||||
return getattr(self, command)(options, args, file_list)
|
||||
|
||||
|
||||
class GitWrapper(SCMWrapper):
|
||||
class GitWrapper(SCMWrapper, scm.GIT):
|
||||
"""Wrapper for Git"""
|
||||
|
||||
def cleanup(self, options, args, file_list):
|
||||
"""Cleanup working copy."""
|
||||
__pychecker__ = 'unusednames=args,file_list,options'
|
||||
self._RunGit(['prune'], redirect_stdout=False)
|
||||
self._RunGit(['fsck'], redirect_stdout=False)
|
||||
self._RunGit(['gc'], redirect_stdout=False)
|
||||
self._Run(['prune'], redirect_stdout=False)
|
||||
self._Run(['fsck'], redirect_stdout=False)
|
||||
self._Run(['gc'], redirect_stdout=False)
|
||||
|
||||
def diff(self, options, args, file_list):
|
||||
__pychecker__ = 'unusednames=args,file_list,options'
|
||||
merge_base = self._RunGit(['merge-base', 'HEAD', 'origin'])
|
||||
self._RunGit(['diff', merge_base], redirect_stdout=False)
|
||||
merge_base = self._Run(['merge-base', 'HEAD', 'origin'])
|
||||
self._Run(['diff', merge_base], redirect_stdout=False)
|
||||
|
||||
def export(self, options, args, file_list):
|
||||
__pychecker__ = 'unusednames=file_list,options'
|
||||
@@ -114,8 +98,8 @@ class GitWrapper(SCMWrapper):
|
||||
export_path = os.path.abspath(os.path.join(args[0], self.relpath))
|
||||
if not os.path.exists(export_path):
|
||||
os.makedirs(export_path)
|
||||
self._RunGit(['checkout-index', '-a', '--prefix=%s/' % export_path],
|
||||
redirect_stdout=False)
|
||||
self._Run(['checkout-index', '-a', '--prefix=%s/' % export_path],
|
||||
redirect_stdout=False)
|
||||
|
||||
def update(self, options, args, file_list):
|
||||
"""Runs git to update or transparently checkout the working copy.
|
||||
@@ -142,21 +126,21 @@ class GitWrapper(SCMWrapper):
|
||||
print("\n_____ %s%s" % (self.relpath, rev_str))
|
||||
|
||||
if not os.path.exists(self.checkout_path):
|
||||
self._RunGit(['clone', url, self.checkout_path],
|
||||
cwd=self._root_dir, redirect_stdout=False)
|
||||
self._Run(['clone', url, self.checkout_path],
|
||||
cwd=self._root_dir, redirect_stdout=False)
|
||||
if revision:
|
||||
self._RunGit(['reset', '--hard', revision], redirect_stdout=False)
|
||||
files = self._RunGit(['ls-files']).split()
|
||||
self._Run(['reset', '--hard', revision], redirect_stdout=False)
|
||||
files = self._Run(['ls-files']).split()
|
||||
file_list.extend([os.path.join(self.checkout_path, f) for f in files])
|
||||
return
|
||||
|
||||
self._RunGit(['remote', 'update'], redirect_stdout=False)
|
||||
self._Run(['remote', 'update'], redirect_stdout=False)
|
||||
new_base = 'origin'
|
||||
if revision:
|
||||
new_base = revision
|
||||
files = self._RunGit(['diff', new_base, '--name-only']).split()
|
||||
files = self._Run(['diff', new_base, '--name-only']).split()
|
||||
file_list.extend([os.path.join(self.checkout_path, f) for f in files])
|
||||
self._RunGit(['rebase', '-v', new_base], redirect_stdout=False)
|
||||
self._Run(['rebase', '-v', new_base], redirect_stdout=False)
|
||||
print "Checked out revision %s." % self.revinfo(options, (), None)
|
||||
|
||||
def revert(self, options, args, file_list):
|
||||
@@ -172,15 +156,15 @@ class GitWrapper(SCMWrapper):
|
||||
print("\n_____ %s is missing, synching instead" % self.relpath)
|
||||
# Don't reuse the args.
|
||||
return self.update(options, [], file_list)
|
||||
merge_base = self._RunGit(['merge-base', 'HEAD', 'origin'])
|
||||
files = self._RunGit(['diff', merge_base, '--name-only']).split()
|
||||
self._RunGit(['reset', '--hard', merge_base], redirect_stdout=False)
|
||||
merge_base = self._Run(['merge-base', 'HEAD', 'origin'])
|
||||
files = self._Run(['diff', merge_base, '--name-only']).split()
|
||||
self._Run(['reset', '--hard', merge_base], redirect_stdout=False)
|
||||
file_list.extend([os.path.join(self.checkout_path, f) for f in files])
|
||||
|
||||
def revinfo(self, options, args, file_list):
|
||||
"""Display revision"""
|
||||
__pychecker__ = 'unusednames=args,file_list,options'
|
||||
return self._RunGit(['rev-parse', 'HEAD'])
|
||||
return self._Run(['rev-parse', 'HEAD'])
|
||||
|
||||
def runhooks(self, options, args, file_list):
|
||||
self.status(options, args, file_list)
|
||||
@@ -192,29 +176,30 @@ class GitWrapper(SCMWrapper):
|
||||
print('\n________ couldn\'t run status in %s:\nThe directory '
|
||||
'does not exist.' % self.checkout_path)
|
||||
else:
|
||||
merge_base = self._RunGit(['merge-base', 'HEAD', 'origin'])
|
||||
self._RunGit(['diff', '--name-status', merge_base], redirect_stdout=False)
|
||||
files = self._RunGit(['diff', '--name-only', merge_base]).split()
|
||||
merge_base = self._Run(['merge-base', 'HEAD', 'origin'])
|
||||
self._Run(['diff', '--name-status', merge_base], redirect_stdout=False)
|
||||
files = self._Run(['diff', '--name-only', merge_base]).split()
|
||||
file_list.extend([os.path.join(self.checkout_path, f) for f in files])
|
||||
|
||||
def _RunGit(self, args, cwd=None, checkrc=True, redirect_stdout=True):
|
||||
def _Run(self, args, cwd=None, checkrc=True, redirect_stdout=True):
|
||||
# TODO(maruel): Merge with Capture?
|
||||
stdout=None
|
||||
if redirect_stdout:
|
||||
stdout=subprocess.PIPE
|
||||
if cwd == None:
|
||||
cwd = self.checkout_path
|
||||
cmd = ['git']
|
||||
cmd = [self.COMMAND]
|
||||
cmd.extend(args)
|
||||
sp = subprocess.Popen(cmd, cwd=cwd, stdout=stdout)
|
||||
if checkrc and sp.returncode:
|
||||
raise gclient_utils.Error('git command %s returned %d' %
|
||||
(args[0], sp.returncode))
|
||||
output = sp.communicate()[0]
|
||||
if output != None:
|
||||
if output is not None:
|
||||
return output.strip()
|
||||
|
||||
|
||||
class SVNWrapper(SCMWrapper):
|
||||
class SVNWrapper(SCMWrapper, scm.SVN):
|
||||
""" Wrapper for SVN """
|
||||
|
||||
def cleanup(self, options, args, file_list):
|
||||
@@ -222,14 +207,14 @@ class SVNWrapper(SCMWrapper):
|
||||
__pychecker__ = 'unusednames=file_list,options'
|
||||
command = ['cleanup']
|
||||
command.extend(args)
|
||||
RunSVN(command, os.path.join(self._root_dir, self.relpath))
|
||||
self.Run(command, os.path.join(self._root_dir, self.relpath))
|
||||
|
||||
def diff(self, options, args, file_list):
|
||||
# NOTE: This function does not currently modify file_list.
|
||||
__pychecker__ = 'unusednames=file_list,options'
|
||||
command = ['diff']
|
||||
command.extend(args)
|
||||
RunSVN(command, os.path.join(self._root_dir, self.relpath))
|
||||
self.Run(command, os.path.join(self._root_dir, self.relpath))
|
||||
|
||||
def export(self, options, args, file_list):
|
||||
__pychecker__ = 'unusednames=file_list,options'
|
||||
@@ -242,7 +227,7 @@ class SVNWrapper(SCMWrapper):
|
||||
assert os.path.exists(export_path)
|
||||
command = ['export', '--force', '.']
|
||||
command.append(export_path)
|
||||
RunSVN(command, os.path.join(self._root_dir, self.relpath))
|
||||
self.Run(command, os.path.join(self._root_dir, self.relpath))
|
||||
|
||||
def update(self, options, args, file_list):
|
||||
"""Runs SCM to update or transparently checkout the working copy.
|
||||
@@ -279,11 +264,11 @@ class SVNWrapper(SCMWrapper):
|
||||
command = ['checkout', url, checkout_path]
|
||||
if revision:
|
||||
command.extend(['--revision', str(revision)])
|
||||
RunSVNAndGetFileList(options, command, self._root_dir, file_list)
|
||||
self.RunAndGetFileList(options, command, self._root_dir, file_list)
|
||||
return
|
||||
|
||||
# Get the existing scm url and the revision number of the current checkout.
|
||||
from_info = CaptureSVNInfo(os.path.join(checkout_path, '.'), '.')
|
||||
from_info = self.CaptureInfo(os.path.join(checkout_path, '.'), '.')
|
||||
if not from_info:
|
||||
raise gclient_utils.Error("Can't update/checkout %r if an unversioned "
|
||||
"directory is present. Delete the directory "
|
||||
@@ -293,12 +278,12 @@ class SVNWrapper(SCMWrapper):
|
||||
if options.manually_grab_svn_rev:
|
||||
# Retrieve the current HEAD version because svn is slow at null updates.
|
||||
if not revision:
|
||||
from_info_live = CaptureSVNInfo(from_info['URL'], '.')
|
||||
from_info_live = self.CaptureInfo(from_info['URL'], '.')
|
||||
revision = str(from_info_live['Revision'])
|
||||
rev_str = ' at %s' % revision
|
||||
|
||||
if from_info['URL'] != base_url:
|
||||
to_info = CaptureSVNInfo(url, '.')
|
||||
to_info = self.CaptureInfo(url, '.')
|
||||
if not to_info.get('Repository Root') or not to_info.get('UUID'):
|
||||
# The url is invalid or the server is not accessible, it's safer to bail
|
||||
# out right now.
|
||||
@@ -320,12 +305,12 @@ class SVNWrapper(SCMWrapper):
|
||||
from_info['Repository Root'],
|
||||
to_info['Repository Root'],
|
||||
self.relpath]
|
||||
RunSVN(command, self._root_dir)
|
||||
self.Run(command, self._root_dir)
|
||||
from_info['URL'] = from_info['URL'].replace(
|
||||
from_info['Repository Root'],
|
||||
to_info['Repository Root'])
|
||||
else:
|
||||
if CaptureSVNStatus(checkout_path):
|
||||
if self.CaptureStatus(checkout_path):
|
||||
raise gclient_utils.Error("Can't switch the checkout to %s; UUID "
|
||||
"don't match and there is local changes "
|
||||
"in %s. Delete the directory and "
|
||||
@@ -337,7 +322,7 @@ class SVNWrapper(SCMWrapper):
|
||||
command = ['checkout', url, checkout_path]
|
||||
if revision:
|
||||
command.extend(['--revision', str(revision)])
|
||||
RunSVNAndGetFileList(options, command, self._root_dir, file_list)
|
||||
self.RunAndGetFileList(options, command, self._root_dir, file_list)
|
||||
return
|
||||
|
||||
|
||||
@@ -351,7 +336,7 @@ class SVNWrapper(SCMWrapper):
|
||||
command = ["update", checkout_path]
|
||||
if revision:
|
||||
command.extend(['--revision', str(revision)])
|
||||
RunSVNAndGetFileList(options, command, self._root_dir, file_list)
|
||||
self.RunAndGetFileList(options, command, self._root_dir, file_list)
|
||||
|
||||
def revert(self, options, args, file_list):
|
||||
"""Reverts local modifications. Subversion specific.
|
||||
@@ -368,7 +353,7 @@ class SVNWrapper(SCMWrapper):
|
||||
# Don't reuse the args.
|
||||
return self.update(options, [], file_list)
|
||||
|
||||
for file_status in CaptureSVNStatus(path):
|
||||
for file_status in self.CaptureStatus(path):
|
||||
file_path = os.path.join(path, file_status[1])
|
||||
if file_status[0][0] == 'X':
|
||||
# Ignore externals.
|
||||
@@ -403,7 +388,7 @@ class SVNWrapper(SCMWrapper):
|
||||
try:
|
||||
# svn revert is so broken we don't even use it. Using
|
||||
# "svn up --revision BASE" achieve the same effect.
|
||||
RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'], path,
|
||||
self.RunAndGetFileList(options, ['update', '--revision', 'BASE'], path,
|
||||
file_list)
|
||||
except OSError, e:
|
||||
# Maybe the directory disapeared meanwhile. We don't want it to throw an
|
||||
@@ -413,7 +398,7 @@ class SVNWrapper(SCMWrapper):
|
||||
def revinfo(self, options, args, file_list):
|
||||
"""Display revision"""
|
||||
__pychecker__ = 'unusednames=args,file_list,options'
|
||||
return CaptureSVNHeadRevision(self.url)
|
||||
return self.CaptureHeadRevision(self.url)
|
||||
|
||||
def runhooks(self, options, args, file_list):
|
||||
self.status(options, args, file_list)
|
||||
@@ -430,7 +415,7 @@ class SVNWrapper(SCMWrapper):
|
||||
% (' '.join(command), path))
|
||||
# There's no file list to retrieve.
|
||||
else:
|
||||
RunSVNAndGetFileList(options, command, path, file_list)
|
||||
self.RunAndGetFileList(options, command, path, file_list)
|
||||
|
||||
def pack(self, options, args, file_list):
|
||||
"""Generates a patch file which can be applied to the root of the
|
||||
@@ -475,4 +460,4 @@ class SVNWrapper(SCMWrapper):
|
||||
print line
|
||||
|
||||
filterer = DiffFilterer(self.relpath)
|
||||
RunSVNAndFilterOutput(command, path, False, False, filterer.Filter)
|
||||
self.RunAndFilterOutput(command, path, False, False, filterer.Filter)
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Generic utils."""
|
||||
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
@@ -22,8 +24,6 @@ import time
|
||||
import xml.dom.minidom
|
||||
import xml.parsers.expat
|
||||
|
||||
## Generic utils
|
||||
|
||||
|
||||
def SplitUrlRevision(url):
|
||||
"""Splits url and returns a two-tuple: url, rev"""
|
||||
@@ -76,9 +76,9 @@ class PrintableObject(object):
|
||||
return output
|
||||
|
||||
|
||||
def FileRead(filename):
|
||||
def FileRead(filename, mode='rU'):
|
||||
content = None
|
||||
f = open(filename, "rU")
|
||||
f = open(filename, mode)
|
||||
try:
|
||||
content = f.read()
|
||||
finally:
|
||||
@@ -86,8 +86,8 @@ def FileRead(filename):
|
||||
return content
|
||||
|
||||
|
||||
def FileWrite(filename, content):
|
||||
f = open(filename, "w")
|
||||
def FileWrite(filename, content, mode='w'):
|
||||
f = open(filename, mode)
|
||||
try:
|
||||
f.write(content)
|
||||
finally:
|
||||
@@ -201,9 +201,9 @@ def SubprocessCallAndFilter(command,
|
||||
only if we actually need to print something else as well, so you can
|
||||
get the context of the output. If print_messages is false and print_stdout
|
||||
is false, no output at all is generated.
|
||||
|
||||
Also, if print_stdout is true, the command's stdout is also forwarded
|
||||
to stdout.
|
||||
|
||||
Also, if print_stdout is true, the command's stdout is also forwarded
|
||||
to stdout.
|
||||
|
||||
If a filter function is specified, it is expected to take a single
|
||||
string argument, and it will be called with each line of the
|
||||
@@ -223,7 +223,7 @@ def SubprocessCallAndFilter(command,
|
||||
# executable, but shell=True makes subprocess on Linux fail when it's called
|
||||
# with a list because it only tries to execute the first item in the list.
|
||||
kid = subprocess.Popen(command, bufsize=0, cwd=in_directory,
|
||||
shell=(sys.platform == 'win32'), stdout=subprocess.PIPE,
|
||||
shell=(sys.platform == 'win32'), stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
# Also, we need to forward stdout to prevent weird re-ordering of output.
|
||||
@@ -238,7 +238,7 @@ def SubprocessCallAndFilter(command,
|
||||
if not print_messages:
|
||||
print("\n________ running \'%s\' in \'%s\'"
|
||||
% (' '.join(command), in_directory))
|
||||
print_messages = True
|
||||
print_messages = True
|
||||
sys.stdout.write(in_byte)
|
||||
if in_byte != "\n":
|
||||
in_line += in_byte
|
||||
|
||||
@@ -7,9 +7,8 @@ import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Imported from depot_tools.
|
||||
import gclient_scm
|
||||
import presubmit_support
|
||||
import scm
|
||||
|
||||
def Backquote(cmd, cwd=None):
|
||||
"""Like running `cmd` in a shell script."""
|
||||
@@ -35,8 +34,8 @@ class ChangeOptions:
|
||||
raise Exception("Could not parse log message: %s" % log)
|
||||
name = m.group(1)
|
||||
description = m.group(2)
|
||||
files = gclient_scm.CaptureGitStatus([root], upstream_branch)
|
||||
issue = Backquote(['git', 'cl', 'status', '--field=id'])
|
||||
files = scm.GIT.CaptureStatus([root], upstream_branch)
|
||||
issue = Backquote(['cl', 'status', '--field=id'])
|
||||
patchset = None
|
||||
self.change = presubmit_support.GitChange(name, description, root, files,
|
||||
issue, patchset)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"""Enables directory-specific presubmit checks to run at upload and/or commit.
|
||||
"""
|
||||
|
||||
__version__ = '1.3.3'
|
||||
__version__ = '1.3.4'
|
||||
|
||||
# TODO(joi) Add caching where appropriate/needed. The API is designed to allow
|
||||
# caching (between all different invocations of presubmit scripts for a given
|
||||
@@ -35,11 +35,10 @@ import urllib2 # Exposed through the API.
|
||||
import warnings
|
||||
|
||||
# Local imports.
|
||||
# TODO(joi) Would be cleaner to factor out utils in gcl to separate module, but
|
||||
# for now it would only be a couple of functions so hardly worth it.
|
||||
import gcl
|
||||
import gclient_scm
|
||||
import gclient_utils
|
||||
import presubmit_canned_checks
|
||||
import scm
|
||||
|
||||
|
||||
# Ask for feedback only once in program lifetime.
|
||||
@@ -241,7 +240,7 @@ class InputApi(object):
|
||||
|
||||
Remember to check for the None case and show an appropriate error!
|
||||
"""
|
||||
local_path = gclient_scm.CaptureSVNInfo(depot_path).get('Path')
|
||||
local_path = scm.SVN.CaptureInfo(depot_path).get('Path')
|
||||
if local_path:
|
||||
return local_path
|
||||
|
||||
@@ -254,7 +253,7 @@ class InputApi(object):
|
||||
Returns:
|
||||
The depot path (SVN URL) of the file if mapped, otherwise None.
|
||||
"""
|
||||
depot_path = gclient_scm.CaptureSVNInfo(local_path).get('URL')
|
||||
depot_path = scm.SVN.CaptureInfo(local_path).get('URL')
|
||||
if depot_path:
|
||||
return depot_path
|
||||
|
||||
@@ -354,7 +353,7 @@ class InputApi(object):
|
||||
file_item = file_item.AbsoluteLocalPath()
|
||||
if not file_item.startswith(self.change.RepositoryRoot()):
|
||||
raise IOError('Access outside the repository root is denied.')
|
||||
return gcl.ReadFile(file_item, mode)
|
||||
return gclient_utils.FileRead(file_item, mode)
|
||||
|
||||
@staticmethod
|
||||
def _RightHandSideLinesImpl(affected_files):
|
||||
@@ -432,7 +431,8 @@ class AffectedFile(object):
|
||||
if self.IsDirectory():
|
||||
return []
|
||||
else:
|
||||
return gcl.ReadFile(self.AbsoluteLocalPath()).splitlines()
|
||||
return gclient_utils.FileRead(self.AbsoluteLocalPath(),
|
||||
'rU').splitlines()
|
||||
|
||||
def OldContents(self):
|
||||
"""Returns an iterator over the lines in the old version of file.
|
||||
@@ -464,7 +464,7 @@ class SvnAffectedFile(AffectedFile):
|
||||
|
||||
def ServerPath(self):
|
||||
if self._server_path is None:
|
||||
self._server_path = gclient_scm.CaptureSVNInfo(
|
||||
self._server_path = scm.SVN.CaptureInfo(
|
||||
self.AbsoluteLocalPath()).get('URL', '')
|
||||
return self._server_path
|
||||
|
||||
@@ -476,13 +476,13 @@ class SvnAffectedFile(AffectedFile):
|
||||
# querying subversion, especially on Windows.
|
||||
self._is_directory = os.path.isdir(path)
|
||||
else:
|
||||
self._is_directory = gclient_scm.CaptureSVNInfo(
|
||||
self._is_directory = scm.SVN.CaptureInfo(
|
||||
path).get('Node Kind') in ('dir', 'directory')
|
||||
return self._is_directory
|
||||
|
||||
def Property(self, property_name):
|
||||
if not property_name in self._properties:
|
||||
self._properties[property_name] = gcl.GetSVNFileProperty(
|
||||
self._properties[property_name] = scm.SVN.GetFileProperty(
|
||||
self.AbsoluteLocalPath(), property_name).rstrip()
|
||||
return self._properties[property_name]
|
||||
|
||||
@@ -494,8 +494,8 @@ class SvnAffectedFile(AffectedFile):
|
||||
elif self.IsDirectory():
|
||||
self._is_text_file = False
|
||||
else:
|
||||
mime_type = gcl.GetSVNFileProperty(self.AbsoluteLocalPath(),
|
||||
'svn:mime-type')
|
||||
mime_type = scm.SVN.GetFileProperty(self.AbsoluteLocalPath(),
|
||||
'svn:mime-type')
|
||||
self._is_text_file = (not mime_type or mime_type.startswith('text/'))
|
||||
return self._is_text_file
|
||||
|
||||
@@ -809,7 +809,7 @@ def DoGetTrySlaves(changed_files,
|
||||
if verbose:
|
||||
output_stream.write("Running %s\n" % filename)
|
||||
# Accept CRLF presubmit script.
|
||||
presubmit_script = gcl.ReadFile(filename, 'rU')
|
||||
presubmit_script = gclient_utils.FileRead(filename, 'rU')
|
||||
results += executer.ExecPresubmitScript(presubmit_script)
|
||||
|
||||
slaves = list(set(results))
|
||||
@@ -925,7 +925,7 @@ def DoPresubmitChecks(change,
|
||||
if verbose:
|
||||
output_stream.write("Running %s\n" % filename)
|
||||
# Accept CRLF presubmit script.
|
||||
presubmit_script = gcl.ReadFile(filename, 'rU')
|
||||
presubmit_script = gclient_utils.FileRead(filename, 'rU')
|
||||
results += executer.ExecPresubmitScript(presubmit_script, filename)
|
||||
|
||||
errors = []
|
||||
@@ -1022,7 +1022,7 @@ def Main(argv):
|
||||
options.files = ParseFiles(args, options.recursive)
|
||||
else:
|
||||
# Grab modified files.
|
||||
options.files = gclient_scm.CaptureGitStatus([options.root])
|
||||
options.files = scm.GIT.CaptureStatus([options.root])
|
||||
elif os.path.isdir(os.path.join(options.root, '.svn')):
|
||||
change_class = SvnChange
|
||||
if not options.files:
|
||||
@@ -1030,7 +1030,7 @@ def Main(argv):
|
||||
options.files = ParseFiles(args, options.recursive)
|
||||
else:
|
||||
# Grab modified files.
|
||||
options.files = gclient_scm.CaptureSVNStatus([options.root])
|
||||
options.files = scm.SVN.CaptureStatus([options.root])
|
||||
else:
|
||||
# Doesn't seem under source control.
|
||||
change_class = Change
|
||||
|
||||
@@ -146,7 +146,7 @@ def Revert(revisions, force=False, commit=True, send_email=True, message=None,
|
||||
print ""
|
||||
|
||||
# Make sure these files are unmodified with svn status.
|
||||
status = gclient_scm.CaptureSVNStatus(files)
|
||||
status = gclient_scm.scm.SVN.CaptureStatus(files)
|
||||
if status:
|
||||
if force:
|
||||
# TODO(maruel): Use the tool to correctly revert '?' files.
|
||||
|
||||
629
scm.py
629
scm.py
@@ -2,335 +2,414 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""SCM-specific functions."""
|
||||
"""SCM-specific utility classes."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import xml.dom.minidom
|
||||
|
||||
import gclient_utils
|
||||
|
||||
|
||||
SVN_COMMAND = "svn"
|
||||
GIT_COMMAND = "git"
|
||||
class GIT(object):
|
||||
COMMAND = "git"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Git utils:
|
||||
@staticmethod
|
||||
def Capture(args, in_directory=None, print_error=True):
|
||||
"""Runs git, capturing output sent to stdout as a string.
|
||||
|
||||
Args:
|
||||
args: A sequence of command line parameters to be passed to git.
|
||||
in_directory: The directory where git is to be run.
|
||||
|
||||
Returns:
|
||||
The output sent to stdout as a string.
|
||||
"""
|
||||
c = [GIT.COMMAND]
|
||||
c.extend(args)
|
||||
|
||||
# *Sigh*: Windows needs shell=True, or else it won't search %PATH% for
|
||||
# the git.exe executable, but shell=True makes subprocess on Linux fail
|
||||
# when it's called with a list because it only tries to execute the
|
||||
# first string ("git").
|
||||
stderr = None
|
||||
if not print_error:
|
||||
stderr = subprocess.PIPE
|
||||
return subprocess.Popen(c,
|
||||
cwd=in_directory,
|
||||
shell=sys.platform.startswith('win'),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=stderr).communicate()[0]
|
||||
|
||||
|
||||
def CaptureGit(args, in_directory=None, print_error=True):
|
||||
"""Runs git, capturing output sent to stdout as a string.
|
||||
@staticmethod
|
||||
def CaptureStatus(files, upstream_branch='origin'):
|
||||
"""Returns git status.
|
||||
|
||||
Args:
|
||||
args: A sequence of command line parameters to be passed to git.
|
||||
in_directory: The directory where git is to be run.
|
||||
@files can be a string (one file) or a list of files.
|
||||
|
||||
Returns:
|
||||
The output sent to stdout as a string.
|
||||
"""
|
||||
c = [GIT_COMMAND]
|
||||
c.extend(args)
|
||||
Returns an array of (status, file) tuples."""
|
||||
command = ["diff", "--name-status", "-r", "%s.." % upstream_branch]
|
||||
if not files:
|
||||
pass
|
||||
elif isinstance(files, basestring):
|
||||
command.append(files)
|
||||
else:
|
||||
command.extend(files)
|
||||
|
||||
# *Sigh*: Windows needs shell=True, or else it won't search %PATH% for
|
||||
# the git.exe executable, but shell=True makes subprocess on Linux fail
|
||||
# when it's called with a list because it only tries to execute the
|
||||
# first string ("git").
|
||||
stderr = None
|
||||
if not print_error:
|
||||
stderr = subprocess.PIPE
|
||||
return subprocess.Popen(c,
|
||||
cwd=in_directory,
|
||||
shell=sys.platform.startswith('win'),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=stderr).communicate()[0]
|
||||
status = GIT.Capture(command).rstrip()
|
||||
results = []
|
||||
if status:
|
||||
for statusline in status.split('\n'):
|
||||
m = re.match('^(\w)\t(.+)$', statusline)
|
||||
if not m:
|
||||
raise Exception("status currently unsupported: %s" % statusline)
|
||||
results.append(('%s ' % m.group(1), m.group(2)))
|
||||
return results
|
||||
|
||||
|
||||
def CaptureGitStatus(files, upstream_branch='origin'):
|
||||
"""Returns git status.
|
||||
class SVN(object):
|
||||
COMMAND = "svn"
|
||||
|
||||
@files can be a string (one file) or a list of files.
|
||||
@staticmethod
|
||||
def Run(args, in_directory):
|
||||
"""Runs svn, sending output to stdout.
|
||||
|
||||
Returns an array of (status, file) tuples."""
|
||||
command = ["diff", "--name-status", "-r", "%s.." % upstream_branch]
|
||||
if not files:
|
||||
pass
|
||||
elif isinstance(files, basestring):
|
||||
command.append(files)
|
||||
else:
|
||||
command.extend(files)
|
||||
Args:
|
||||
args: A sequence of command line parameters to be passed to svn.
|
||||
in_directory: The directory where svn is to be run.
|
||||
|
||||
status = CaptureGit(command).rstrip()
|
||||
results = []
|
||||
if status:
|
||||
for statusline in status.split('\n'):
|
||||
m = re.match('^(\w)\t(.+)$', statusline)
|
||||
if not m:
|
||||
raise Exception("status currently unsupported: %s" % statusline)
|
||||
results.append(('%s ' % m.group(1), m.group(2)))
|
||||
return results
|
||||
Raises:
|
||||
Error: An error occurred while running the svn command.
|
||||
"""
|
||||
c = [SVN.COMMAND]
|
||||
c.extend(args)
|
||||
|
||||
gclient_utils.SubprocessCall(c, in_directory)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# SVN utils:
|
||||
@staticmethod
|
||||
def Capture(args, in_directory=None, print_error=True):
|
||||
"""Runs svn, capturing output sent to stdout as a string.
|
||||
|
||||
Args:
|
||||
args: A sequence of command line parameters to be passed to svn.
|
||||
in_directory: The directory where svn is to be run.
|
||||
|
||||
def RunSVN(args, in_directory):
|
||||
"""Runs svn, sending output to stdout.
|
||||
Returns:
|
||||
The output sent to stdout as a string.
|
||||
"""
|
||||
c = [SVN.COMMAND]
|
||||
c.extend(args)
|
||||
|
||||
Args:
|
||||
args: A sequence of command line parameters to be passed to svn.
|
||||
in_directory: The directory where svn is to be run.
|
||||
# *Sigh*: Windows needs shell=True, or else it won't search %PATH% for
|
||||
# the svn.exe executable, but shell=True makes subprocess on Linux fail
|
||||
# when it's called with a list because it only tries to execute the
|
||||
# first string ("svn").
|
||||
stderr = None
|
||||
if not print_error:
|
||||
stderr = subprocess.PIPE
|
||||
return subprocess.Popen(c,
|
||||
cwd=in_directory,
|
||||
shell=(sys.platform == 'win32'),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=stderr).communicate()[0]
|
||||
|
||||
Raises:
|
||||
Error: An error occurred while running the svn command.
|
||||
"""
|
||||
c = [SVN_COMMAND]
|
||||
c.extend(args)
|
||||
@staticmethod
|
||||
def RunAndGetFileList(options, args, in_directory, file_list):
|
||||
"""Runs svn checkout, update, or status, output to stdout.
|
||||
|
||||
gclient_utils.SubprocessCall(c, in_directory)
|
||||
The first item in args must be either "checkout", "update", or "status".
|
||||
|
||||
svn's stdout is parsed to collect a list of files checked out or updated.
|
||||
These files are appended to file_list. svn's stdout is also printed to
|
||||
sys.stdout as in Run.
|
||||
|
||||
def CaptureSVN(args, in_directory=None, print_error=True):
|
||||
"""Runs svn, capturing output sent to stdout as a string.
|
||||
Args:
|
||||
options: command line options to gclient
|
||||
args: A sequence of command line parameters to be passed to svn.
|
||||
in_directory: The directory where svn is to be run.
|
||||
|
||||
Args:
|
||||
args: A sequence of command line parameters to be passed to svn.
|
||||
in_directory: The directory where svn is to be run.
|
||||
Raises:
|
||||
Error: An error occurred while running the svn command.
|
||||
"""
|
||||
command = [SVN.COMMAND]
|
||||
command.extend(args)
|
||||
|
||||
Returns:
|
||||
The output sent to stdout as a string.
|
||||
"""
|
||||
c = [SVN_COMMAND]
|
||||
c.extend(args)
|
||||
# svn update and svn checkout use the same pattern: the first three columns
|
||||
# are for file status, property status, and lock status. This is followed
|
||||
# by two spaces, and then the path to the file.
|
||||
update_pattern = '^... (.*)$'
|
||||
|
||||
# *Sigh*: Windows needs shell=True, or else it won't search %PATH% for
|
||||
# the svn.exe executable, but shell=True makes subprocess on Linux fail
|
||||
# when it's called with a list because it only tries to execute the
|
||||
# first string ("svn").
|
||||
stderr = None
|
||||
if not print_error:
|
||||
stderr = subprocess.PIPE
|
||||
return subprocess.Popen(c,
|
||||
cwd=in_directory,
|
||||
shell=(sys.platform == 'win32'),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=stderr).communicate()[0]
|
||||
# The first three columns of svn status are the same as for svn update and
|
||||
# svn checkout. The next three columns indicate addition-with-history,
|
||||
# switch, and remote lock status. This is followed by one space, and then
|
||||
# the path to the file.
|
||||
status_pattern = '^...... (.*)$'
|
||||
|
||||
# args[0] must be a supported command. This will blow up if it's something
|
||||
# else, which is good. Note that the patterns are only effective when
|
||||
# these commands are used in their ordinary forms, the patterns are invalid
|
||||
# for "svn status --show-updates", for example.
|
||||
pattern = {
|
||||
'checkout': update_pattern,
|
||||
'status': status_pattern,
|
||||
'update': update_pattern,
|
||||
}[args[0]]
|
||||
|
||||
def RunSVNAndGetFileList(options, args, in_directory, file_list):
|
||||
"""Runs svn checkout, update, or status, output to stdout.
|
||||
compiled_pattern = re.compile(pattern)
|
||||
|
||||
The first item in args must be either "checkout", "update", or "status".
|
||||
def CaptureMatchingLines(line):
|
||||
match = compiled_pattern.search(line)
|
||||
if match:
|
||||
file_list.append(match.group(1))
|
||||
|
||||
svn's stdout is parsed to collect a list of files checked out or updated.
|
||||
These files are appended to file_list. svn's stdout is also printed to
|
||||
sys.stdout as in RunSVN.
|
||||
SVN.RunAndFilterOutput(args,
|
||||
in_directory,
|
||||
options.verbose,
|
||||
True,
|
||||
CaptureMatchingLines)
|
||||
|
||||
Args:
|
||||
options: command line options to gclient
|
||||
args: A sequence of command line parameters to be passed to svn.
|
||||
in_directory: The directory where svn is to be run.
|
||||
@staticmethod
|
||||
def RunAndFilterOutput(args,
|
||||
in_directory,
|
||||
print_messages,
|
||||
print_stdout,
|
||||
filter):
|
||||
"""Runs svn checkout, update, status, or diff, optionally outputting
|
||||
to stdout.
|
||||
|
||||
Raises:
|
||||
Error: An error occurred while running the svn command.
|
||||
"""
|
||||
command = [SVN_COMMAND]
|
||||
command.extend(args)
|
||||
The first item in args must be either "checkout", "update",
|
||||
"status", or "diff".
|
||||
|
||||
# svn update and svn checkout use the same pattern: the first three columns
|
||||
# are for file status, property status, and lock status. This is followed
|
||||
# by two spaces, and then the path to the file.
|
||||
update_pattern = '^... (.*)$'
|
||||
svn's stdout is passed line-by-line to the given filter function. If
|
||||
print_stdout is true, it is also printed to sys.stdout as in Run.
|
||||
|
||||
# The first three columns of svn status are the same as for svn update and
|
||||
# svn checkout. The next three columns indicate addition-with-history,
|
||||
# switch, and remote lock status. This is followed by one space, and then
|
||||
# the path to the file.
|
||||
status_pattern = '^...... (.*)$'
|
||||
Args:
|
||||
args: A sequence of command line parameters to be passed to svn.
|
||||
in_directory: The directory where svn is to be run.
|
||||
print_messages: Whether to print status messages to stdout about
|
||||
which Subversion commands are being run.
|
||||
print_stdout: Whether to forward Subversion's output to stdout.
|
||||
filter: A function taking one argument (a string) which will be
|
||||
passed each line (with the ending newline character removed) of
|
||||
Subversion's output for filtering.
|
||||
|
||||
# args[0] must be a supported command. This will blow up if it's something
|
||||
# else, which is good. Note that the patterns are only effective when
|
||||
# these commands are used in their ordinary forms, the patterns are invalid
|
||||
# for "svn status --show-updates", for example.
|
||||
pattern = {
|
||||
'checkout': update_pattern,
|
||||
'status': status_pattern,
|
||||
'update': update_pattern,
|
||||
}[args[0]]
|
||||
Raises:
|
||||
Error: An error occurred while running the svn command.
|
||||
"""
|
||||
command = [SVN.COMMAND]
|
||||
command.extend(args)
|
||||
|
||||
compiled_pattern = re.compile(pattern)
|
||||
gclient_utils.SubprocessCallAndFilter(command,
|
||||
in_directory,
|
||||
print_messages,
|
||||
print_stdout,
|
||||
filter=filter)
|
||||
|
||||
def CaptureMatchingLines(line):
|
||||
match = compiled_pattern.search(line)
|
||||
if match:
|
||||
file_list.append(match.group(1))
|
||||
@staticmethod
|
||||
def CaptureInfo(relpath, in_directory=None, print_error=True):
|
||||
"""Returns a dictionary from the svn info output for the given file.
|
||||
|
||||
RunSVNAndFilterOutput(args,
|
||||
in_directory,
|
||||
options.verbose,
|
||||
True,
|
||||
CaptureMatchingLines)
|
||||
Args:
|
||||
relpath: The directory where the working copy resides relative to
|
||||
the directory given by in_directory.
|
||||
in_directory: The directory where svn is to be run.
|
||||
"""
|
||||
output = SVN.Capture(["info", "--xml", relpath], in_directory, print_error)
|
||||
dom = gclient_utils.ParseXML(output)
|
||||
result = {}
|
||||
if dom:
|
||||
GetNamedNodeText = gclient_utils.GetNamedNodeText
|
||||
GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText
|
||||
def C(item, f):
|
||||
if item is not None: return f(item)
|
||||
# /info/entry/
|
||||
# url
|
||||
# reposityory/(root|uuid)
|
||||
# wc-info/(schedule|depth)
|
||||
# commit/(author|date)
|
||||
# str() the results because they may be returned as Unicode, which
|
||||
# interferes with the higher layers matching up things in the deps
|
||||
# dictionary.
|
||||
# TODO(maruel): Fix at higher level instead (!)
|
||||
result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str)
|
||||
result['URL'] = C(GetNamedNodeText(dom, 'url'), str)
|
||||
result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str)
|
||||
result['Revision'] = C(GetNodeNamedAttributeText(dom, 'entry',
|
||||
'revision'),
|
||||
int)
|
||||
result['Node Kind'] = C(GetNodeNamedAttributeText(dom, 'entry', 'kind'),
|
||||
str)
|
||||
# Differs across versions.
|
||||
if result['Node Kind'] == 'dir':
|
||||
result['Node Kind'] = 'directory'
|
||||
result['Schedule'] = C(GetNamedNodeText(dom, 'schedule'), str)
|
||||
result['Path'] = C(GetNodeNamedAttributeText(dom, 'entry', 'path'), str)
|
||||
result['Copied From URL'] = C(GetNamedNodeText(dom, 'copy-from-url'), str)
|
||||
result['Copied From Rev'] = C(GetNamedNodeText(dom, 'copy-from-rev'), str)
|
||||
return result
|
||||
|
||||
def RunSVNAndFilterOutput(args,
|
||||
in_directory,
|
||||
print_messages,
|
||||
print_stdout,
|
||||
filter):
|
||||
"""Runs svn checkout, update, status, or diff, optionally outputting
|
||||
to stdout.
|
||||
@staticmethod
|
||||
def CaptureHeadRevision(url):
|
||||
"""Get the head revision of a SVN repository.
|
||||
|
||||
The first item in args must be either "checkout", "update",
|
||||
"status", or "diff".
|
||||
Returns:
|
||||
Int head revision
|
||||
"""
|
||||
info = SVN.Capture(["info", "--xml", url], os.getcwd())
|
||||
dom = xml.dom.minidom.parseString(info)
|
||||
return dom.getElementsByTagName('entry')[0].getAttribute('revision')
|
||||
|
||||
svn's stdout is passed line-by-line to the given filter function. If
|
||||
print_stdout is true, it is also printed to sys.stdout as in RunSVN.
|
||||
@staticmethod
|
||||
def CaptureStatus(files):
|
||||
"""Returns the svn 1.5 svn status emulated output.
|
||||
|
||||
Args:
|
||||
args: A sequence of command line parameters to be passed to svn.
|
||||
in_directory: The directory where svn is to be run.
|
||||
print_messages: Whether to print status messages to stdout about
|
||||
which Subversion commands are being run.
|
||||
print_stdout: Whether to forward Subversion's output to stdout.
|
||||
filter: A function taking one argument (a string) which will be
|
||||
passed each line (with the ending newline character removed) of
|
||||
Subversion's output for filtering.
|
||||
@files can be a string (one file) or a list of files.
|
||||
|
||||
Raises:
|
||||
Error: An error occurred while running the svn command.
|
||||
"""
|
||||
command = [SVN_COMMAND]
|
||||
command.extend(args)
|
||||
Returns an array of (status, file) tuples."""
|
||||
command = ["status", "--xml"]
|
||||
if not files:
|
||||
pass
|
||||
elif isinstance(files, basestring):
|
||||
command.append(files)
|
||||
else:
|
||||
command.extend(files)
|
||||
|
||||
gclient_utils.SubprocessCallAndFilter(command,
|
||||
in_directory,
|
||||
print_messages,
|
||||
print_stdout,
|
||||
filter=filter)
|
||||
status_letter = {
|
||||
None: ' ',
|
||||
'': ' ',
|
||||
'added': 'A',
|
||||
'conflicted': 'C',
|
||||
'deleted': 'D',
|
||||
'external': 'X',
|
||||
'ignored': 'I',
|
||||
'incomplete': '!',
|
||||
'merged': 'G',
|
||||
'missing': '!',
|
||||
'modified': 'M',
|
||||
'none': ' ',
|
||||
'normal': ' ',
|
||||
'obstructed': '~',
|
||||
'replaced': 'R',
|
||||
'unversioned': '?',
|
||||
}
|
||||
dom = gclient_utils.ParseXML(SVN.Capture(command))
|
||||
results = []
|
||||
if dom:
|
||||
# /status/target/entry/(wc-status|commit|author|date)
|
||||
for target in dom.getElementsByTagName('target'):
|
||||
#base_path = target.getAttribute('path')
|
||||
for entry in target.getElementsByTagName('entry'):
|
||||
file_path = entry.getAttribute('path')
|
||||
wc_status = entry.getElementsByTagName('wc-status')
|
||||
assert len(wc_status) == 1
|
||||
# Emulate svn 1.5 status ouput...
|
||||
statuses = [' '] * 7
|
||||
# Col 0
|
||||
xml_item_status = wc_status[0].getAttribute('item')
|
||||
if xml_item_status in status_letter:
|
||||
statuses[0] = status_letter[xml_item_status]
|
||||
else:
|
||||
raise Exception('Unknown item status "%s"; please implement me!' %
|
||||
xml_item_status)
|
||||
# Col 1
|
||||
xml_props_status = wc_status[0].getAttribute('props')
|
||||
if xml_props_status == 'modified':
|
||||
statuses[1] = 'M'
|
||||
elif xml_props_status == 'conflicted':
|
||||
statuses[1] = 'C'
|
||||
elif (not xml_props_status or xml_props_status == 'none' or
|
||||
xml_props_status == 'normal'):
|
||||
pass
|
||||
else:
|
||||
raise Exception('Unknown props status "%s"; please implement me!' %
|
||||
xml_props_status)
|
||||
# Col 2
|
||||
if wc_status[0].getAttribute('wc-locked') == 'true':
|
||||
statuses[2] = 'L'
|
||||
# Col 3
|
||||
if wc_status[0].getAttribute('copied') == 'true':
|
||||
statuses[3] = '+'
|
||||
# Col 4
|
||||
if wc_status[0].getAttribute('switched') == 'true':
|
||||
statuses[4] = 'S'
|
||||
# TODO(maruel): Col 5 and 6
|
||||
item = (''.join(statuses), file_path)
|
||||
results.append(item)
|
||||
return results
|
||||
|
||||
def CaptureSVNInfo(relpath, in_directory=None, print_error=True):
|
||||
"""Returns a dictionary from the svn info output for the given file.
|
||||
@staticmethod
|
||||
def IsMoved(filename):
|
||||
"""Determine if a file has been added through svn mv"""
|
||||
info = SVN.CaptureInfo(filename)
|
||||
return (info.get('Copied From URL') and
|
||||
info.get('Copied From Rev') and
|
||||
info.get('Schedule') == 'add')
|
||||
|
||||
Args:
|
||||
relpath: The directory where the working copy resides relative to
|
||||
the directory given by in_directory.
|
||||
in_directory: The directory where svn is to be run.
|
||||
"""
|
||||
output = CaptureSVN(["info", "--xml", relpath], in_directory, print_error)
|
||||
dom = gclient_utils.ParseXML(output)
|
||||
result = {}
|
||||
if dom:
|
||||
GetNamedNodeText = gclient_utils.GetNamedNodeText
|
||||
GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText
|
||||
def C(item, f):
|
||||
if item is not None: return f(item)
|
||||
# /info/entry/
|
||||
# url
|
||||
# reposityory/(root|uuid)
|
||||
# wc-info/(schedule|depth)
|
||||
# commit/(author|date)
|
||||
# str() the results because they may be returned as Unicode, which
|
||||
# interferes with the higher layers matching up things in the deps
|
||||
# dictionary.
|
||||
# TODO(maruel): Fix at higher level instead (!)
|
||||
result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str)
|
||||
result['URL'] = C(GetNamedNodeText(dom, 'url'), str)
|
||||
result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str)
|
||||
result['Revision'] = C(GetNodeNamedAttributeText(dom, 'entry', 'revision'),
|
||||
int)
|
||||
result['Node Kind'] = C(GetNodeNamedAttributeText(dom, 'entry', 'kind'),
|
||||
str)
|
||||
result['Schedule'] = C(GetNamedNodeText(dom, 'schedule'), str)
|
||||
result['Path'] = C(GetNodeNamedAttributeText(dom, 'entry', 'path'), str)
|
||||
result['Copied From URL'] = C(GetNamedNodeText(dom, 'copy-from-url'), str)
|
||||
result['Copied From Rev'] = C(GetNamedNodeText(dom, 'copy-from-rev'), str)
|
||||
return result
|
||||
@staticmethod
|
||||
def GetFileProperty(file, property_name):
|
||||
"""Returns the value of an SVN property for the given file.
|
||||
|
||||
Args:
|
||||
file: The file to check
|
||||
property_name: The name of the SVN property, e.g. "svn:mime-type"
|
||||
|
||||
def CaptureSVNHeadRevision(url):
|
||||
"""Get the head revision of a SVN repository.
|
||||
Returns:
|
||||
The value of the property, which will be the empty string if the property
|
||||
is not set on the file. If the file is not under version control, the
|
||||
empty string is also returned.
|
||||
"""
|
||||
output = SVN.Run(["propget", property_name, file], None)
|
||||
if (output.startswith("svn: ") and
|
||||
output.endswith("is not under version control")):
|
||||
return ""
|
||||
else:
|
||||
return output
|
||||
|
||||
Returns:
|
||||
Int head revision
|
||||
"""
|
||||
info = CaptureSVN(["info", "--xml", url], os.getcwd())
|
||||
dom = xml.dom.minidom.parseString(info)
|
||||
return dom.getElementsByTagName('entry')[0].getAttribute('revision')
|
||||
@staticmethod
|
||||
def DiffItem(filename):
|
||||
"""Diff a single file"""
|
||||
# Use svn info output instead of os.path.isdir because the latter fails
|
||||
# when the file is deleted.
|
||||
if SVN.CaptureInfo(filename).get("Node Kind") == "directory":
|
||||
return None
|
||||
# If the user specified a custom diff command in their svn config file,
|
||||
# then it'll be used when we do svn diff, which we don't want to happen
|
||||
# since we want the unified diff. Using --diff-cmd=diff doesn't always
|
||||
# work, since they can have another diff executable in their path that
|
||||
# gives different line endings. So we use a bogus temp directory as the
|
||||
# config directory, which gets around these problems.
|
||||
if sys.platform.startswith("win"):
|
||||
parent_dir = tempfile.gettempdir()
|
||||
else:
|
||||
parent_dir = sys.path[0] # tempdir is not secure.
|
||||
bogus_dir = os.path.join(parent_dir, "temp_svn_config")
|
||||
if not os.path.exists(bogus_dir):
|
||||
os.mkdir(bogus_dir)
|
||||
# Grabs the diff data.
|
||||
data = SVN.Capture(["diff", "--config-dir", bogus_dir, filename], None)
|
||||
|
||||
|
||||
def CaptureSVNStatus(files):
|
||||
"""Returns the svn 1.5 svn status emulated output.
|
||||
|
||||
@files can be a string (one file) or a list of files.
|
||||
|
||||
Returns an array of (status, file) tuples."""
|
||||
command = ["status", "--xml"]
|
||||
if not files:
|
||||
pass
|
||||
elif isinstance(files, basestring):
|
||||
command.append(files)
|
||||
else:
|
||||
command.extend(files)
|
||||
|
||||
status_letter = {
|
||||
None: ' ',
|
||||
'': ' ',
|
||||
'added': 'A',
|
||||
'conflicted': 'C',
|
||||
'deleted': 'D',
|
||||
'external': 'X',
|
||||
'ignored': 'I',
|
||||
'incomplete': '!',
|
||||
'merged': 'G',
|
||||
'missing': '!',
|
||||
'modified': 'M',
|
||||
'none': ' ',
|
||||
'normal': ' ',
|
||||
'obstructed': '~',
|
||||
'replaced': 'R',
|
||||
'unversioned': '?',
|
||||
}
|
||||
dom = gclient_utils.ParseXML(CaptureSVN(command))
|
||||
results = []
|
||||
if dom:
|
||||
# /status/target/entry/(wc-status|commit|author|date)
|
||||
for target in dom.getElementsByTagName('target'):
|
||||
for entry in target.getElementsByTagName('entry'):
|
||||
file_path = entry.getAttribute('path')
|
||||
wc_status = entry.getElementsByTagName('wc-status')
|
||||
assert len(wc_status) == 1
|
||||
# Emulate svn 1.5 status ouput...
|
||||
statuses = [' '] * 7
|
||||
# Col 0
|
||||
xml_item_status = wc_status[0].getAttribute('item')
|
||||
if xml_item_status in status_letter:
|
||||
statuses[0] = status_letter[xml_item_status]
|
||||
else:
|
||||
raise Exception('Unknown item status "%s"; please implement me!' %
|
||||
xml_item_status)
|
||||
# Col 1
|
||||
xml_props_status = wc_status[0].getAttribute('props')
|
||||
if xml_props_status == 'modified':
|
||||
statuses[1] = 'M'
|
||||
elif xml_props_status == 'conflicted':
|
||||
statuses[1] = 'C'
|
||||
elif (not xml_props_status or xml_props_status == 'none' or
|
||||
xml_props_status == 'normal'):
|
||||
pass
|
||||
else:
|
||||
raise Exception('Unknown props status "%s"; please implement me!' %
|
||||
xml_props_status)
|
||||
# Col 2
|
||||
if wc_status[0].getAttribute('wc-locked') == 'true':
|
||||
statuses[2] = 'L'
|
||||
# Col 3
|
||||
if wc_status[0].getAttribute('copied') == 'true':
|
||||
statuses[3] = '+'
|
||||
# Col 4
|
||||
if wc_status[0].getAttribute('switched') == 'true':
|
||||
statuses[4] = 'S'
|
||||
# TODO(maruel): Col 5 and 6
|
||||
item = (''.join(statuses), file_path)
|
||||
results.append(item)
|
||||
return results
|
||||
# We know the diff will be incorrectly formatted. Fix it.
|
||||
if SVN.IsMoved(filename):
|
||||
# The file is "new" in the patch sense. Generate a homebrew diff.
|
||||
# We can't use ReadFile() since it's not using binary mode.
|
||||
file_handle = open(filename, 'rb')
|
||||
file_content = file_handle.read()
|
||||
file_handle.close()
|
||||
# Prepend '+' to every lines.
|
||||
file_content = ['+' + i for i in file_content.splitlines(True)]
|
||||
nb_lines = len(file_content)
|
||||
# We need to use / since patch on unix will fail otherwise.
|
||||
filename = filename.replace('\\', '/')
|
||||
data = "Index: %s\n" % filename
|
||||
data += ("============================================================="
|
||||
"======\n")
|
||||
# Note: Should we use /dev/null instead?
|
||||
data += "--- %s\n" % filename
|
||||
data += "+++ %s\n" % filename
|
||||
data += "@@ -0,0 +1,%d @@\n" % nb_lines
|
||||
data += ''.join(file_content)
|
||||
return data
|
||||
|
||||
@@ -16,7 +16,7 @@ class GclTestsBase(SuperMoxTestBase):
|
||||
SuperMoxTestBase.setUp(self)
|
||||
self.fake_root_dir = self.RootDir()
|
||||
self.mox.StubOutWithMock(gcl, 'RunShell')
|
||||
self.mox.StubOutWithMock(gcl.gclient_scm, 'CaptureSVNInfo')
|
||||
self.mox.StubOutWithMock(gcl.SVN, 'CaptureInfo')
|
||||
self.mox.StubOutWithMock(gcl, 'tempfile')
|
||||
self.mox.StubOutWithMock(gcl.upload, 'RealMain')
|
||||
# These are not tested.
|
||||
@@ -29,25 +29,21 @@ class GclUnittest(GclTestsBase):
|
||||
def testMembersChanged(self):
|
||||
self.mox.ReplayAll()
|
||||
members = [
|
||||
'CODEREVIEW_SETTINGS', 'CODEREVIEW_SETTINGS_FILE',
|
||||
'Change', 'ChangeInfo', 'Changes', 'Commit',
|
||||
'DEFAULT_LINT_IGNORE_REGEX', 'DEFAULT_LINT_REGEX',
|
||||
'DeleteEmptyChangeLists', 'DoPresubmitChecks',
|
||||
'ErrorExit', 'FILES_CACHE', 'FilterFlag', 'GenerateChangeName',
|
||||
'GenerateDiff',
|
||||
'GetCacheDir', 'GetCachedFile', 'GetChangesDir', 'GetCLs',
|
||||
'GetChangelistInfoFile', 'GetCodeReviewSetting', 'GetEditor',
|
||||
'GetFilesNotInCL', 'GetInfoDir', 'GetIssueDescription',
|
||||
'GetModifiedFiles', 'GetRepositoryRoot',
|
||||
'GetSVNFileProperty', 'Help', 'IsSVNMoved',
|
||||
'Lint', 'LoadChangelistInfoForMultiple',
|
||||
'MISSING_TEST_MSG', 'Opened', 'OptionallyDoPresubmitChecks',
|
||||
'PresubmitCL', 'ReadFile', 'REPOSITORY_ROOT', 'RunShell',
|
||||
'RunShellWithReturnCode', 'SendToRietveld', 'TryChange',
|
||||
'UnknownFiles', 'UploadCL', 'Warn', 'WriteFile',
|
||||
'gclient_scm', 'gclient_utils', 'getpass', 'main', 'os', 'random', 're',
|
||||
'shutil', 'string', 'subprocess', 'sys', 'tempfile',
|
||||
'upload', 'urllib2',
|
||||
'CODEREVIEW_SETTINGS', 'CODEREVIEW_SETTINGS_FILE', 'Change',
|
||||
'ChangeInfo', 'Changes', 'Commit', 'DEFAULT_LINT_IGNORE_REGEX',
|
||||
'DEFAULT_LINT_REGEX', 'DeleteEmptyChangeLists', 'DoPresubmitChecks',
|
||||
'ErrorExit', 'FILES_CACHE', 'FilterFlag', 'GenerateChangeName',
|
||||
'GenerateDiff', 'GetCLs', 'GetCacheDir', 'GetCachedFile',
|
||||
'GetChangelistInfoFile', 'GetChangesDir', 'GetCodeReviewSetting',
|
||||
'GetEditor', 'GetFilesNotInCL', 'GetInfoDir', 'GetIssueDescription',
|
||||
'GetModifiedFiles', 'GetRepositoryRoot', 'Help', 'Lint',
|
||||
'LoadChangelistInfoForMultiple', 'MISSING_TEST_MSG', 'Opened',
|
||||
'OptionallyDoPresubmitChecks', 'PresubmitCL', 'REPOSITORY_ROOT',
|
||||
'ReadFile', 'RunShell', 'RunShellWithReturnCode', 'SVN',
|
||||
'SendToRietveld', 'TryChange', 'UnknownFiles', 'UploadCL', 'Warn',
|
||||
'WriteFile', 'gclient_utils', 'getpass', 'main', 'os', 'random', 're',
|
||||
'shutil', 'string', 'subprocess', 'sys', 'tempfile', 'upload',
|
||||
'urllib2',
|
||||
]
|
||||
# If this test fails, you should add the relevant test.
|
||||
self.compareMembers(gcl, members)
|
||||
@@ -70,8 +66,7 @@ class GclUnittest(GclTestsBase):
|
||||
result = {
|
||||
"Repository Root": ""
|
||||
}
|
||||
gcl.gclient_scm.CaptureSVNInfo("/bleh/prout", print_error=False).AndReturn(
|
||||
result)
|
||||
gcl.SVN.CaptureInfo("/bleh/prout", print_error=False).AndReturn(result)
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(Exception, gcl.GetRepositoryRoot)
|
||||
|
||||
@@ -80,13 +75,11 @@ class GclUnittest(GclTestsBase):
|
||||
root_path = gcl.os.path.join('bleh', 'prout', 'pouet')
|
||||
gcl.os.getcwd().AndReturn(root_path)
|
||||
result1 = { "Repository Root": "Some root" }
|
||||
gcl.gclient_scm.CaptureSVNInfo(root_path,
|
||||
print_error=False).AndReturn(result1)
|
||||
gcl.SVN.CaptureInfo(root_path, print_error=False).AndReturn(result1)
|
||||
gcl.os.getcwd().AndReturn(root_path)
|
||||
results2 = { "Repository Root": "A different root" }
|
||||
gcl.gclient_scm.CaptureSVNInfo(
|
||||
gcl.os.path.dirname(root_path),
|
||||
print_error=False).AndReturn(results2)
|
||||
gcl.SVN.CaptureInfo(gcl.os.path.dirname(root_path),
|
||||
print_error=False).AndReturn(results2)
|
||||
self.mox.ReplayAll()
|
||||
self.assertEquals(gcl.GetRepositoryRoot(), root_path)
|
||||
|
||||
|
||||
@@ -33,12 +33,12 @@ class BaseTestCase(GCBaseTestCase):
|
||||
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileWrite')
|
||||
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'SubprocessCall')
|
||||
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'RemoveDirectory')
|
||||
self._CaptureSVNInfo = gclient_scm.CaptureSVNInfo
|
||||
self.mox.StubOutWithMock(gclient_scm, 'CaptureSVN')
|
||||
self.mox.StubOutWithMock(gclient_scm, 'CaptureSVNInfo')
|
||||
self.mox.StubOutWithMock(gclient_scm, 'CaptureSVNStatus')
|
||||
self.mox.StubOutWithMock(gclient_scm, 'RunSVN')
|
||||
self.mox.StubOutWithMock(gclient_scm, 'RunSVNAndGetFileList')
|
||||
self._CaptureSVNInfo = gclient_scm.scm.SVN.CaptureInfo
|
||||
self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'Capture')
|
||||
self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'CaptureInfo')
|
||||
self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'CaptureStatus')
|
||||
self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'Run')
|
||||
self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'RunAndGetFileList')
|
||||
self._scm_wrapper = gclient_scm.CreateSCM
|
||||
|
||||
|
||||
@@ -64,9 +64,11 @@ class SVNWrapperTestCase(BaseTestCase):
|
||||
|
||||
def testDir(self):
|
||||
members = [
|
||||
'FullUrlForRelativeUrl', 'RunCommand', 'cleanup', 'diff', 'export',
|
||||
'pack', 'relpath', 'revert', 'revinfo', 'runhooks', 'scm_name', 'status',
|
||||
'update', 'url',
|
||||
'COMMAND', 'Capture', 'CaptureHeadRevision', 'CaptureInfo',
|
||||
'CaptureStatus', 'DiffItem', 'FullUrlForRelativeUrl', 'GetFileProperty',
|
||||
'IsMoved', 'Run', 'RunAndFilterOutput', 'RunAndGetFileList',
|
||||
'RunCommand', 'cleanup', 'diff', 'export', 'pack', 'relpath', 'revert',
|
||||
'revinfo', 'runhooks', 'scm_name', 'status', 'update', 'url',
|
||||
]
|
||||
|
||||
# If you add a member, be sure to add the relevant test!
|
||||
@@ -113,8 +115,9 @@ class SVNWrapperTestCase(BaseTestCase):
|
||||
# Checkout.
|
||||
gclient_scm.os.path.exists(base_path).AndReturn(False)
|
||||
files_list = self.mox.CreateMockAnything()
|
||||
gclient_scm.RunSVNAndGetFileList(options, ['checkout', self.url, base_path],
|
||||
self.root_dir, files_list)
|
||||
gclient_scm.scm.SVN.RunAndGetFileList(options,
|
||||
['checkout', self.url, base_path],
|
||||
self.root_dir, files_list)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
|
||||
@@ -125,9 +128,10 @@ class SVNWrapperTestCase(BaseTestCase):
|
||||
options = self.Options(verbose=True)
|
||||
base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
|
||||
gclient_scm.os.path.isdir(base_path).AndReturn(True)
|
||||
gclient_scm.CaptureSVNStatus(base_path).AndReturn([])
|
||||
gclient_scm.RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'],
|
||||
base_path, mox.IgnoreArg())
|
||||
gclient_scm.scm.SVN.CaptureStatus(base_path).AndReturn([])
|
||||
gclient_scm.scm.SVN.RunAndGetFileList(options,
|
||||
['update', '--revision', 'BASE'],
|
||||
base_path, mox.IgnoreArg())
|
||||
|
||||
self.mox.ReplayAll()
|
||||
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
|
||||
@@ -145,15 +149,16 @@ class SVNWrapperTestCase(BaseTestCase):
|
||||
]
|
||||
file_path1 = gclient_scm.os.path.join(base_path, 'a')
|
||||
file_path2 = gclient_scm.os.path.join(base_path, 'b')
|
||||
gclient_scm.CaptureSVNStatus(base_path).AndReturn(items)
|
||||
gclient_scm.scm.SVN.CaptureStatus(base_path).AndReturn(items)
|
||||
gclient_scm.os.path.exists(file_path1).AndReturn(True)
|
||||
gclient_scm.os.path.isfile(file_path1).AndReturn(True)
|
||||
gclient_scm.os.remove(file_path1)
|
||||
gclient_scm.os.path.exists(file_path2).AndReturn(True)
|
||||
gclient_scm.os.path.isfile(file_path2).AndReturn(True)
|
||||
gclient_scm.os.remove(file_path2)
|
||||
gclient_scm.RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'],
|
||||
base_path, mox.IgnoreArg())
|
||||
gclient_scm.scm.SVN.RunAndGetFileList(options,
|
||||
['update', '--revision', 'BASE'],
|
||||
base_path, mox.IgnoreArg())
|
||||
print(gclient_scm.os.path.join(base_path, 'a'))
|
||||
print(gclient_scm.os.path.join(base_path, 'b'))
|
||||
|
||||
@@ -170,7 +175,7 @@ class SVNWrapperTestCase(BaseTestCase):
|
||||
items = [
|
||||
('~ ', 'a'),
|
||||
]
|
||||
gclient_scm.CaptureSVNStatus(base_path).AndReturn(items)
|
||||
gclient_scm.scm.SVN.CaptureStatus(base_path).AndReturn(items)
|
||||
file_path = gclient_scm.os.path.join(base_path, 'a')
|
||||
print(file_path)
|
||||
gclient_scm.os.path.exists(file_path).AndReturn(True)
|
||||
@@ -178,8 +183,9 @@ class SVNWrapperTestCase(BaseTestCase):
|
||||
gclient_scm.os.path.isdir(file_path).AndReturn(True)
|
||||
gclient_scm.gclient_utils.RemoveDirectory(file_path)
|
||||
file_list1 = []
|
||||
gclient_scm.RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'],
|
||||
base_path, mox.IgnoreArg())
|
||||
gclient_scm.scm.SVN.RunAndGetFileList(options,
|
||||
['update', '--revision', 'BASE'],
|
||||
base_path, mox.IgnoreArg())
|
||||
|
||||
self.mox.ReplayAll()
|
||||
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
|
||||
@@ -191,8 +197,9 @@ class SVNWrapperTestCase(BaseTestCase):
|
||||
options = self.Options(verbose=True)
|
||||
base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
|
||||
gclient_scm.os.path.isdir(base_path).AndReturn(True)
|
||||
gclient_scm.RunSVNAndGetFileList(options, ['status'] + self.args,
|
||||
base_path, []).AndReturn(None)
|
||||
gclient_scm.scm.SVN.RunAndGetFileList(options,
|
||||
['status'] + self.args,
|
||||
base_path, []).AndReturn(None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
|
||||
@@ -216,8 +223,9 @@ class SVNWrapperTestCase(BaseTestCase):
|
||||
# Checkout.
|
||||
gclient_scm.os.path.exists(base_path).AndReturn(False)
|
||||
files_list = self.mox.CreateMockAnything()
|
||||
gclient_scm.RunSVNAndGetFileList(options, ['checkout', self.url,
|
||||
base_path], self.root_dir, files_list)
|
||||
gclient_scm.scm.SVN.RunAndGetFileList(options,
|
||||
['checkout', self.url, base_path],
|
||||
self.root_dir, files_list)
|
||||
self.mox.ReplayAll()
|
||||
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
|
||||
relpath=self.relpath)
|
||||
@@ -238,17 +246,19 @@ class SVNWrapperTestCase(BaseTestCase):
|
||||
).AndReturn(False)
|
||||
# Checkout or update.
|
||||
gclient_scm.os.path.exists(base_path).AndReturn(True)
|
||||
gclient_scm.CaptureSVNInfo(gclient_scm.os.path.join(base_path, "."), '.'
|
||||
).AndReturn(file_info)
|
||||
gclient_scm.scm.SVN.CaptureInfo(
|
||||
gclient_scm.os.path.join(base_path, "."), '.'
|
||||
).AndReturn(file_info)
|
||||
# Cheat a bit here.
|
||||
gclient_scm.CaptureSVNInfo(file_info['URL'], '.').AndReturn(file_info)
|
||||
gclient_scm.scm.SVN.CaptureInfo(file_info['URL'], '.').AndReturn(file_info)
|
||||
additional_args = []
|
||||
if options.manually_grab_svn_rev:
|
||||
additional_args = ['--revision', str(file_info['Revision'])]
|
||||
files_list = []
|
||||
gclient_scm.RunSVNAndGetFileList(options,
|
||||
['update', base_path] + additional_args,
|
||||
self.root_dir, files_list)
|
||||
gclient_scm.scm.SVN.RunAndGetFileList(
|
||||
options,
|
||||
['update', base_path] + additional_args,
|
||||
self.root_dir, files_list)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
|
||||
@@ -356,9 +366,9 @@ from :3
|
||||
|
||||
def testDir(self):
|
||||
members = [
|
||||
'FullUrlForRelativeUrl', 'RunCommand', 'cleanup', 'diff', 'export',
|
||||
'relpath', 'revert', 'revinfo', 'runhooks', 'scm_name', 'status',
|
||||
'update', 'url',
|
||||
'COMMAND', 'Capture', 'CaptureStatus', 'FullUrlForRelativeUrl',
|
||||
'RunCommand', 'cleanup', 'diff', 'export', 'relpath', 'revert',
|
||||
'revinfo', 'runhooks', 'scm_name', 'status', 'update', 'url',
|
||||
]
|
||||
|
||||
# If you add a member, be sure to add the relevant test!
|
||||
|
||||
@@ -22,8 +22,6 @@ import __builtin__
|
||||
import StringIO
|
||||
|
||||
import gclient
|
||||
# Temporary due to the "from scm import *" in gclient_scm.
|
||||
import scm
|
||||
from super_mox import mox, IsOneOf, SuperMoxTestBase
|
||||
|
||||
|
||||
@@ -50,16 +48,11 @@ class GClientBaseTestCase(BaseTestCase):
|
||||
self.mox.StubOutWithMock(gclient.gclient_utils, 'SubprocessCall')
|
||||
self.mox.StubOutWithMock(gclient.gclient_utils, 'RemoveDirectory')
|
||||
# Mock them to be sure nothing bad happens.
|
||||
self.mox.StubOutWithMock(gclient.gclient_scm, 'CaptureSVN')
|
||||
self.mox.StubOutWithMock(gclient.gclient_scm, 'CaptureSVNInfo')
|
||||
self.mox.StubOutWithMock(gclient.gclient_scm, 'CaptureSVNStatus')
|
||||
self.mox.StubOutWithMock(gclient.gclient_scm, 'RunSVN')
|
||||
self.mox.StubOutWithMock(gclient.gclient_scm, 'RunSVNAndGetFileList')
|
||||
self.mox.StubOutWithMock(scm, 'CaptureSVN')
|
||||
self.mox.StubOutWithMock(scm, 'CaptureSVNInfo')
|
||||
self.mox.StubOutWithMock(scm, 'CaptureSVNStatus')
|
||||
self.mox.StubOutWithMock(scm, 'RunSVN')
|
||||
self.mox.StubOutWithMock(scm, 'RunSVNAndGetFileList')
|
||||
self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'Capture')
|
||||
self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'CaptureInfo')
|
||||
self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'CaptureStatus')
|
||||
self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'Run')
|
||||
self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'RunAndGetFileList')
|
||||
self._gclient_gclient = gclient.GClient
|
||||
gclient.GClient = self.mox.CreateMockAnything()
|
||||
self._scm_wrapper = gclient.gclient_scm.CreateSCM
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
import StringIO
|
||||
|
||||
# Local imports
|
||||
import presubmit_support as presubmit
|
||||
import presubmit_canned_checks
|
||||
# Shortcut.
|
||||
from presubmit_support import presubmit_canned_checks
|
||||
from super_mox import mox, SuperMoxTestBase
|
||||
|
||||
|
||||
@@ -47,9 +47,11 @@ def GetPreferredTrySlaves():
|
||||
presubmit.os.path.abspath = MockAbsPath
|
||||
presubmit.os.getcwd = self.RootDir
|
||||
presubmit.os.chdir = MockChdir
|
||||
self.mox.StubOutWithMock(presubmit.gclient_scm, 'CaptureSVNInfo')
|
||||
self.mox.StubOutWithMock(presubmit.gcl, 'GetSVNFileProperty')
|
||||
self.mox.StubOutWithMock(presubmit.scm.SVN, 'CaptureInfo')
|
||||
self.mox.StubOutWithMock(presubmit.scm.SVN, 'GetFileProperty')
|
||||
# TODO(maruel): Err, small duplication of code here.
|
||||
self.mox.StubOutWithMock(presubmit.gcl, 'ReadFile')
|
||||
self.mox.StubOutWithMock(presubmit.gclient_utils, 'FileRead')
|
||||
|
||||
|
||||
class PresubmitUnittest(PresubmitTestsBase):
|
||||
@@ -63,9 +65,9 @@ class PresubmitUnittest(PresubmitTestsBase):
|
||||
'NotImplementedException', 'OutputApi', 'ParseFiles',
|
||||
'PresubmitExecuter', 'PromptYesNo', 'ScanSubDirs',
|
||||
'SvnAffectedFile', 'SvnChange', 'cPickle', 'cStringIO',
|
||||
'exceptions', 'fnmatch', 'gcl', 'gclient_scm', 'glob',
|
||||
'exceptions', 'fnmatch', 'gcl', 'gclient_utils', 'glob',
|
||||
'logging', 'marshal', 'normpath', 'optparse', 'os', 'pickle',
|
||||
'presubmit_canned_checks', 'random', 're', 'subprocess', 'sys',
|
||||
'presubmit_canned_checks', 'random', 're', 'scm', 'subprocess', 'sys',
|
||||
'tempfile', 'time', 'traceback', 'types', 'unittest', 'urllib2',
|
||||
'warnings',
|
||||
]
|
||||
@@ -140,22 +142,22 @@ class PresubmitUnittest(PresubmitTestsBase):
|
||||
presubmit.os.path.exists(notfound).AndReturn(True)
|
||||
presubmit.os.path.isdir(notfound).AndReturn(False)
|
||||
presubmit.os.path.exists(flap).AndReturn(False)
|
||||
presubmit.gclient_scm.CaptureSVNInfo(flap
|
||||
presubmit.scm.SVN.CaptureInfo(flap
|
||||
).AndReturn({'Node Kind': 'file'})
|
||||
presubmit.gcl.GetSVNFileProperty(blat, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.gcl.GetSVNFileProperty(
|
||||
presubmit.scm.SVN.GetFileProperty(blat, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.scm.SVN.GetFileProperty(
|
||||
binary, 'svn:mime-type').AndReturn('application/octet-stream')
|
||||
presubmit.gcl.GetSVNFileProperty(
|
||||
presubmit.scm.SVN.GetFileProperty(
|
||||
notfound, 'svn:mime-type').AndReturn('')
|
||||
presubmit.gclient_scm.CaptureSVNInfo(blat).AndReturn(
|
||||
presubmit.scm.SVN.CaptureInfo(blat).AndReturn(
|
||||
{'URL': 'svn:/foo/foo/blat.cc'})
|
||||
presubmit.gclient_scm.CaptureSVNInfo(binary).AndReturn(
|
||||
presubmit.scm.SVN.CaptureInfo(binary).AndReturn(
|
||||
{'URL': 'svn:/foo/binary.dll'})
|
||||
presubmit.gclient_scm.CaptureSVNInfo(notfound).AndReturn({})
|
||||
presubmit.gclient_scm.CaptureSVNInfo(flap).AndReturn(
|
||||
presubmit.scm.SVN.CaptureInfo(notfound).AndReturn({})
|
||||
presubmit.scm.SVN.CaptureInfo(flap).AndReturn(
|
||||
{'URL': 'svn:/foo/boo/flap.h'})
|
||||
presubmit.gcl.ReadFile(blat).AndReturn('boo!\nahh?')
|
||||
presubmit.gcl.ReadFile(notfound).AndReturn('look!\nthere?')
|
||||
presubmit.gclient_utils.FileRead(blat, 'rU').AndReturn('boo!\nahh?')
|
||||
presubmit.gclient_utils.FileRead(notfound, 'rU').AndReturn('look!\nthere?')
|
||||
self.mox.ReplayAll()
|
||||
|
||||
change = presubmit.SvnChange('mychange', '\n'.join(description_lines),
|
||||
@@ -285,10 +287,10 @@ class PresubmitUnittest(PresubmitTestsBase):
|
||||
root_path = join(self.fake_root_dir, 'PRESUBMIT.py')
|
||||
presubmit.os.path.isfile(root_path).AndReturn(True)
|
||||
presubmit.os.path.isfile(haspresubmit_path).AndReturn(True)
|
||||
presubmit.gcl.ReadFile(root_path,
|
||||
'rU').AndReturn(self.presubmit_text)
|
||||
presubmit.gcl.ReadFile(haspresubmit_path,
|
||||
'rU').AndReturn(self.presubmit_text)
|
||||
presubmit.gclient_utils.FileRead(root_path,
|
||||
'rU').AndReturn(self.presubmit_text)
|
||||
presubmit.gclient_utils.FileRead(haspresubmit_path,
|
||||
'rU').AndReturn(self.presubmit_text)
|
||||
presubmit.random.randint(0, 4).AndReturn(1)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@@ -313,9 +315,9 @@ class PresubmitUnittest(PresubmitTestsBase):
|
||||
for i in range(2):
|
||||
presubmit.os.path.isfile(presubmit_path).AndReturn(True)
|
||||
presubmit.os.path.isfile(haspresubmit_path).AndReturn(True)
|
||||
presubmit.gcl.ReadFile(presubmit_path, 'rU'
|
||||
presubmit.gclient_utils.FileRead(presubmit_path, 'rU'
|
||||
).AndReturn(self.presubmit_text)
|
||||
presubmit.gcl.ReadFile(haspresubmit_path, 'rU'
|
||||
presubmit.gclient_utils.FileRead(haspresubmit_path, 'rU'
|
||||
).AndReturn(self.presubmit_text)
|
||||
presubmit.random.randint(0, 4).AndReturn(1)
|
||||
presubmit.random.randint(0, 4).AndReturn(1)
|
||||
@@ -349,8 +351,9 @@ class PresubmitUnittest(PresubmitTestsBase):
|
||||
'PRESUBMIT.py')
|
||||
presubmit.os.path.isfile(presubmit_path).AndReturn(True)
|
||||
presubmit.os.path.isfile(haspresubmit_path).AndReturn(True)
|
||||
presubmit.gcl.ReadFile(presubmit_path, 'rU').AndReturn(self.presubmit_text)
|
||||
presubmit.gcl.ReadFile(haspresubmit_path, 'rU').AndReturn(
|
||||
presubmit.gclient_utils.FileRead(presubmit_path, 'rU'
|
||||
).AndReturn(self.presubmit_text)
|
||||
presubmit.gclient_utils.FileRead(haspresubmit_path, 'rU').AndReturn(
|
||||
self.presubmit_text)
|
||||
presubmit.random.randint(0, 4).AndReturn(1)
|
||||
self.mox.ReplayAll()
|
||||
@@ -502,14 +505,14 @@ def CheckChangeOnCommit(input_api, output_api):
|
||||
linux_presubmit = join(self.fake_root_dir, 'linux_only', 'PRESUBMIT.py')
|
||||
|
||||
presubmit.os.path.isfile(root_presubmit).AndReturn(True)
|
||||
presubmit.gcl.ReadFile(root_presubmit, 'rU').AndReturn(
|
||||
presubmit.gclient_utils.FileRead(root_presubmit, 'rU').AndReturn(
|
||||
self.presubmit_tryslave % '["win"]')
|
||||
|
||||
presubmit.os.path.isfile(root_presubmit).AndReturn(True)
|
||||
presubmit.os.path.isfile(linux_presubmit).AndReturn(True)
|
||||
presubmit.gcl.ReadFile(root_presubmit, 'rU').AndReturn(
|
||||
presubmit.gclient_utils.FileRead(root_presubmit, 'rU').AndReturn(
|
||||
self.presubmit_tryslave % '["win"]')
|
||||
presubmit.gcl.ReadFile(linux_presubmit, 'rU').AndReturn(
|
||||
presubmit.gclient_utils.FileRead(linux_presubmit, 'rU').AndReturn(
|
||||
self.presubmit_tryslave % '["linux"]')
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@@ -565,9 +568,9 @@ class InputApiUnittest(PresubmitTestsBase):
|
||||
self.compareMembers(presubmit.InputApi(None, './.', False), members)
|
||||
|
||||
def testDepotToLocalPath(self):
|
||||
presubmit.gclient_scm.CaptureSVNInfo('svn://foo/smurf').AndReturn(
|
||||
presubmit.scm.SVN.CaptureInfo('svn://foo/smurf').AndReturn(
|
||||
{'Path': 'prout'})
|
||||
presubmit.gclient_scm.CaptureSVNInfo('svn:/foo/notfound/burp').AndReturn({})
|
||||
presubmit.scm.SVN.CaptureInfo('svn:/foo/notfound/burp').AndReturn({})
|
||||
self.mox.ReplayAll()
|
||||
|
||||
path = presubmit.InputApi(None, './p', False).DepotToLocalPath(
|
||||
@@ -578,9 +581,9 @@ class InputApiUnittest(PresubmitTestsBase):
|
||||
self.failUnless(path == None)
|
||||
|
||||
def testLocalToDepotPath(self):
|
||||
presubmit.gclient_scm.CaptureSVNInfo('smurf').AndReturn({'URL':
|
||||
presubmit.scm.SVN.CaptureInfo('smurf').AndReturn({'URL':
|
||||
'svn://foo'})
|
||||
presubmit.gclient_scm.CaptureSVNInfo('notfound-food').AndReturn({})
|
||||
presubmit.scm.SVN.CaptureInfo('notfound-food').AndReturn({})
|
||||
self.mox.ReplayAll()
|
||||
|
||||
path = presubmit.InputApi(None, './p', False).LocalToDepotPath('smurf')
|
||||
@@ -631,18 +634,20 @@ class InputApiUnittest(PresubmitTestsBase):
|
||||
presubmit.os.path.exists(notfound).AndReturn(False)
|
||||
presubmit.os.path.exists(flap).AndReturn(True)
|
||||
presubmit.os.path.isdir(flap).AndReturn(False)
|
||||
presubmit.gclient_scm.CaptureSVNInfo(beingdeleted).AndReturn({})
|
||||
presubmit.gclient_scm.CaptureSVNInfo(notfound).AndReturn({})
|
||||
presubmit.gcl.GetSVNFileProperty(blat, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.gcl.GetSVNFileProperty(readme, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.gcl.GetSVNFileProperty(binary, 'svn:mime-type').AndReturn(
|
||||
presubmit.scm.SVN.CaptureInfo(beingdeleted).AndReturn({})
|
||||
presubmit.scm.SVN.CaptureInfo(notfound).AndReturn({})
|
||||
presubmit.scm.SVN.GetFileProperty(blat, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.scm.SVN.GetFileProperty(readme, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.scm.SVN.GetFileProperty(binary, 'svn:mime-type').AndReturn(
|
||||
'application/octet-stream')
|
||||
presubmit.gcl.GetSVNFileProperty(weird, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.gcl.GetSVNFileProperty(another, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.gcl.GetSVNFileProperty(third_party, 'svn:mime-type'
|
||||
presubmit.scm.SVN.GetFileProperty(weird, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.scm.SVN.GetFileProperty(another, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.scm.SVN.GetFileProperty(third_party, 'svn:mime-type'
|
||||
).AndReturn(None)
|
||||
presubmit.gcl.ReadFile(blat).AndReturn('whatever\ncookie')
|
||||
presubmit.gcl.ReadFile(another).AndReturn('whatever\ncookie2')
|
||||
presubmit.gclient_utils.FileRead(blat, 'rU'
|
||||
).AndReturn('whatever\ncookie')
|
||||
presubmit.gclient_utils.FileRead(another, 'rU'
|
||||
).AndReturn('whatever\ncookie2')
|
||||
self.mox.ReplayAll()
|
||||
|
||||
change = presubmit.SvnChange('mychange', '\n'.join(description_lines),
|
||||
@@ -754,7 +759,7 @@ class InputApiUnittest(PresubmitTestsBase):
|
||||
item = presubmit.os.path.join(self.fake_root_dir, item)
|
||||
presubmit.os.path.exists(item).AndReturn(True)
|
||||
presubmit.os.path.isdir(item).AndReturn(False)
|
||||
presubmit.gcl.GetSVNFileProperty(item, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.scm.SVN.GetFileProperty(item, 'svn:mime-type').AndReturn(None)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
change = presubmit.SvnChange('mychange', '', self.fake_root_dir, files, 0,
|
||||
@@ -776,7 +781,7 @@ class InputApiUnittest(PresubmitTestsBase):
|
||||
item = presubmit.os.path.join(self.fake_root_dir, item)
|
||||
presubmit.os.path.exists(item).AndReturn(True)
|
||||
presubmit.os.path.isdir(item).AndReturn(False)
|
||||
presubmit.gcl.GetSVNFileProperty(item, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.scm.SVN.GetFileProperty(item, 'svn:mime-type').AndReturn(None)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
change = presubmit.SvnChange('mychange', '', self.fake_root_dir, files, 0,
|
||||
@@ -852,7 +857,7 @@ class InputApiUnittest(PresubmitTestsBase):
|
||||
|
||||
def testReadFileStringAccepted(self):
|
||||
path = presubmit.os.path.join(self.fake_root_dir, 'AA/boo')
|
||||
presubmit.gcl.ReadFile(path, 'x').AndReturn(None)
|
||||
presubmit.gclient_utils.FileRead(path, 'x').AndReturn(None)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
change = presubmit.Change('foo', 'foo', self.fake_root_dir, [('M', 'AA')],
|
||||
@@ -873,7 +878,8 @@ class InputApiUnittest(PresubmitTestsBase):
|
||||
|
||||
def testReadFileAffectedFileAccepted(self):
|
||||
file = presubmit.AffectedFile('AA/boo', 'M', self.fake_root_dir)
|
||||
presubmit.gcl.ReadFile(file.AbsoluteLocalPath(), 'x').AndReturn(None)
|
||||
presubmit.gclient_utils.FileRead(file.AbsoluteLocalPath(), 'x'
|
||||
).AndReturn(None)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
change = presubmit.Change('foo', 'foo', self.fake_root_dir, [('M', 'AA')],
|
||||
@@ -955,8 +961,8 @@ class AffectedFileUnittest(PresubmitTestsBase):
|
||||
path = presubmit.os.path.join('foo', 'blat.cc')
|
||||
presubmit.os.path.exists(path).AndReturn(True)
|
||||
presubmit.os.path.isdir(path).AndReturn(False)
|
||||
presubmit.gcl.ReadFile(path).AndReturn('whatever\ncookie')
|
||||
presubmit.gclient_scm.CaptureSVNInfo(path).AndReturn(
|
||||
presubmit.gclient_utils.FileRead(path, 'rU').AndReturn('whatever\ncookie')
|
||||
presubmit.scm.SVN.CaptureInfo(path).AndReturn(
|
||||
{'URL': 'svn:/foo/foo/blat.cc'})
|
||||
self.mox.ReplayAll()
|
||||
af = presubmit.SvnAffectedFile('foo/blat.cc', 'M')
|
||||
@@ -968,7 +974,7 @@ class AffectedFileUnittest(PresubmitTestsBase):
|
||||
self.failUnless(af.ServerPath() == '')
|
||||
|
||||
def testProperty(self):
|
||||
presubmit.gcl.GetSVNFileProperty('foo.cc', 'svn:secret-property'
|
||||
presubmit.scm.SVN.GetFileProperty('foo.cc', 'svn:secret-property'
|
||||
).AndReturn('secret-property-value')
|
||||
self.mox.ReplayAll()
|
||||
affected_file = presubmit.SvnAffectedFile('foo.cc', 'A')
|
||||
@@ -980,7 +986,7 @@ class AffectedFileUnittest(PresubmitTestsBase):
|
||||
|
||||
def testIsDirectoryNotExists(self):
|
||||
presubmit.os.path.exists('foo.cc').AndReturn(False)
|
||||
presubmit.gclient_scm.CaptureSVNInfo('foo.cc').AndReturn({})
|
||||
presubmit.scm.SVN.CaptureInfo('foo.cc').AndReturn({})
|
||||
self.mox.ReplayAll()
|
||||
affected_file = presubmit.SvnAffectedFile('foo.cc', 'A')
|
||||
# Verify cache coherency.
|
||||
@@ -1006,8 +1012,8 @@ class AffectedFileUnittest(PresubmitTestsBase):
|
||||
presubmit.os.path.isdir(blat).AndReturn(False)
|
||||
presubmit.os.path.exists(blob).AndReturn(True)
|
||||
presubmit.os.path.isdir(blob).AndReturn(False)
|
||||
presubmit.gcl.GetSVNFileProperty(blat, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.gcl.GetSVNFileProperty(blob, 'svn:mime-type'
|
||||
presubmit.scm.SVN.GetFileProperty(blat, 'svn:mime-type').AndReturn(None)
|
||||
presubmit.scm.SVN.GetFileProperty(blob, 'svn:mime-type'
|
||||
).AndReturn('application/octet-stream')
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@@ -1151,10 +1157,10 @@ class CannedChecksUnittest(PresubmitTestsBase):
|
||||
input_api1.AffectedSourceFiles(None).AndReturn(files1)
|
||||
else:
|
||||
input_api1.AffectedFiles(include_deleted=False).AndReturn(files1)
|
||||
presubmit.gcl.GetSVNFileProperty(presubmit.normpath('foo/bar.cc'),
|
||||
property).AndReturn(value1)
|
||||
presubmit.gcl.GetSVNFileProperty(presubmit.normpath('foo.cc'),
|
||||
property).AndReturn(value1)
|
||||
presubmit.scm.SVN.GetFileProperty(presubmit.normpath('foo/bar.cc'),
|
||||
property).AndReturn(value1)
|
||||
presubmit.scm.SVN.GetFileProperty(presubmit.normpath('foo.cc'),
|
||||
property).AndReturn(value1)
|
||||
change2 = presubmit.SvnChange('mychange', '', self.fake_root_dir, [], 0, 0)
|
||||
input_api2 = self.MockInputApi(change2, committing)
|
||||
files2 = [
|
||||
@@ -1166,10 +1172,10 @@ class CannedChecksUnittest(PresubmitTestsBase):
|
||||
else:
|
||||
input_api2.AffectedFiles(include_deleted=False).AndReturn(files2)
|
||||
|
||||
presubmit.gcl.GetSVNFileProperty(presubmit.normpath('foo/bar.cc'),
|
||||
property).AndReturn(value2)
|
||||
presubmit.gcl.GetSVNFileProperty(presubmit.normpath('foo.cc'),
|
||||
property).AndReturn(value2)
|
||||
presubmit.scm.SVN.GetFileProperty(presubmit.normpath('foo/bar.cc'),
|
||||
property).AndReturn(value2)
|
||||
presubmit.scm.SVN.GetFileProperty(presubmit.normpath('foo.cc'),
|
||||
property).AndReturn(value2)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
results1 = check(input_api1, presubmit.OutputApi, None)
|
||||
|
||||
@@ -61,6 +61,7 @@ class RevertMainUnittest(RevertTestsBase):
|
||||
class RevertRevertUnittest(RevertTestsBase):
|
||||
def setUp(self):
|
||||
RevertTestsBase.setUp(self)
|
||||
self.mox.StubOutWithMock(revert.gclient_scm.scm.SVN, 'CaptureStatus')
|
||||
|
||||
def testRevert(self):
|
||||
revert.gcl.GetRepositoryRoot().AndReturn('foo')
|
||||
@@ -73,7 +74,7 @@ class RevertRevertUnittest(RevertTestsBase):
|
||||
}]
|
||||
revert.CaptureSVNLog(['-r', '42', '-v']).AndReturn(entries)
|
||||
revert.GetRepoBase().AndReturn('proto://fqdn/repo/')
|
||||
revert.gclient_scm.CaptureSVNStatus(['random_file']).AndReturn([])
|
||||
revert.gclient_scm.scm.SVN.CaptureStatus(['random_file']).AndReturn([])
|
||||
revert.gcl.RunShell(['svn', 'up', 'random_file'])
|
||||
revert.os.path.isdir('random_file').AndReturn(False)
|
||||
status = """--- Reverse-merging r42 into '.':
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
|
||||
"""Unit tests for scm.py."""
|
||||
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from gclient_test import BaseTestCase
|
||||
import scm
|
||||
from super_mox import mox
|
||||
from super_mox import mox, SuperMoxBaseTestBase
|
||||
|
||||
|
||||
class BaseSCMTestCase(BaseTestCase):
|
||||
@@ -21,17 +24,14 @@ class RootTestCase(BaseSCMTestCase):
|
||||
def testMembersChanged(self):
|
||||
self.mox.ReplayAll()
|
||||
members = [
|
||||
'CaptureGit', 'CaptureGitStatus', 'GIT_COMMAND',
|
||||
'CaptureSVN', 'CaptureSVNHeadRevision', 'CaptureSVNInfo',
|
||||
'CaptureSVNStatus', 'RunSVN', 'RunSVNAndFilterOutput',
|
||||
'RunSVNAndGetFileList', 'SVN_COMMAND',
|
||||
'gclient_utils', 'os', 're', 'subprocess', 'sys', 'xml',
|
||||
'GIT', 'SVN',
|
||||
'gclient_utils', 'os', 're', 'subprocess', 'sys', 'tempfile', 'xml',
|
||||
]
|
||||
# If this test fails, you should add the relevant test.
|
||||
self.compareMembers(scm, members)
|
||||
|
||||
|
||||
class GitWrapperTestCase(BaseSCMTestCase):
|
||||
class GitWrapperTestCase(SuperMoxBaseTestBase):
|
||||
sample_git_import = """blob
|
||||
mark :1
|
||||
data 6
|
||||
@@ -80,30 +80,44 @@ from :3
|
||||
|
||||
def CreateGitRepo(self, git_import, path):
|
||||
try:
|
||||
subprocess.Popen(['git', 'init'], stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, cwd=path).communicate()
|
||||
except WindowsError:
|
||||
scm.subprocess.Popen(['git', 'init'],
|
||||
stdout=scm.subprocess.PIPE,
|
||||
stderr=scm.subprocess.STDOUT,
|
||||
cwd=path).communicate()
|
||||
except OSError:
|
||||
# git is not available, skip this test.
|
||||
return False
|
||||
subprocess.Popen(['git', 'fast-import'], stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
cwd=path).communicate(input=git_import)
|
||||
subprocess.Popen(['git', 'checkout'], stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, cwd=path).communicate()
|
||||
scm.subprocess.Popen(['git', 'fast-import'],
|
||||
stdin=scm.subprocess.PIPE,
|
||||
stdout=scm.subprocess.PIPE,
|
||||
stderr=scm.subprocess.STDOUT,
|
||||
cwd=path).communicate(input=git_import)
|
||||
scm.subprocess.Popen(['git', 'checkout'],
|
||||
stdout=scm.subprocess.PIPE,
|
||||
stderr=scm.subprocess.STDOUT,
|
||||
cwd=path).communicate()
|
||||
return True
|
||||
|
||||
def setUp(self):
|
||||
BaseSCMTestCase.setUp(self)
|
||||
SuperMoxBaseTestBase.setUp(self)
|
||||
self.args = self.Args()
|
||||
self.url = 'git://foo'
|
||||
self.root_dir = tempfile.mkdtemp()
|
||||
self.relpath = '.'
|
||||
self.base_path = os.path.join(self.root_dir, self.relpath)
|
||||
self.base_path = scm.os.path.join(self.root_dir, self.relpath)
|
||||
self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.root_dir)
|
||||
gclient_test.BaseTestCase.tearDown(self)
|
||||
SuperMoxBaseTestBase.tearDown(self)
|
||||
|
||||
def testMembersChanged(self):
|
||||
self.mox.ReplayAll()
|
||||
members = [
|
||||
'COMMAND', 'Capture', 'CaptureStatus',
|
||||
]
|
||||
# If this test fails, you should add the relevant test.
|
||||
self.compareMembers(scm.GIT, members)
|
||||
|
||||
|
||||
class SVNTestCase(BaseSCMTestCase):
|
||||
@@ -114,7 +128,17 @@ class SVNTestCase(BaseSCMTestCase):
|
||||
self.url = self.Url()
|
||||
self.relpath = 'asf'
|
||||
|
||||
def testGetSVNFileInfo(self):
|
||||
def testMembersChanged(self):
|
||||
self.mox.ReplayAll()
|
||||
members = [
|
||||
'COMMAND', 'Capture', 'CaptureHeadRevision', 'CaptureInfo',
|
||||
'CaptureStatus', 'DiffItem', 'GetFileProperty', 'IsMoved', 'Run',
|
||||
'RunAndFilterOutput', 'RunAndGetFileList',
|
||||
]
|
||||
# If this test fails, you should add the relevant test.
|
||||
self.compareMembers(scm.SVN, members)
|
||||
|
||||
def testGetFileInfo(self):
|
||||
xml_text = r"""<?xml version="1.0"?>
|
||||
<info>
|
||||
<entry kind="file" path="%s" revision="14628">
|
||||
@@ -130,8 +154,8 @@ class SVNTestCase(BaseSCMTestCase):
|
||||
</entry>
|
||||
</info>
|
||||
""" % self.url
|
||||
self.mox.StubOutWithMock(scm, 'CaptureSVN')
|
||||
scm.CaptureSVN(['info', '--xml', self.url], '.', True).AndReturn(xml_text)
|
||||
self.mox.StubOutWithMock(scm.SVN, 'Capture')
|
||||
scm.SVN.Capture(['info', '--xml', self.url], '.', True).AndReturn(xml_text)
|
||||
expected = {
|
||||
'URL': 'http://src.chromium.org/svn/trunk/src/chrome/app/d',
|
||||
'UUID': None,
|
||||
@@ -145,10 +169,10 @@ class SVNTestCase(BaseSCMTestCase):
|
||||
'Node Kind': 'file',
|
||||
}
|
||||
self.mox.ReplayAll()
|
||||
file_info = scm.CaptureSVNInfo(self.url, '.', True)
|
||||
file_info = scm.SVN.CaptureInfo(self.url, '.', True)
|
||||
self.assertEquals(sorted(file_info.items()), sorted(expected.items()))
|
||||
|
||||
def testCaptureSvnInfo(self):
|
||||
def testCaptureInfo(self):
|
||||
xml_text = """<?xml version="1.0"?>
|
||||
<info>
|
||||
<entry
|
||||
@@ -172,10 +196,10 @@ class SVNTestCase(BaseSCMTestCase):
|
||||
</entry>
|
||||
</info>
|
||||
""" % (self.url, self.root_dir)
|
||||
self.mox.StubOutWithMock(scm, 'CaptureSVN')
|
||||
scm.CaptureSVN(['info', '--xml', self.url], '.', True).AndReturn(xml_text)
|
||||
self.mox.StubOutWithMock(scm.SVN, 'Capture')
|
||||
scm.SVN.Capture(['info', '--xml', self.url], '.', True).AndReturn(xml_text)
|
||||
self.mox.ReplayAll()
|
||||
file_info = scm.CaptureSVNInfo(self.url, '.', True)
|
||||
file_info = scm.SVN.CaptureInfo(self.url, '.', True)
|
||||
expected = {
|
||||
'URL': self.url,
|
||||
'UUID': '7b9385f5-0452-0410-af26-ad4892b7a1fb',
|
||||
@@ -185,11 +209,11 @@ class SVNTestCase(BaseSCMTestCase):
|
||||
'Copied From URL': None,
|
||||
'Copied From Rev': None,
|
||||
'Path': '.',
|
||||
'Node Kind': 'dir',
|
||||
'Node Kind': 'directory',
|
||||
}
|
||||
self.assertEqual(file_info, expected)
|
||||
|
||||
def testCaptureSVNStatus(self):
|
||||
def testCaptureStatus(self):
|
||||
text =r"""<?xml version="1.0"?>
|
||||
<status>
|
||||
<target path=".">
|
||||
@@ -236,7 +260,7 @@ class SVNTestCase(BaseSCMTestCase):
|
||||
proc.communicate().AndReturn((text, 0))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
info = scm.CaptureSVNStatus('.')
|
||||
info = scm.SVN.CaptureStatus('.')
|
||||
expected = [
|
||||
('? ', 'unversionned_file.txt'),
|
||||
('M ', 'build\\internal\\essential.vsprops'),
|
||||
@@ -246,14 +270,14 @@ class SVNTestCase(BaseSCMTestCase):
|
||||
]
|
||||
self.assertEquals(sorted(info), sorted(expected))
|
||||
|
||||
def testRunSVN(self):
|
||||
def testRun(self):
|
||||
param2 = 'bleh'
|
||||
scm.gclient_utils.SubprocessCall(['svn', 'foo', 'bar'],
|
||||
param2).AndReturn(None)
|
||||
self.mox.ReplayAll()
|
||||
scm.RunSVN(['foo', 'bar'], param2)
|
||||
scm.SVN.Run(['foo', 'bar'], param2)
|
||||
|
||||
def testCaptureSVNStatusEmpty(self):
|
||||
def testCaptureStatusEmpty(self):
|
||||
text = r"""<?xml version="1.0"?>
|
||||
<status>
|
||||
<target
|
||||
@@ -268,7 +292,7 @@ class SVNTestCase(BaseSCMTestCase):
|
||||
stdout=scm.subprocess.PIPE).AndReturn(proc)
|
||||
proc.communicate().AndReturn((text, 0))
|
||||
self.mox.ReplayAll()
|
||||
info = scm.CaptureSVNStatus(None)
|
||||
info = scm.SVN.CaptureStatus(None)
|
||||
self.assertEquals(info, [])
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class SuperMoxBaseTestBase(mox.MoxTestBase):
|
||||
if actual_members != expected_members:
|
||||
diff = ([i for i in actual_members if i not in expected_members] +
|
||||
[i for i in expected_members if i not in actual_members])
|
||||
print diff
|
||||
print>>sys.stderr, diff
|
||||
self.assertEqual(actual_members, expected_members)
|
||||
|
||||
def UnMock(self, object, name):
|
||||
|
||||
@@ -25,9 +25,9 @@ class TryChangeUnittest(TryChangeTestsBase):
|
||||
'GetTryServerSettings', 'GuessVCS',
|
||||
'HELP_STRING', 'InvalidScript', 'NoTryServerAccess', 'PathDifference',
|
||||
'RunCommand', 'SCM', 'SVN', 'TryChange', 'USAGE',
|
||||
'datetime', 'gcl', 'gclient_scm', 'getpass', 'logging',
|
||||
'optparse', 'os', 'presubmit_support', 'shutil', 'socket', 'subprocess',
|
||||
'sys', 'tempfile', 'upload', 'urllib',
|
||||
'datetime', 'gcl', 'getpass', 'logging',
|
||||
'optparse', 'os', 'presubmit_support', 'scm', 'shutil', 'socket',
|
||||
'subprocess', 'sys', 'tempfile', 'upload', 'urllib',
|
||||
]
|
||||
# If this test fails, you should add the relevant test.
|
||||
self.compareMembers(trychange, members)
|
||||
|
||||
51
trychange.py
51
trychange.py
@@ -21,11 +21,11 @@ import tempfile
|
||||
import urllib
|
||||
|
||||
import gcl
|
||||
import gclient_scm
|
||||
import scm
|
||||
import presubmit_support
|
||||
import upload
|
||||
|
||||
__version__ = '1.1.1'
|
||||
__version__ = '1.1.2'
|
||||
|
||||
|
||||
# Constants
|
||||
@@ -150,50 +150,8 @@ class SVN(SCM):
|
||||
else:
|
||||
os.chdir(root)
|
||||
|
||||
diff = []
|
||||
for filename in files:
|
||||
# Use svn info output instead of os.path.isdir because the latter fails
|
||||
# when the file is deleted.
|
||||
if gclient_scm.CaptureSVNInfo(filename).get("Node Kind") in (
|
||||
"dir", "directory"):
|
||||
continue
|
||||
# If the user specified a custom diff command in their svn config file,
|
||||
# then it'll be used when we do svn diff, which we don't want to happen
|
||||
# since we want the unified diff. Using --diff-cmd=diff doesn't always
|
||||
# work, since they can have another diff executable in their path that
|
||||
# gives different line endings. So we use a bogus temp directory as the
|
||||
# config directory, which gets around these problems.
|
||||
if sys.platform.startswith("win"):
|
||||
parent_dir = tempfile.gettempdir()
|
||||
else:
|
||||
parent_dir = sys.path[0] # tempdir is not secure.
|
||||
bogus_dir = os.path.join(parent_dir, "temp_svn_config")
|
||||
if not os.path.exists(bogus_dir):
|
||||
os.mkdir(bogus_dir)
|
||||
# Grabs the diff data.
|
||||
data = gcl.RunShell(["svn", "diff", "--config-dir", bogus_dir, filename])
|
||||
|
||||
# We know the diff will be incorrectly formatted. Fix it.
|
||||
if gcl.IsSVNMoved(filename):
|
||||
# The file is "new" in the patch sense. Generate a homebrew diff.
|
||||
# We can't use ReadFile() since it's not using binary mode.
|
||||
file_handle = open(filename, 'rb')
|
||||
file_content = file_handle.read()
|
||||
file_handle.close()
|
||||
# Prepend '+' to every lines.
|
||||
file_content = ['+' + i for i in file_content.splitlines(True)]
|
||||
nb_lines = len(file_content)
|
||||
# We need to use / since patch on unix will fail otherwise.
|
||||
filename = filename.replace('\\', '/')
|
||||
data = "Index: %s\n" % filename
|
||||
data += ("============================================================="
|
||||
"======\n")
|
||||
# Note: Should we use /dev/null instead?
|
||||
data += "--- %s\n" % filename
|
||||
data += "+++ %s\n" % filename
|
||||
data += "@@ -0,0 +1,%d @@\n" % nb_lines
|
||||
data += ''.join(file_content)
|
||||
diff.append(data)
|
||||
# Directories will return None so filter them out.
|
||||
diff = filter(None, [scm.SVN.Diff(f) for f in files])
|
||||
os.chdir(previous_cwd)
|
||||
return "".join(diff)
|
||||
|
||||
@@ -407,6 +365,7 @@ def GuessVCS(options):
|
||||
Returns:
|
||||
A SCM instance. Exits if the SCM can't be guessed.
|
||||
"""
|
||||
__pychecker__ = 'no-returnvalues'
|
||||
# Subversion has a .svn in all working directories.
|
||||
if os.path.isdir('.svn'):
|
||||
logging.info("Guessed VCS = Subversion")
|
||||
|
||||
Reference in New Issue
Block a user