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): ...@@ -37,14 +37,14 @@ class NoUsableRevError(gclient_utils.Error):
class DiffFiltererWrapper(object): class DiffFiltererWrapper(object):
"""Simple base class which tracks which file is being diffed and """Simple base class which tracks which file is being diffed and
replaces instances of its file name in the original 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 index_string = None
original_prefix = "--- " original_prefix = "--- "
working_prefix = "+++ " working_prefix = "+++ "
def __init__(self, relpath, print_func): def __init__(self, relpath, print_func):
# Note that we always use '/' as the path separator to be # 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._relpath = relpath.replace("\\", "/")
self._current_file = None self._current_file = None
self._print_func = print_func self._print_func = print_func
...@@ -70,10 +70,6 @@ class DiffFiltererWrapper(object): ...@@ -70,10 +70,6 @@ class DiffFiltererWrapper(object):
self._print_func(line) self._print_func(line)
class SvnDiffFilterer(DiffFiltererWrapper):
index_string = "Index: "
class GitDiffFilterer(DiffFiltererWrapper): class GitDiffFilterer(DiffFiltererWrapper):
index_string = "diff --git " index_string = "diff --git "
...@@ -90,26 +86,20 @@ class GitDiffFilterer(DiffFiltererWrapper): ...@@ -90,26 +86,20 @@ class GitDiffFilterer(DiffFiltererWrapper):
# Factory Method for SCM wrapper creation # Factory Method for SCM wrapper creation
def GetScmName(url): def GetScmName(url):
if url: if not url:
url, _ = gclient_utils.SplitUrlRevision(url) return None
if (url.startswith('git://') or url.startswith('ssh://') or url, _ = gclient_utils.SplitUrlRevision(url)
url.startswith('git+http://') or url.startswith('git+https://') or if url.endswith('.git'):
url.endswith('.git') or url.startswith('sso://') or return 'git'
'googlesource' in url): protocol = url.split('://')[0]
return 'git' if protocol in (
elif (url.startswith('http://') or url.startswith('https://') or 'file', 'git', 'git+http', 'git+https', 'http', 'https', 'ssh', 'sso'):
url.startswith('svn://') or url.startswith('svn+ssh://')): return 'git'
return 'svn'
elif url.startswith('file://'):
if url.endswith('.git'):
return 'git'
return 'svn'
return None return None
def CreateSCM(url, root_dir=None, relpath=None, out_fh=None, out_cb=None): def CreateSCM(url, root_dir=None, relpath=None, out_fh=None, out_cb=None):
SCM_MAP = { SCM_MAP = {
'svn' : SVNWrapper,
'git' : GitWrapper, 'git' : GitWrapper,
} }
...@@ -192,10 +182,6 @@ class SCMWrapper(object): ...@@ -192,10 +182,6 @@ class SCMWrapper(object):
actual_remote_url.replace('\\', '/')): actual_remote_url.replace('\\', '/')):
actual_remote_url = self._get_first_remote_url(mirror.mirror_path) actual_remote_url = self._get_first_remote_url(mirror.mirror_path)
return actual_remote_url 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 return None
def DoesRemoteURLMatch(self, options): def DoesRemoteURLMatch(self, options):
...@@ -210,7 +196,7 @@ class SCMWrapper(object): ...@@ -210,7 +196,7 @@ class SCMWrapper(object):
== gclient_utils.SplitUrlRevision(self.url)[0].rstrip('/')) == gclient_utils.SplitUrlRevision(self.url)[0].rstrip('/'))
else: else:
# This may occur if the self.checkout_path exists but does not contain a # This may occur if the self.checkout_path exists but does not contain a
# valid git or svn checkout. # valid git checkout.
return False return False
def _DeleteOrMove(self, force): def _DeleteOrMove(self, force):
...@@ -503,7 +489,7 @@ class GitWrapper(SCMWrapper): ...@@ -503,7 +489,7 @@ class GitWrapper(SCMWrapper):
# 0) HEAD is detached. Probably from our initial clone. # 0) HEAD is detached. Probably from our initial clone.
# - make sure HEAD is contained by a named ref, then update. # - make sure HEAD is contained by a named ref, then update.
# Cases 1-4. HEAD is a branch. # 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 # - try to rebase onto the new hash or branch
# 2) current branch is tracking a remote branch with local committed # 2) current branch is tracking a remote branch with local committed
# changes, but the DEPS file switched to point to a hash # changes, but the DEPS file switched to point to a hash
...@@ -573,22 +559,15 @@ class GitWrapper(SCMWrapper): ...@@ -573,22 +559,15 @@ class GitWrapper(SCMWrapper):
self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False)
elif current_type == 'hash': elif current_type == 'hash':
# case 1 # case 1
if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: # Can't find a merge-base since we don't know our upstream. That makes
# Our git-svn branch (upstream_branch) is our upstream # this command VERY likely to produce a rebase failure. For now we
self._AttemptRebase(upstream_branch, files, options, # assume origin is our upstream since that's what the old behavior was.
newbase=revision, printed_path=printed_path, upstream_branch = self.remote
merge=options.merge) if options.revision or deps_revision:
printed_path = True upstream_branch = revision
else: self._AttemptRebase(upstream_branch, files, options,
# Can't find a merge-base since we don't know our upstream. That makes printed_path=printed_path, merge=options.merge)
# this command VERY likely to produce a rebase failure. For now we printed_path = True
# assume origin is our upstream since that's what the old behavior was.
upstream_branch = self.remote
if options.revision or deps_revision:
upstream_branch = revision
self._AttemptRebase(upstream_branch, files, options,
printed_path=printed_path, merge=options.merge)
printed_path = True
elif rev_type == 'hash': elif rev_type == 'hash':
# case 2 # case 2
self._AttemptRebase(upstream_branch, files, options, self._AttemptRebase(upstream_branch, files, options,
...@@ -788,16 +767,13 @@ class GitWrapper(SCMWrapper): ...@@ -788,16 +767,13 @@ class GitWrapper(SCMWrapper):
except subprocess2.CalledProcessError: except subprocess2.CalledProcessError:
merge_base = [] merge_base = []
self._Run(['diff', '--name-status'] + merge_base, options, 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: if file_list is not None:
files = self._Capture(['diff', '--name-only'] + merge_base).split() files = self._Capture(['diff', '--name-only'] + merge_base).split()
file_list.extend([os.path.join(self.checkout_path, f) for f in files]) file_list.extend([os.path.join(self.checkout_path, f) for f in files])
def GetUsableRev(self, rev, options): def GetUsableRev(self, rev, options):
"""Finds a useful revision for this repository. """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."""
sha1 = None sha1 = None
if not os.path.isdir(self.checkout_path): if not os.path.isdir(self.checkout_path):
raise NoUsableRevError( raise NoUsableRevError(
...@@ -807,56 +783,23 @@ class GitWrapper(SCMWrapper): ...@@ -807,56 +783,23 @@ class GitWrapper(SCMWrapper):
'For more info, see: ' 'For more info, see: '
'http://code.google.com/p/chromium/wiki/UsingNewGit' 'http://code.google.com/p/chromium/wiki/UsingNewGit'
'#Initial_checkout' ) % rev) '#Initial_checkout' ) % rev)
elif rev.isdigit() and len(rev) < 7:
# Handles an SVN rev. As an optimization, only verify an SVN revision as if scm.GIT.IsValidRevision(cwd=self.checkout_path, rev=rev):
# [0-9]{1,6} for now to avoid making a network request. sha1 = rev
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: else:
# May exist in origin, but we don't have it yet, so fetch and look
# again.
self._Fetch(options)
if scm.GIT.IsValidRevision(cwd=self.checkout_path, rev=rev): if scm.GIT.IsValidRevision(cwd=self.checkout_path, rev=rev):
sha1 = rev sha1 = rev
else:
# May exist in origin, but we don't have it yet, so fetch and look
# again.
self._Fetch(options)
if scm.GIT.IsValidRevision(cwd=self.checkout_path, rev=rev):
sha1 = rev
if not sha1: if not sha1:
raise NoUsableRevError( raise NoUsableRevError(
( 'We could not find a valid hash for safesync_url response "%s".\n' ('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' 'Please ensure that your safesync_url provides git sha1 hashes.\n'
'remote or a safesync_url that provides git sha1s. Please add a\n' 'For more info, see:\n'
'git-svn remote or change your safesync_url. For more info, see:\n' 'http://code.google.com/p/chromium/wiki/UsingNewGit#Initial_checkout'
'http://code.google.com/p/chromium/wiki/UsingNewGit' ) % rev)
'#Initial_checkout' ) % rev)
return sha1 return sha1
...@@ -1239,480 +1182,3 @@ class GitWrapper(SCMWrapper): ...@@ -1239,480 +1182,3 @@ class GitWrapper(SCMWrapper):
gclient_utils.CheckCallAndFilterAndHeader(cmd, env=env, **kwargs) gclient_utils.CheckCallAndFilterAndHeader(cmd, env=env, **kwargs)
else: else:
gclient_utils.CheckCallAndFilter(cmd, env=env, **kwargs) 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): ...@@ -179,9 +179,8 @@ def rmtree(path):
Recursively removes a directory, even if it's marked read-only. 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 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 are read-only. We need to be able to force the files to be writable (i.e.,
be able to force the files to be writable (i.e., deletable) as we traverse deletable) as we traverse the tree.
the tree.
Even with all this, Windows still sometimes fails to delete a file, citing 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 a permission error (maybe something to do with antivirus scans or disk
...@@ -494,8 +493,9 @@ def CheckCallAndFilter(args, stdout=None, filter_fn=None, ...@@ -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. # 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: # 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 # normally buffering is done for each line, but if the process requests
# end-of-line character is output after the prompt and it would not show up. # input, no end-of-line character is output after the prompt and it would
# not show up.
try: try:
in_byte = kid.stdout.read(1) in_byte = kid.stdout.read(1)
if in_byte: if in_byte:
...@@ -1060,7 +1060,7 @@ def GetEditor(git_editor=None): ...@@ -1060,7 +1060,7 @@ def GetEditor(git_editor=None):
"""Returns the most plausible editor to use. """Returns the most plausible editor to use.
In order of preference: In order of preference:
- GIT_EDITOR/SVN_EDITOR environment variable - GIT_EDITOR environment variable
- core.editor git configuration variable (if supplied by git-cl) - core.editor git configuration variable (if supplied by git-cl)
- VISUAL environment variable - VISUAL environment variable
- EDITOR environment variable - EDITOR environment variable
......
...@@ -67,24 +67,16 @@ class BaseTestCase(GCBaseTestCase, SuperMoxTestBase): ...@@ -67,24 +67,16 @@ class BaseTestCase(GCBaseTestCase, SuperMoxTestBase):
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileRead') self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileRead')
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileWrite') self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileWrite')
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'rmtree') 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, 'communicate')
self.mox.StubOutWithMock(subprocess2, 'Popen') self.mox.StubOutWithMock(subprocess2, 'Popen')
self._scm_wrapper = gclient_scm.CreateSCM 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 self._original_GitBinaryExists = gclient_scm.GitWrapper.BinaryExists
gclient_scm.SVNWrapper.BinaryExists = staticmethod(lambda : True)
gclient_scm.GitWrapper.BinaryExists = staticmethod(lambda : True) gclient_scm.GitWrapper.BinaryExists = staticmethod(lambda : True)
# Absolute path of the fake checkout directory. # Absolute path of the fake checkout directory.
self.base_path = join(self.root_dir, self.relpath) self.base_path = join(self.root_dir, self.relpath)
def tearDown(self): def tearDown(self):
SuperMoxTestBase.tearDown(self) SuperMoxTestBase.tearDown(self)
gclient_scm.SVNWrapper.BinaryExists = self._original_SVNBinaryExists
gclient_scm.GitWrapper.BinaryExists = self._original_GitBinaryExists gclient_scm.GitWrapper.BinaryExists = self._original_GitBinaryExists
...@@ -123,7 +115,6 @@ class BaseGitWrapperTestCase(GCBaseTestCase, StdoutCheck, TestCaseUtils, ...@@ -123,7 +115,6 @@ class BaseGitWrapperTestCase(GCBaseTestCase, StdoutCheck, TestCaseUtils,
self.auto_rebase = False self.auto_rebase = False
self.verbose = verbose self.verbose = verbose
self.revision = revision self.revision = revision
self.manually_grab_svn_rev = True
self.deps_os = None self.deps_os = None
self.force = False self.force = False
self.reset = False self.reset = False
...@@ -249,9 +240,7 @@ from :3 ...@@ -249,9 +240,7 @@ from :3
self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path) self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
StdoutCheck.setUp(self) StdoutCheck.setUp(self)
self._original_GitBinaryExists = gclient_scm.GitWrapper.BinaryExists self._original_GitBinaryExists = gclient_scm.GitWrapper.BinaryExists
self._original_SVNBinaryExists = gclient_scm.SVNWrapper.BinaryExists
gclient_scm.GitWrapper.BinaryExists = staticmethod(lambda : True) gclient_scm.GitWrapper.BinaryExists = staticmethod(lambda : True)
gclient_scm.SVNWrapper.BinaryExists = staticmethod(lambda : True)
def tearDown(self): def tearDown(self):
try: try:
...@@ -262,7 +251,6 @@ from :3 ...@@ -262,7 +251,6 @@ from :3
finally: finally:
# TODO(maruel): Use auto_stub.TestCase. # TODO(maruel): Use auto_stub.TestCase.
gclient_scm.GitWrapper.BinaryExists = self._original_GitBinaryExists gclient_scm.GitWrapper.BinaryExists = self._original_GitBinaryExists
gclient_scm.SVNWrapper.BinaryExists = self._original_SVNBinaryExists
class ManagedGitWrapperTestCase(BaseGitWrapperTestCase): class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
...@@ -607,11 +595,15 @@ class ManagedGitWrapperTestCaseMox(BaseTestCase): ...@@ -607,11 +595,15 @@ class ManagedGitWrapperTestCaseMox(BaseTestCase):
self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'IsValidRevision', True) self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'IsValidRevision', True)
gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev=self.fake_hash_1 gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev=self.fake_hash_1
).AndReturn(True) ).AndReturn(True)
gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev='1'
self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'IsGitSvn', True) ).AndReturn(False)
gclient_scm.scm.GIT.IsGitSvn(cwd=self.base_path).MultipleTimes( gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev='1'
).AndReturn(False) ).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.scm.os.path.isdir(self.base_path).AndReturn(True)
gclient_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): ...@@ -627,79 +619,6 @@ class ManagedGitWrapperTestCaseMox(BaseTestCase):
self.assertRaises(gclient_scm.gclient_utils.Error, self.assertRaises(gclient_scm.gclient_utils.Error,
git_scm.GetUsableRev, '1', options) 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): def testUpdateNoDotGit(self):
options = self.Options() options = self.Options()
......
...@@ -156,11 +156,7 @@ class GClientSmokeBase(fake_repos.FakeReposTestBase): ...@@ -156,11 +156,7 @@ class GClientSmokeBase(fake_repos.FakeReposTestBase):
class GClientSmoke(GClientSmokeBase): class GClientSmoke(GClientSmokeBase):
"""Doesn't require either svnserve nor git-daemon.""" """Doesn't require git-daemon."""
@property
def svn_base(self):
return 'svn://random.server/svn/'
@property @property
def git_base(self): def git_base(self):
return 'git://random.server/git/' return 'git://random.server/git/'
...@@ -203,10 +199,10 @@ class GClientSmoke(GClientSmokeBase): ...@@ -203,10 +199,10 @@ class GClientSmoke(GClientSmokeBase):
self.check(('', '', 0), results) self.check(('', '', 0), results)
self.checkString(expected, open(p, 'rU').read()) self.checkString(expected, open(p, 'rU').read())
test(['config', self.svn_base + 'trunk/src/'], test(['config', self.git_base + 'src/'],
('solutions = [\n' ('solutions = [\n'
' { "name" : "src",\n' ' { "name" : "src",\n'
' "url" : "%strunk/src",\n' ' "url" : "%ssrc",\n'
' "deps_file" : "DEPS",\n' ' "deps_file" : "DEPS",\n'
' "managed" : True,\n' ' "managed" : True,\n'
' "custom_deps" : {\n' ' "custom_deps" : {\n'
...@@ -214,7 +210,7 @@ class GClientSmoke(GClientSmokeBase): ...@@ -214,7 +210,7 @@ class GClientSmoke(GClientSmokeBase):
' "safesync_url": "",\n' ' "safesync_url": "",\n'
' },\n' ' },\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'], test(['config', self.git_base + 'repo_1', '--name', 'src'],
('solutions = [\n' ('solutions = [\n'
...@@ -287,14 +283,19 @@ class GClientSmoke(GClientSmokeBase): ...@@ -287,14 +283,19 @@ class GClientSmoke(GClientSmokeBase):
def testDifferentTopLevelDirectory(self): def testDifferentTopLevelDirectory(self):
# Check that even if the .gclient file does not mention the directory src # 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. # 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') deps = join(self.root_dir, 'src.DEPS')
os.mkdir(deps) os.mkdir(deps)
subprocess2.check_output(['git', 'init'], cwd=deps)
write(join(deps, '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') src = join(self.root_dir, 'src')
os.mkdir(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)]) 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