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
diff --git a/scm.py b/scm.py
index 85eea42..62fe1d2 100644
--- a/scm.py
+++ b/scm.py
@@ -781,83 +781,93 @@
     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):