From 28de290fe3f5f31c905c9bd06eeabf80ba668744 Mon Sep 17 00:00:00 2001 From: "pkasting@chromium.org" Date: Fri, 19 Apr 2013 23:15:55 +0000 Subject: [PATCH] Use --internal-diff on svn 1.7+ to slightly reduce disk thrashing. This just saves the need to create and remove an empty directory on every call to GenerateDiff. Review URL: https://chromiumcodereview.appspot.com/14064017 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@195328 0039d316-1c4b-4281-b951-d872f2087c98 --- scm.py | 152 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 81 insertions(+), 71 deletions(-) diff --git a/scm.py b/scm.py index 85eea4297..62fe1d259 100644 --- a/scm.py +++ b/scm.py @@ -781,83 +781,93 @@ class SVN(object): The diff will always use relative paths. """ assert isinstance(filenames, (list, tuple)) + # If the user specified a custom diff command in their svn config file, + # then it'll be used when we do svn diff, which we don't want to happen + # since we want the unified diff. + if SVN.AssertVersion("1.7")[0]: + # On svn >= 1.7, the "--internal-diff" flag will solve this. + return SVN._GenerateDiffInternal(filenames, cwd, full_move, revision, + ["diff", "--internal-diff"]) + else: + # On svn < 1.7, the "--internal-diff" flag doesn't exist. Using + # --diff-cmd=diff doesn't always work, since e.g. Windows cmd users may + # not have a "diff" executable in their path at all. So we use an empty + # temporary directory as the config directory, which bypasses any user + # settings for the diff-cmd. + bogus_dir = tempfile.mkdtemp() + try: + return SVN._GenerateDiffInternal(filenames, cwd, full_move, revision, + ["diff", "--config_dir", bogus_dir]) + finally: + gclient_utils.RemoveDirectory(bogus_dir) + + @staticmethod + def _GenerateDiffInternal(filenames, cwd, full_move, revision, diff_command): root = os.path.normcase(os.path.join(cwd, '')) def RelativePath(path, root): """We must use relative paths.""" if os.path.normcase(path).startswith(root): return path[len(root):] return path - # If the user specified a custom diff command in their svn config file, - # then it'll be used when we do svn diff, which we don't want to happen - # since we want the unified diff. Using --diff-cmd=diff doesn't always - # work, since e.g. Windows cmd users may not have a "diff" executable in - # their path at all. So we use an empty temporary directory as the config - # directory, which gets around these problems. - bogus_dir = tempfile.mkdtemp() - command = ['diff', '--config-dir', bogus_dir] - try: - # Cleanup filenames - filenames = [RelativePath(f, root) for f in filenames] - # Get information about the modified items (files and directories) - data = dict((f, SVN.CaptureLocalInfo([f], root)) for f in filenames) - diffs = [] - if full_move: - # Eliminate modified files inside moved/copied directory. - for (filename, info) in data.iteritems(): - if SVN.IsMovedInfo(info) and info.get("Node Kind") == "directory": - # Remove files inside the directory. - filenames = [f for f in filenames - if not f.startswith(filename + os.path.sep)] - for filename in data.keys(): - if not filename in filenames: - # Remove filtered out items. - del data[filename] - else: - metaheaders = [] - for (filename, info) in data.iteritems(): - if SVN.IsMovedInfo(info): - # 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') - file_root = info.get('Repository Root') - rev = int(info.get('Copied From Rev')) - assert srcurl.startswith(file_root) - src = srcurl[len(file_root)+1:] - try: - srcinfo = SVN.CaptureRemoteInfo(srcurl) - except subprocess2.CalledProcessError, e: - if not 'Not a valid URL' in e.stderr: - raise - # Assume the file was deleted. No idea how to figure out at which - # revision the file was deleted. - srcinfo = {'Revision': rev} - if (srcinfo.get('Revision') != rev and - SVN.Capture(command + ['-r', '%d:head' % rev, srcurl], cwd)): - metaheaders.append("#$ svn cp -r %d %s %s " - "### WARNING: note non-trunk copy\n" % - (rev, src, filename)) - else: - metaheaders.append("#$ cp %s %s\n" % (src, - filename)) - - if metaheaders: - diffs.append("### BEGIN SVN COPY METADATA\n") - diffs.extend(metaheaders) - diffs.append("### END SVN COPY METADATA\n") - # Now ready to do the actual diff. - for filename in sorted(data): - diffs.append(SVN._DiffItemInternal( - filename, cwd, data[filename], command, full_move, revision)) - # Use StringIO since it can be messy when diffing a directory move with - # full_move=True. - buf = cStringIO.StringIO() - for d in filter(None, diffs): - buf.write(d) - result = buf.getvalue() - buf.close() - return result - finally: - gclient_utils.RemoveDirectory(bogus_dir) + # Cleanup filenames + filenames = [RelativePath(f, root) for f in filenames] + # Get information about the modified items (files and directories) + data = dict((f, SVN.CaptureLocalInfo([f], root)) for f in filenames) + diffs = [] + if full_move: + # Eliminate modified files inside moved/copied directory. + for (filename, info) in data.iteritems(): + if SVN.IsMovedInfo(info) and info.get("Node Kind") == "directory": + # Remove files inside the directory. + filenames = [f for f in filenames + if not f.startswith(filename + os.path.sep)] + for filename in data.keys(): + if not filename in filenames: + # Remove filtered out items. + del data[filename] + else: + metaheaders = [] + for (filename, info) in data.iteritems(): + if SVN.IsMovedInfo(info): + # 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') + file_root = info.get('Repository Root') + rev = int(info.get('Copied From Rev')) + assert srcurl.startswith(file_root) + src = srcurl[len(file_root)+1:] + try: + srcinfo = SVN.CaptureRemoteInfo(srcurl) + except subprocess2.CalledProcessError, e: + if not 'Not a valid URL' in e.stderr: + raise + # Assume the file was deleted. No idea how to figure out at which + # revision the file was deleted. + srcinfo = {'Revision': rev} + if (srcinfo.get('Revision') != rev and + SVN.Capture(diff_command + ['-r', '%d:head' % rev, srcurl], cwd)): + metaheaders.append("#$ svn cp -r %d %s %s " + "### WARNING: note non-trunk copy\n" % + (rev, src, filename)) + else: + metaheaders.append("#$ cp %s %s\n" % (src, + filename)) + if metaheaders: + diffs.append("### BEGIN SVN COPY METADATA\n") + diffs.extend(metaheaders) + diffs.append("### END SVN COPY METADATA\n") + # Now ready to do the actual diff. + for filename in sorted(data): + diffs.append(SVN._DiffItemInternal( + filename, cwd, data[filename], diff_command, full_move, revision)) + # Use StringIO since it can be messy when diffing a directory move with + # full_move=True. + buf = cStringIO.StringIO() + for d in filter(None, diffs): + buf.write(d) + result = buf.getvalue() + buf.close() + return result @staticmethod def _DiffItemInternal(filename, cwd, info, diff_command, full_move, revision):