Commit 41e3a6c5 authored by agable's avatar agable Committed by Commit bot

Remove SVN support from gclient_utils and gclient_scm

This removes SVN support (most notably the SVNWrapper class, and the git-svn
logic in GitWrapper.GetUsableRev) from gclient_scm. It also removes some
references to SVN from comments in gclient_utils.

R=maruel@chromium.org
BUG=641588

Review-Url: https://chromiumcodereview.appspot.com/2393773003
parent bc0b4c6e
......@@ -37,14 +37,14 @@ class NoUsableRevError(gclient_utils.Error):
class DiffFiltererWrapper(object):
"""Simple base class which tracks which file is being diffed and
replaces instances of its file name in the original and
working copy lines of the svn/git diff output."""
working copy lines of the git diff output."""
index_string = None
original_prefix = "--- "
working_prefix = "+++ "
def __init__(self, relpath, print_func):
# Note that we always use '/' as the path separator to be
# consistent with svn's cygwin-style output on Windows
# consistent with cygwin-style output on Windows
self._relpath = relpath.replace("\\", "/")
self._current_file = None
self._print_func = print_func
......@@ -70,10 +70,6 @@ class DiffFiltererWrapper(object):
self._print_func(line)
class SvnDiffFilterer(DiffFiltererWrapper):
index_string = "Index: "
class GitDiffFilterer(DiffFiltererWrapper):
index_string = "diff --git "
......@@ -90,26 +86,20 @@ class GitDiffFilterer(DiffFiltererWrapper):
# Factory Method for SCM wrapper creation
def GetScmName(url):
if url:
if not url:
return None
url, _ = gclient_utils.SplitUrlRevision(url)
if (url.startswith('git://') or url.startswith('ssh://') or
url.startswith('git+http://') or url.startswith('git+https://') or
url.endswith('.git') or url.startswith('sso://') or
'googlesource' in url):
return 'git'
elif (url.startswith('http://') or url.startswith('https://') or
url.startswith('svn://') or url.startswith('svn+ssh://')):
return 'svn'
elif url.startswith('file://'):
if url.endswith('.git'):
return 'git'
return 'svn'
protocol = url.split('://')[0]
if protocol in (
'file', 'git', 'git+http', 'git+https', 'http', 'https', 'ssh', 'sso'):
return 'git'
return None
def CreateSCM(url, root_dir=None, relpath=None, out_fh=None, out_cb=None):
SCM_MAP = {
'svn' : SVNWrapper,
'git' : GitWrapper,
}
......@@ -192,10 +182,6 @@ class SCMWrapper(object):
actual_remote_url.replace('\\', '/')):
actual_remote_url = self._get_first_remote_url(mirror.mirror_path)
return actual_remote_url
# Svn
if os.path.exists(os.path.join(self.checkout_path, '.svn')):
return scm.SVN.CaptureLocalInfo([], self.checkout_path)['URL']
return None
def DoesRemoteURLMatch(self, options):
......@@ -210,7 +196,7 @@ class SCMWrapper(object):
== gclient_utils.SplitUrlRevision(self.url)[0].rstrip('/'))
else:
# This may occur if the self.checkout_path exists but does not contain a
# valid git or svn checkout.
# valid git checkout.
return False
def _DeleteOrMove(self, force):
......@@ -503,7 +489,7 @@ class GitWrapper(SCMWrapper):
# 0) HEAD is detached. Probably from our initial clone.
# - make sure HEAD is contained by a named ref, then update.
# Cases 1-4. HEAD is a branch.
# 1) current branch is not tracking a remote branch (could be git-svn)
# 1) current branch is not tracking a remote branch
# - try to rebase onto the new hash or branch
# 2) current branch is tracking a remote branch with local committed
# changes, but the DEPS file switched to point to a hash
......@@ -573,13 +559,6 @@ class GitWrapper(SCMWrapper):
self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False)
elif current_type == 'hash':
# case 1
if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None:
# Our git-svn branch (upstream_branch) is our upstream
self._AttemptRebase(upstream_branch, files, options,
newbase=revision, printed_path=printed_path,
merge=options.merge)
printed_path = True
else:
# Can't find a merge-base since we don't know our upstream. That makes
# this command VERY likely to produce a rebase failure. For now we
# assume origin is our upstream since that's what the old behavior was.
......@@ -788,16 +767,13 @@ class GitWrapper(SCMWrapper):
except subprocess2.CalledProcessError:
merge_base = []
self._Run(['diff', '--name-status'] + merge_base, options,
stdout=self.out_fh)
stdout=self.out_fh, always=options.verbose)
if file_list is not None:
files = self._Capture(['diff', '--name-only'] + merge_base).split()
file_list.extend([os.path.join(self.checkout_path, f) for f in files])
def GetUsableRev(self, rev, options):
"""Finds a useful revision for this repository.
If SCM is git-svn and the head revision is less than |rev|, git svn fetch
will be called on the source."""
"""Finds a useful revision for this repository."""
sha1 = None
if not os.path.isdir(self.checkout_path):
raise NoUsableRevError(
......@@ -807,39 +783,7 @@ class GitWrapper(SCMWrapper):
'For more info, see: '
'http://code.google.com/p/chromium/wiki/UsingNewGit'
'#Initial_checkout' ) % rev)
elif rev.isdigit() and len(rev) < 7:
# Handles an SVN rev. As an optimization, only verify an SVN revision as
# [0-9]{1,6} for now to avoid making a network request.
if scm.GIT.IsGitSvn(cwd=self.checkout_path):
local_head = scm.GIT.GetGitSvnHeadRev(cwd=self.checkout_path)
if not local_head or local_head < int(rev):
try:
logging.debug('Looking for git-svn configuration optimizations.')
if scm.GIT.Capture(['config', '--get', 'svn-remote.svn.fetch'],
cwd=self.checkout_path):
self._Fetch(options)
except subprocess2.CalledProcessError:
logging.debug('git config --get svn-remote.svn.fetch failed, '
'ignoring possible optimization.')
if options.verbose:
self.Print('Running git svn fetch. This might take a while.\n')
scm.GIT.Capture(['svn', 'fetch'], cwd=self.checkout_path)
try:
sha1 = scm.GIT.GetBlessedSha1ForSvnRev(
cwd=self.checkout_path, rev=rev)
except gclient_utils.Error, e:
sha1 = e.message
self.Print('Warning: Could not find a git revision with accurate\n'
'.DEPS.git that maps to SVN revision %s. Sync-ing to\n'
'the closest sane git revision, which is:\n'
' %s\n' % (rev, e.message))
if not sha1:
raise NoUsableRevError(
( 'It appears that either your git-svn remote is incorrectly\n'
'configured or the revision in your safesync_url is\n'
'higher than git-svn remote\'s HEAD as we couldn\'t find a\n'
'corresponding git hash for SVN rev %s.' ) % rev)
else:
if scm.GIT.IsValidRevision(cwd=self.checkout_path, rev=rev):
sha1 = rev
else:
......@@ -851,12 +795,11 @@ class GitWrapper(SCMWrapper):
if not sha1:
raise NoUsableRevError(
( 'We could not find a valid hash for safesync_url response "%s".\n'
'Safesync URLs with a git checkout currently require a git-svn\n'
'remote or a safesync_url that provides git sha1s. Please add a\n'
'git-svn remote or change your safesync_url. For more info, see:\n'
'http://code.google.com/p/chromium/wiki/UsingNewGit'
'#Initial_checkout' ) % rev)
('We could not find a valid hash for safesync_url response "%s".\n'
'Please ensure that your safesync_url provides git sha1 hashes.\n'
'For more info, see:\n'
'http://code.google.com/p/chromium/wiki/UsingNewGit#Initial_checkout'
) % rev)
return sha1
......@@ -1239,480 +1182,3 @@ class GitWrapper(SCMWrapper):
gclient_utils.CheckCallAndFilterAndHeader(cmd, env=env, **kwargs)
else:
gclient_utils.CheckCallAndFilter(cmd, env=env, **kwargs)
class SVNWrapper(SCMWrapper):
""" Wrapper for SVN """
name = 'svn'
_PRINTED_DEPRECATION = False
_MESSAGE = (
'Oh hai! You are using subversion. Chrome infra is eager to get rid of',
'svn support so please switch to git.',
'Tracking bug: http://crbug.com/475320',
'If you are a project owner, you may request git migration assistance at: ',
' https://code.google.com/p/chromium/issues/entry?template=Infra-Git')
def __init__(self, *args, **kwargs):
super(SVNWrapper, self).__init__(*args, **kwargs)
suppress_deprecated_notice = os.environ.get(
'SUPPRESS_DEPRECATED_SVN_NOTICE', False)
if not SVNWrapper._PRINTED_DEPRECATION and not suppress_deprecated_notice:
SVNWrapper._PRINTED_DEPRECATION = True
sys.stderr.write('\n'.join(self._MESSAGE) + '\n')
@staticmethod
def BinaryExists():
"""Returns true if the command exists."""
try:
result, version = scm.SVN.AssertVersion('1.4')
if not result:
raise gclient_utils.Error('SVN version is older than 1.4: %s' % version)
return result
except OSError:
return False
def GetCheckoutRoot(self):
return scm.SVN.GetCheckoutRoot(self.checkout_path)
def GetRevisionDate(self, revision):
"""Returns the given revision's date in ISO-8601 format (which contains the
time zone)."""
date = scm.SVN.Capture(
['propget', '--revprop', 'svn:date', '-r', revision],
os.path.join(self.checkout_path, '.'))
return date.strip()
def cleanup(self, options, args, _file_list):
"""Cleanup working copy."""
self._Run(['cleanup'] + args, options)
def diff(self, options, args, _file_list):
# NOTE: This function does not currently modify file_list.
if not os.path.isdir(self.checkout_path):
raise gclient_utils.Error('Directory %s is not present.' %
self.checkout_path)
self._Run(['diff'] + args, options)
def pack(self, _options, args, _file_list):
"""Generates a patch file which can be applied to the root of the
repository."""
if not os.path.isdir(self.checkout_path):
raise gclient_utils.Error('Directory %s is not present.' %
self.checkout_path)
gclient_utils.CheckCallAndFilter(
['svn', 'diff', '-x', '--ignore-eol-style'] + args,
cwd=self.checkout_path,
print_stdout=False,
filter_fn=SvnDiffFilterer(self.relpath, print_func=self.Print).Filter)
def update(self, options, args, file_list):
"""Runs svn to update or transparently checkout the working copy.
All updated files will be appended to file_list.
Raises:
Error: if can't get URL for relative path.
"""
# Only update if hg is not controlling the directory.
hg_path = os.path.join(self.checkout_path, '.hg')
if os.path.exists(hg_path):
self.Print('________ found .hg directory; skipping %s' % self.relpath)
return
if args:
raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args))
# revision is the revision to match. It is None if no revision is specified,
# i.e. the 'deps ain't pinned'.
url, revision = gclient_utils.SplitUrlRevision(self.url)
# Keep the original unpinned url for reference in case the repo is switched.
base_url = url
managed = True
if options.revision:
# Override the revision number.
revision = str(options.revision)
if revision:
if revision != 'unmanaged':
forced_revision = True
# Reconstruct the url.
url = '%s@%s' % (url, revision)
rev_str = ' at %s' % revision
else:
managed = False
revision = None
else:
forced_revision = False
rev_str = ''
exists = os.path.exists(self.checkout_path)
if exists and managed:
# Git is only okay if it's a git-svn checkout of the right repo.
if scm.GIT.IsGitSvn(self.checkout_path):
remote_url = scm.GIT.Capture(['config', '--local', '--get',
'svn-remote.svn.url'],
cwd=self.checkout_path).rstrip()
if remote_url.rstrip('/') == base_url.rstrip('/'):
self.Print('\n_____ %s looks like a git-svn checkout. Skipping.'
% self.relpath)
return # TODO(borenet): Get the svn revision number?
# Get the existing scm url and the revision number of the current checkout.
if exists and managed:
try:
from_info = scm.SVN.CaptureLocalInfo(
[], os.path.join(self.checkout_path, '.'))
except (gclient_utils.Error, subprocess2.CalledProcessError):
self._DeleteOrMove(options.force)
exists = False
BASE_URLS = {
'/chrome/trunk/src': 'gs://chromium-svn-checkout/chrome/',
'/blink/trunk': 'gs://chromium-svn-checkout/blink/',
}
WHITELISTED_ROOTS = [
'svn://svn.chromium.org',
'svn://svn-mirror.golo.chromium.org',
]
if not exists:
try:
# Split out the revision number since it's not useful for us.
base_path = urlparse.urlparse(url).path.split('@')[0]
# Check to see if we're on a whitelisted root. We do this because
# only some svn servers have matching UUIDs.
local_parsed = urlparse.urlparse(url)
local_root = '%s://%s' % (local_parsed.scheme, local_parsed.netloc)
if ('CHROME_HEADLESS' in os.environ
and sys.platform == 'linux2' # TODO(hinoka): Enable for win/mac.
and base_path in BASE_URLS
and local_root in WHITELISTED_ROOTS):
# Use a tarball for initial sync if we are on a bot.
# Get an unauthenticated gsutil instance.
gsutil = download_from_google_storage.Gsutil(
GSUTIL_DEFAULT_PATH, boto_path=os.devnull)
gs_path = BASE_URLS[base_path]
_, out, _ = gsutil.check_call('ls', gs_path)
# So that we can get the most recent revision.
sorted_items = sorted(out.splitlines())
latest_checkout = sorted_items[-1]
tempdir = tempfile.mkdtemp()
self.Print('Downloading %s...' % latest_checkout)
code, out, err = gsutil.check_call('cp', latest_checkout, tempdir)
if code:
self.Print('%s\n%s' % (out, err))
raise Exception()
filename = latest_checkout.split('/')[-1]
tarball = os.path.join(tempdir, filename)
self.Print('Unpacking into %s...' % self.checkout_path)
gclient_utils.safe_makedirs(self.checkout_path)
# TODO(hinoka): Use 7z for windows.
cmd = ['tar', '--extract', '--ungzip',
'--directory', self.checkout_path,
'--file', tarball]
gclient_utils.CheckCallAndFilter(
cmd, stdout=sys.stdout, print_stdout=True)
self.Print('Deleting temp file')
gclient_utils.rmtree(tempdir)
# Rewrite the repository root to match.
tarball_url = scm.SVN.CaptureLocalInfo(
['.'], self.checkout_path)['Repository Root']
tarball_parsed = urlparse.urlparse(tarball_url)
tarball_root = '%s://%s' % (tarball_parsed.scheme,
tarball_parsed.netloc)
if tarball_root != local_root:
self.Print('Switching repository root to %s' % local_root)
self._Run(['switch', '--relocate', tarball_root,
local_root, self.checkout_path],
options)
except Exception as e:
self.Print('We tried to get a source tarball but failed.')
self.Print('Resuming normal operations.')
self.Print(str(e))
gclient_utils.safe_makedirs(os.path.dirname(self.checkout_path))
# We need to checkout.
command = ['checkout', url, self.checkout_path]
command = self._AddAdditionalUpdateFlags(command, options, revision)
self._RunAndGetFileList(command, options, file_list, self._root_dir)
return self.Svnversion()
if not managed:
self.Print(('________ unmanaged solution; skipping %s' % self.relpath))
if os.path.exists(os.path.join(self.checkout_path, '.svn')):
return self.Svnversion()
return
if 'URL' not in from_info:
raise gclient_utils.Error(
('gclient is confused. Couldn\'t get the url for %s.\n'
'Try using @unmanaged.\n%s') % (
self.checkout_path, from_info))
# Look for locked directories.
dir_info = scm.SVN.CaptureStatus(
None, os.path.join(self.checkout_path, '.'))
if any(d[0][2] == 'L' for d in dir_info):
try:
self._Run(['cleanup', self.checkout_path], options)
except subprocess2.CalledProcessError, e:
# Get the status again, svn cleanup may have cleaned up at least
# something.
dir_info = scm.SVN.CaptureStatus(
None, os.path.join(self.checkout_path, '.'))
# Try to fix the failures by removing troublesome files.
for d in dir_info:
if d[0][2] == 'L':
if d[0][0] == '!' and options.force:
# We don't pass any files/directories to CaptureStatus and set
# cwd=self.checkout_path, so we should get relative paths here.
assert not os.path.isabs(d[1])
path_to_remove = os.path.normpath(
os.path.join(self.checkout_path, d[1]))
self.Print('Removing troublesome path %s' % path_to_remove)
gclient_utils.rmtree(path_to_remove)
else:
self.Print(
'Not removing troublesome path %s automatically.' % d[1])
if d[0][0] == '!':
self.Print('You can pass --force to enable automatic removal.')
raise e
if from_info['URL'].rstrip('/') != base_url.rstrip('/'):
# The repository url changed, need to switch.
try:
to_info = scm.SVN.CaptureRemoteInfo(url)
except (gclient_utils.Error, subprocess2.CalledProcessError):
# The url is invalid or the server is not accessible, it's safer to bail
# out right now.
raise gclient_utils.Error('This url is unreachable: %s' % url)
can_switch = ((from_info['Repository Root'] != to_info['Repository Root'])
and (from_info['UUID'] == to_info['UUID']))
if can_switch:
self.Print('_____ relocating %s to a new checkout' % self.relpath)
# We have different roots, so check if we can switch --relocate.
# Subversion only permits this if the repository UUIDs match.
# Perform the switch --relocate, then rewrite the from_url
# to reflect where we "are now." (This is the same way that
# Subversion itself handles the metadata when switch --relocate
# is used.) This makes the checks below for whether we
# can update to a revision or have to switch to a different
# branch work as expected.
# TODO(maruel): TEST ME !
command = ['switch', '--relocate',
from_info['Repository Root'],
to_info['Repository Root'],
self.relpath]
self._Run(command, options, cwd=self._root_dir)
from_info['URL'] = from_info['URL'].replace(
from_info['Repository Root'],
to_info['Repository Root'])
else:
if not options.force and not options.reset:
# Look for local modifications but ignore unversioned files.
for status in scm.SVN.CaptureStatus(None, self.checkout_path):
if status[0][0] != '?':
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 '
'try again.') % (url, self.checkout_path))
# Ok delete it.
self.Print('_____ switching %s to a new checkout' % self.relpath)
gclient_utils.rmtree(self.checkout_path)
# We need to checkout.
command = ['checkout', url, self.checkout_path]
command = self._AddAdditionalUpdateFlags(command, options, revision)
self._RunAndGetFileList(command, options, file_list, self._root_dir)
return self.Svnversion()
# If the provided url has a revision number that matches the revision
# number of the existing directory, then we don't need to bother updating.
if not options.force and str(from_info['Revision']) == revision:
if options.verbose or not forced_revision:
self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False)
else:
command = ['update', self.checkout_path]
command = self._AddAdditionalUpdateFlags(command, options, revision)
self._RunAndGetFileList(command, options, file_list, self._root_dir)
# If --reset and --delete_unversioned_trees are specified, remove any
# untracked files and directories.
if options.reset and options.delete_unversioned_trees:
for status in scm.SVN.CaptureStatus(None, self.checkout_path):
full_path = os.path.join(self.checkout_path, status[1])
if (status[0][0] == '?'
and os.path.isdir(full_path)
and not os.path.islink(full_path)):
self.Print('_____ removing unversioned directory %s' % status[1])
gclient_utils.rmtree(full_path)
return self.Svnversion()
def updatesingle(self, options, args, file_list):
filename = args.pop()
if scm.SVN.AssertVersion("1.5")[0]:
if not os.path.exists(os.path.join(self.checkout_path, '.svn')):
# Create an empty checkout and then update the one file we want. Future
# operations will only apply to the one file we checked out.
command = ["checkout", "--depth", "empty", self.url, self.checkout_path]
self._Run(command, options, cwd=self._root_dir)
if os.path.exists(os.path.join(self.checkout_path, filename)):
os.remove(os.path.join(self.checkout_path, filename))
command = ["update", filename]
self._RunAndGetFileList(command, options, file_list)
# After the initial checkout, we can use update as if it were any other
# dep.
self.update(options, args, file_list)
else:
# If the installed version of SVN doesn't support --depth, fallback to
# just exporting the file. This has the downside that revision
# information is not stored next to the file, so we will have to
# re-export the file every time we sync.
if not os.path.exists(self.checkout_path):
gclient_utils.safe_makedirs(self.checkout_path)
command = ["export", os.path.join(self.url, filename),
os.path.join(self.checkout_path, filename)]
command = self._AddAdditionalUpdateFlags(command, options,
options.revision)
self._Run(command, options, cwd=self._root_dir)
def revert(self, options, _args, file_list):
"""Reverts local modifications. Subversion specific.
All reverted files will be appended to file_list, even if Subversion
doesn't know about them.
"""
if not os.path.isdir(self.checkout_path):
if os.path.exists(self.checkout_path):
gclient_utils.rmtree(self.checkout_path)
# svn revert won't work if the directory doesn't exist. It needs to
# checkout instead.
self.Print('_____ %s is missing, synching instead' % self.relpath)
# Don't reuse the args.
return self.update(options, [], file_list)
if not os.path.isdir(os.path.join(self.checkout_path, '.svn')):
if os.path.isdir(os.path.join(self.checkout_path, '.git')):
self.Print('________ found .git directory; skipping %s' % self.relpath)
return
if os.path.isdir(os.path.join(self.checkout_path, '.hg')):
self.Print('________ found .hg directory; skipping %s' % self.relpath)
return
if not options.force:
raise gclient_utils.Error('Invalid checkout path, aborting')
self.Print(
'\n_____ %s is not a valid svn checkout, synching instead' %
self.relpath)
gclient_utils.rmtree(self.checkout_path)
# Don't reuse the args.
return self.update(options, [], file_list)
def printcb(file_status):
if file_list is not None:
file_list.append(file_status[1])
if logging.getLogger().isEnabledFor(logging.INFO):
logging.info('%s%s' % (file_status[0], file_status[1]))
else:
self.Print(os.path.join(self.checkout_path, file_status[1]))
scm.SVN.Revert(self.checkout_path, callback=printcb)
# Revert() may delete the directory altogether.
if not os.path.isdir(self.checkout_path):
# Don't reuse the args.
return self.update(options, [], file_list)
try:
# svn revert is so broken we don't even use it. Using
# "svn up --revision BASE" achieve the same effect.
# file_list will contain duplicates.
self._RunAndGetFileList(['update', '--revision', 'BASE'], options,
file_list)
except OSError, e:
# Maybe the directory disappeared meanwhile. Do not throw an exception.
logging.error('Failed to update:\n%s' % str(e))
def revinfo(self, _options, _args, _file_list):
"""Display revision"""
try:
return scm.SVN.CaptureRevision(self.checkout_path)
except (gclient_utils.Error, subprocess2.CalledProcessError):
return None
def runhooks(self, options, args, file_list):
self.status(options, args, file_list)
def status(self, options, args, file_list):
"""Display status information."""
command = ['status'] + args
if not os.path.isdir(self.checkout_path):
# svn status won't work if the directory doesn't exist.
self.Print(('\n________ couldn\'t run \'%s\' in \'%s\':\n'
'The directory does not exist.') %
(' '.join(command), self.checkout_path))
# There's no file list to retrieve.
else:
self._RunAndGetFileList(command, options, file_list)
def GetUsableRev(self, rev, _options):
"""Verifies the validity of the revision for this repository."""
if not scm.SVN.IsValidRevision(url='%s@%s' % (self.url, rev)):
raise NoUsableRevError(
( '%s isn\'t a valid revision. Please check that your safesync_url is\n'
'correct.') % rev)
return rev
def FullUrlForRelativeUrl(self, url):
# Find the forth '/' and strip from there. A bit hackish.
return '/'.join(self.url.split('/')[:4]) + url
def _Run(self, args, options, **kwargs):
"""Runs a commands that goes to stdout."""
kwargs.setdefault('cwd', self.checkout_path)
gclient_utils.CheckCallAndFilterAndHeader(['svn'] + args,
always=options.verbose, **kwargs)
def Svnversion(self):
"""Runs the lowest checked out revision in the current project."""
info = scm.SVN.CaptureLocalInfo([], os.path.join(self.checkout_path, '.'))
return info['Revision']
def _RunAndGetFileList(self, args, options, file_list, cwd=None):
"""Runs a commands that goes to stdout and grabs the file listed."""
cwd = cwd or self.checkout_path
scm.SVN.RunAndGetFileList(
options.verbose,
args + ['--ignore-externals'],
cwd=cwd,
file_list=file_list)
@staticmethod
def _AddAdditionalUpdateFlags(command, options, revision):
"""Add additional flags to command depending on what options are set.
command should be a list of strings that represents an svn command.
This method returns a new list to be used as a command."""
new_command = command[:]
if revision:
new_command.extend(['--revision', str(revision).strip()])
# We don't want interaction when jobs are used.
if options.jobs > 1:
new_command.append('--non-interactive')
# --force was added to 'svn update' in svn 1.5.
# --accept was added to 'svn update' in svn 1.6.
if not scm.SVN.AssertVersion('1.5')[0]:
return new_command
# It's annoying to have it block in the middle of a sync, just sensible
# defaults.
if options.force:
new_command.append('--force')
if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
new_command.extend(('--accept', 'theirs-conflict'))
elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
new_command.extend(('--accept', 'postpone'))
return new_command
......@@ -179,9 +179,8 @@ def rmtree(path):
Recursively removes a directory, even if it's marked read-only.
shutil.rmtree() doesn't work on Windows if any of the files or directories
are read-only, which svn repositories and some .svn files are. We need to
be able to force the files to be writable (i.e., deletable) as we traverse
the tree.
are read-only. We need to be able to force the files to be writable (i.e.,
deletable) as we traverse the tree.
Even with all this, Windows still sometimes fails to delete a file, citing
a permission error (maybe something to do with antivirus scans or disk
......@@ -494,8 +493,9 @@ def CheckCallAndFilter(args, stdout=None, filter_fn=None,
# Also, we need to forward stdout to prevent weird re-ordering of output.
# This has to be done on a per byte basis to make sure it is not buffered:
# normally buffering is done for each line, but if svn requests input, no
# end-of-line character is output after the prompt and it would not show up.
# normally buffering is done for each line, but if the process requests
# input, no end-of-line character is output after the prompt and it would
# not show up.
try:
in_byte = kid.stdout.read(1)
if in_byte:
......@@ -1060,7 +1060,7 @@ def GetEditor(git_editor=None):
"""Returns the most plausible editor to use.
In order of preference:
- GIT_EDITOR/SVN_EDITOR environment variable
- GIT_EDITOR environment variable
- core.editor git configuration variable (if supplied by git-cl)
- VISUAL environment variable
- EDITOR environment variable
......
......@@ -67,24 +67,16 @@ class BaseTestCase(GCBaseTestCase, SuperMoxTestBase):
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileRead')
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileWrite')
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'rmtree')
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, 'RunAndGetFileList')
self.mox.StubOutWithMock(subprocess2, 'communicate')
self.mox.StubOutWithMock(subprocess2, 'Popen')
self._scm_wrapper = gclient_scm.CreateSCM
gclient_scm.scm.SVN.current_version = None
self._original_SVNBinaryExists = gclient_scm.SVNWrapper.BinaryExists
self._original_GitBinaryExists = gclient_scm.GitWrapper.BinaryExists
gclient_scm.SVNWrapper.BinaryExists = staticmethod(lambda : True)
gclient_scm.GitWrapper.BinaryExists = staticmethod(lambda : True)
# Absolute path of the fake checkout directory.
self.base_path = join(self.root_dir, self.relpath)
def tearDown(self):
SuperMoxTestBase.tearDown(self)
gclient_scm.SVNWrapper.BinaryExists = self._original_SVNBinaryExists
gclient_scm.GitWrapper.BinaryExists = self._original_GitBinaryExists
......@@ -123,7 +115,6 @@ class BaseGitWrapperTestCase(GCBaseTestCase, StdoutCheck, TestCaseUtils,
self.auto_rebase = False
self.verbose = verbose
self.revision = revision
self.manually_grab_svn_rev = True
self.deps_os = None
self.force = False
self.reset = False
......@@ -249,9 +240,7 @@ from :3
self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
StdoutCheck.setUp(self)
self._original_GitBinaryExists = gclient_scm.GitWrapper.BinaryExists
self._original_SVNBinaryExists = gclient_scm.SVNWrapper.BinaryExists
gclient_scm.GitWrapper.BinaryExists = staticmethod(lambda : True)
gclient_scm.SVNWrapper.BinaryExists = staticmethod(lambda : True)
def tearDown(self):
try:
......@@ -262,7 +251,6 @@ from :3
finally:
# TODO(maruel): Use auto_stub.TestCase.
gclient_scm.GitWrapper.BinaryExists = self._original_GitBinaryExists
gclient_scm.SVNWrapper.BinaryExists = self._original_SVNBinaryExists
class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
......@@ -607,11 +595,15 @@ class ManagedGitWrapperTestCaseMox(BaseTestCase):
self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'IsValidRevision', True)
gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev=self.fake_hash_1
).AndReturn(True)
self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'IsGitSvn', True)
gclient_scm.scm.GIT.IsGitSvn(cwd=self.base_path).MultipleTimes(
gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev='1'
).AndReturn(False)
gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev='1'
).AndReturn(False)
self.mox.StubOutWithMock(gclient_scm.GitWrapper, '_Fetch', True)
# pylint: disable=no-value-for-parameter
gclient_scm.GitWrapper._Fetch(options).AndReturn(None)
gclient_scm.scm.os.path.isdir(self.base_path).AndReturn(True)
gclient_scm.os.path.isdir(self.base_path).AndReturn(True)
......@@ -627,79 +619,6 @@ class ManagedGitWrapperTestCaseMox(BaseTestCase):
self.assertRaises(gclient_scm.gclient_utils.Error,
git_scm.GetUsableRev, '1', options)
def testGetUsableRevGitSvn(self):
# pylint: disable=E1101
options = self.Options()
too_big = str(1e7)
# Pretend like the git-svn repo's HEAD is at r2.
self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'GetGitSvnHeadRev', True)
gclient_scm.scm.GIT.GetGitSvnHeadRev(cwd=self.base_path).MultipleTimes(
).AndReturn(2)
self.mox.StubOutWithMock(
gclient_scm.scm.GIT, 'GetBlessedSha1ForSvnRev', True)
# r1 -> first fake hash, r3 -> second fake hash.
gclient_scm.scm.GIT.GetBlessedSha1ForSvnRev(cwd=self.base_path, rev='1'
).AndReturn(self.fake_hash_1)
gclient_scm.scm.GIT.GetBlessedSha1ForSvnRev(cwd=self.base_path, rev='3'
).MultipleTimes().AndReturn(self.fake_hash_2)
# Ensure that we call git svn fetch if our LKGR is > the git-svn HEAD rev.
self.mox.StubOutWithMock(gclient_scm.GitWrapper, '_Fetch', True)
self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'Capture', True)
gclient_scm.scm.GIT.Capture(['config', '--get', 'svn-remote.svn.fetch'],
cwd=self.base_path).AndReturn('blah')
# pylint: disable=E1120
gclient_scm.scm.GIT.Capture(['svn', 'fetch'], cwd=self.base_path)
error = subprocess2.CalledProcessError(1, 'cmd', '/cwd', 'stdout', 'stderr')
gclient_scm.scm.GIT.Capture(['config', '--get', 'svn-remote.svn.fetch'],
cwd=self.base_path).AndRaise(error)
gclient_scm.GitWrapper._Fetch(options)
gclient_scm.scm.GIT.Capture(['svn', 'fetch'], cwd=self.base_path)
gclient_scm.GitWrapper._Fetch(options)
self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'IsGitSvn', True)
gclient_scm.scm.GIT.IsGitSvn(cwd=self.base_path).MultipleTimes(
).AndReturn(True)
self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'IsValidRevision', True)
gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev=self.fake_hash_1
).AndReturn(True)
gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev=too_big
).MultipleTimes(2).AndReturn(False)
gclient_scm.os.path.isdir(self.base_path).AndReturn(False)
gclient_scm.os.path.isdir(self.base_path).MultipleTimes().AndReturn(True)
self.mox.ReplayAll()
git_svn_scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
relpath=self.relpath)
# Without an existing checkout, this should fail.
# TODO(dbeam) Fix this. http://crbug.com/109184
self.assertRaises(gclient_scm.gclient_utils.Error,
git_svn_scm.GetUsableRev, '1', options)
# Given an SVN revision with a git-svn checkout, it should be translated to
# a git sha1 and be usable.
self.assertEquals(git_svn_scm.GetUsableRev('1', options),
self.fake_hash_1)
# Our fake HEAD rev is r2, so this should call git fetch and git svn fetch
# to get more revs (pymox will complain if this doesn't happen). We mock an
# optimized checkout the first time, so this run should call git fetch.
self.assertEquals(git_svn_scm.GetUsableRev('3', options),
self.fake_hash_2)
# The time we pretend we're not optimized, so no git fetch should fire.
self.assertEquals(git_svn_scm.GetUsableRev('3', options),
self.fake_hash_2)
# Given a git sha1 with a git-svn checkout, it should be used as is.
self.assertEquals(git_svn_scm.GetUsableRev(self.fake_hash_1, options),
self.fake_hash_1)
# We currently check for seemingly valid SVN revisions by assuming 6 digit
# numbers, so assure that numeric revs >= 1000000 don't work.
self.assertRaises(gclient_scm.gclient_utils.Error,
git_svn_scm.GetUsableRev, too_big, options)
def testUpdateNoDotGit(self):
options = self.Options()
......
......@@ -156,11 +156,7 @@ class GClientSmokeBase(fake_repos.FakeReposTestBase):
class GClientSmoke(GClientSmokeBase):
"""Doesn't require either svnserve nor git-daemon."""
@property
def svn_base(self):
return 'svn://random.server/svn/'
"""Doesn't require git-daemon."""
@property
def git_base(self):
return 'git://random.server/git/'
......@@ -203,10 +199,10 @@ class GClientSmoke(GClientSmokeBase):
self.check(('', '', 0), results)
self.checkString(expected, open(p, 'rU').read())
test(['config', self.svn_base + 'trunk/src/'],
test(['config', self.git_base + 'src/'],
('solutions = [\n'
' { "name" : "src",\n'
' "url" : "%strunk/src",\n'
' "url" : "%ssrc",\n'
' "deps_file" : "DEPS",\n'
' "managed" : True,\n'
' "custom_deps" : {\n'
......@@ -214,7 +210,7 @@ class GClientSmoke(GClientSmokeBase):
' "safesync_url": "",\n'
' },\n'
']\n'
'cache_dir = None\n') % self.svn_base)
'cache_dir = None\n') % self.git_base)
test(['config', self.git_base + 'repo_1', '--name', 'src'],
('solutions = [\n'
......@@ -287,14 +283,19 @@ class GClientSmoke(GClientSmokeBase):
def testDifferentTopLevelDirectory(self):
# Check that even if the .gclient file does not mention the directory src
# itself, but it is included via dependencies, the .gclient file is used.
self.gclient(['config', self.svn_base + 'trunk/src.DEPS'])
self.gclient(['config', self.git_base + 'src.DEPS'])
deps = join(self.root_dir, 'src.DEPS')
os.mkdir(deps)
subprocess2.check_output(['git', 'init'], cwd=deps)
write(join(deps, 'DEPS'),
'deps = { "src": "%strunk/src" }' % (self.svn_base))
'deps = { "src": "%ssrc" }' % (self.git_base))
subprocess2.check_output(['git', 'add', 'DEPS'], cwd=deps)
subprocess2.check_output(
['git', 'commit', '-a', '-m', 'DEPS file'], cwd=deps)
src = join(self.root_dir, 'src')
os.mkdir(src)
res = self.gclient(['status', '--jobs', '1'], src)
subprocess2.check_output(['git', 'init'], cwd=src)
res = self.gclient(['status', '--jobs', '1', '-v'], src)
self.checkBlock(res[0], [('running', deps), ('running', src)])
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment