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

Unified Diff: scm.py

Issue 8771042: Enforces using cwd in all svn calls. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Address comments Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « presubmit_support.py ('k') | testing_support/fake_repos.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: scm.py
diff --git a/scm.py b/scm.py
index 261cb64917ba5912138dc0bae5154677d8408da0..56e795fcfecbd9f35fbcc9d67f24eed2f6cb9477 100644
--- a/scm.py
+++ b/scm.py
@@ -398,13 +398,13 @@ class SVN(object):
current_version = None
@staticmethod
- def Capture(args, **kwargs):
+ def Capture(args, cwd, **kwargs):
"""Always redirect stderr.
Throws an exception if non-0 is returned.
"""
return subprocess2.check_output(
- ['svn'] + args, stderr=subprocess2.PIPE, **kwargs)
+ ['svn'] + args, stderr=subprocess2.PIPE, cwd=cwd, **kwargs)
@staticmethod
def RunAndGetFileList(verbose, args, cwd, file_list, stdout=None):
@@ -514,13 +514,30 @@ class SVN(object):
break
@staticmethod
- def CaptureInfo(cwd):
+ def CaptureRemoteInfo(url):
+ """Returns a dictionary from the svn info output for the given url.
+
+ Throws an exception if svn info fails.
+ """
+ assert isinstance(url, str)
+ return SVN._CaptureInfo([url], None)
+
+ @staticmethod
+ def CaptureLocalInfo(files, cwd):
+ """Returns a dictionary from the svn info output for the given files.
+
+ Throws an exception if svn info fails.
+ """
+ assert isinstance(files, (list, tuple))
+ return SVN._CaptureInfo(files, cwd)
+
+ @staticmethod
+ def _CaptureInfo(files, cwd):
"""Returns a dictionary from the svn info output for the given file.
Throws an exception if svn info fails."""
result = {}
- output = SVN.Capture(['info', '--xml', cwd])
- info = ElementTree.XML(output)
+ info = ElementTree.XML(SVN.Capture(['info', '--xml'] + files, cwd))
if info is None:
return result
entry = info.find('entry')
@@ -563,10 +580,10 @@ class SVN(object):
Returns:
Int base revision
"""
- return SVN.CaptureInfo(cwd).get('Revision')
+ return SVN.CaptureLocalInfo([], cwd).get('Revision')
@staticmethod
- def CaptureStatus(files):
+ def CaptureStatus(files, cwd):
"""Returns the svn 1.5 svn status emulated output.
@files can be a string (one file) or a list of files.
@@ -598,7 +615,7 @@ class SVN(object):
'replaced': 'R',
'unversioned': '?',
}
- dom = ElementTree.XML(SVN.Capture(command))
+ dom = ElementTree.XML(SVN.Capture(command, cwd))
results = []
if dom is None:
return results
@@ -645,9 +662,10 @@ class SVN(object):
return results
@staticmethod
- def IsMoved(filename):
+ def IsMoved(filename, cwd):
"""Determine if a file has been added through svn mv"""
- return SVN.IsMovedInfo(SVN.CaptureInfo(filename))
+ assert isinstance(filename, basestring)
+ return SVN.IsMovedInfo(SVN.CaptureLocalInfo([filename], cwd))
@staticmethod
def IsMovedInfo(info):
@@ -657,7 +675,7 @@ class SVN(object):
info.get('Schedule') == 'add')
@staticmethod
- def GetFileProperty(filename, property_name):
+ def GetFileProperty(filename, property_name, cwd):
"""Returns the value of an SVN property for the given file.
Args:
@@ -670,12 +688,12 @@ class SVN(object):
empty string is also returned.
"""
try:
- return SVN.Capture(['propget', property_name, filename])
+ return SVN.Capture(['propget', property_name, filename], cwd)
except subprocess2.CalledProcessError:
return ''
@staticmethod
- def DiffItem(filename, full_move=False, revision=None):
+ def DiffItem(filename, cwd, full_move, revision):
"""Diffs a single file.
Should be simple, eh? No it isn't.
@@ -693,15 +711,18 @@ class SVN(object):
try:
# Use "svn info" output instead of os.path.isdir because the latter fails
# when the file is deleted.
- return SVN._DiffItemInternal(filename, SVN.CaptureInfo(filename),
- bogus_dir,
- full_move=full_move, revision=revision)
+ return SVN._DiffItemInternal(
+ filename,
+ cwd,
+ SVN.CaptureLocalInfo([filename], cwd),
+ bogus_dir,
+ full_move,
+ revision)
finally:
gclient_utils.RemoveDirectory(bogus_dir)
@staticmethod
- def _DiffItemInternal(filename, info, bogus_dir, full_move=False,
- revision=None):
+ def _DiffItemInternal(filename, cwd, info, bogus_dir, full_move, revision):
"""Grabs the diff data."""
command = ["diff", "--config-dir", bogus_dir, filename]
if revision:
@@ -737,7 +758,7 @@ class SVN(object):
else:
if info.get("Node Kind") != "directory":
# svn diff on a mv/cp'd file outputs nothing if there was no change.
- data = SVN.Capture(command)
+ data = SVN.Capture(command, cwd)
if not data:
# We put in an empty Index entry so upload.py knows about them.
data = "Index: %s\n" % filename.replace(os.sep, '/')
@@ -746,7 +767,7 @@ class SVN(object):
if info.get("Node Kind") != "directory":
# Normal simple case.
try:
- data = SVN.Capture(command)
+ data = SVN.Capture(command, cwd)
except subprocess2.CalledProcessError:
if revision:
data = GenFakeDiff(filename)
@@ -756,7 +777,7 @@ class SVN(object):
return data
@staticmethod
- def GenerateDiff(filenames, root=None, full_move=False, revision=None):
+ def GenerateDiff(filenames, cwd, full_move, revision):
"""Returns a string containing the diff for the given file list.
The files in the list should either be absolute paths or relative to the
@@ -765,9 +786,7 @@ class SVN(object):
The diff will always use relative paths.
"""
assert isinstance(filenames, (list, tuple))
- previous_cwd = os.getcwd()
- root = root or SVN.GetCheckoutRoot(previous_cwd)
- root = os.path.normcase(os.path.join(root, ''))
+ root = os.path.normcase(os.path.join(cwd, ''))
def RelativePath(path, root):
"""We must use relative paths."""
if os.path.normcase(path).startswith(root):
@@ -781,11 +800,10 @@ class SVN(object):
# config directory, which gets around these problems.
bogus_dir = tempfile.mkdtemp()
try:
- os.chdir(root)
# Cleanup filenames
filenames = [RelativePath(f, root) for f in filenames]
# Get information about the modified items (files and directories)
- data = dict([(f, SVN.CaptureInfo(f)) for f in filenames])
+ data = dict([(f, SVN.CaptureLocalInfo([f], root)) for f in filenames])
diffs = []
if full_move:
# Eliminate modified files inside moved/copied directory.
@@ -805,12 +823,12 @@ class SVN(object):
# for now, the most common case is a head copy,
# so let's just encode that as a straight up cp.
srcurl = info.get('Copied From URL')
- root = info.get('Repository Root')
+ file_root = info.get('Repository Root')
rev = int(info.get('Copied From Rev'))
- assert srcurl.startswith(root)
- src = srcurl[len(root)+1:]
+ assert srcurl.startswith(file_root)
+ src = srcurl[len(file_root)+1:]
try:
- srcinfo = SVN.CaptureInfo(srcurl)
+ srcinfo = SVN.CaptureRemoteInfo(srcurl)
except subprocess2.CalledProcessError, e:
if not 'Not a valid URL' in e.stderr:
raise
@@ -818,7 +836,7 @@ class SVN(object):
# revision the file was deleted.
srcinfo = {'Revision': rev}
if (srcinfo.get('Revision') != rev and
- SVN.Capture(['diff', '-r', '%d:head' % rev, srcurl])):
+ SVN.Capture(['diff', '-r', '%d:head' % rev, srcurl], cwd)):
metaheaders.append("#$ svn cp -r %d %s %s "
"### WARNING: note non-trunk copy\n" %
(rev, src, filename))
@@ -832,9 +850,8 @@ class SVN(object):
diffs.append("### END SVN COPY METADATA\n")
# Now ready to do the actual diff.
for filename in sorted(data.iterkeys()):
- diffs.append(SVN._DiffItemInternal(filename, data[filename], bogus_dir,
- full_move=full_move,
- revision=revision))
+ diffs.append(SVN._DiffItemInternal(
+ filename, cwd, data[filename], bogus_dir, full_move, revision))
# Use StringIO since it can be messy when diffing a directory move with
# full_move=True.
buf = cStringIO.StringIO()
@@ -844,14 +861,13 @@ class SVN(object):
buf.close()
return result
finally:
- os.chdir(previous_cwd)
gclient_utils.RemoveDirectory(bogus_dir)
@staticmethod
- def GetEmail(repo_root):
+ def GetEmail(cwd):
"""Retrieves the svn account which we assume is an email address."""
try:
- infos = SVN.CaptureInfo(repo_root)
+ infos = SVN.CaptureLocalInfo([], cwd)
except subprocess2.CalledProcessError:
return None
@@ -904,36 +920,36 @@ class SVN(object):
return values
@staticmethod
- def GetCheckoutRoot(directory):
+ def GetCheckoutRoot(cwd):
"""Returns the top level directory of the current repository.
The directory is returned as an absolute path.
"""
- directory = os.path.abspath(directory)
+ cwd = os.path.abspath(cwd)
try:
- info = SVN.CaptureInfo(directory)
+ info = SVN.CaptureLocalInfo([], cwd)
cur_dir_repo_root = info['Repository Root']
url = info['URL']
except subprocess2.CalledProcessError:
return None
while True:
- parent = os.path.dirname(directory)
+ parent = os.path.dirname(cwd)
try:
- info = SVN.CaptureInfo(parent)
+ info = SVN.CaptureLocalInfo([], parent)
if (info['Repository Root'] != cur_dir_repo_root or
info['URL'] != os.path.dirname(url)):
break
url = info['URL']
except subprocess2.CalledProcessError:
break
- directory = parent
- return GetCasedPath(directory)
+ cwd = parent
+ return GetCasedPath(cwd)
@classmethod
def AssertVersion(cls, min_version):
"""Asserts svn's version is at least min_version."""
if cls.current_version is None:
- cls.current_version = cls.Capture(['--version']).split()[2]
+ cls.current_version = cls.Capture(['--version'], None).split()[2]
current_version_list = map(only_int, cls.current_version.split('.'))
for min_ver in map(int, min_version.split('.')):
ver = current_version_list.pop(0)
@@ -944,16 +960,16 @@ class SVN(object):
return (True, cls.current_version)
@staticmethod
- def Revert(repo_root, callback=None, ignore_externals=False):
- """Reverts all svn modifications in repo_root, including properties.
+ def Revert(cwd, callback=None, ignore_externals=False):
+ """Reverts all svn modifications in cwd, including properties.
Deletes any modified files or directory.
A "svn update --revision BASE" call is required after to revive deleted
files.
"""
- for file_status in SVN.CaptureStatus(repo_root):
- file_path = os.path.join(repo_root, file_status[1])
+ for file_status in SVN.CaptureStatus(None, cwd):
+ file_path = os.path.join(cwd, file_status[1])
if (ignore_externals and
file_status[0][0] == 'X' and
file_status[0][1:].isspace()):
@@ -985,11 +1001,11 @@ class SVN(object):
not file_status[0][1:].isspace()):
# Added, deleted file requires manual intervention and require calling
# revert, like for properties.
- if not os.path.isdir(repo_root):
+ if not os.path.isdir(cwd):
# '.' was deleted. It's not worth continuing.
return
try:
- SVN.Capture(['revert', file_status[1]], cwd=repo_root)
+ SVN.Capture(['revert', file_status[1]], cwd=cwd)
except subprocess2.CalledProcessError:
if not os.path.exists(file_path):
continue
« no previous file with comments | « presubmit_support.py ('k') | testing_support/fake_repos.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698