@ -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):
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):
# 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):
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):
== 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):
# 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):
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):
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):
' 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):
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