diff --git a/scm.py b/scm.py index 62fe1d259..85eea4297 100644 --- a/scm.py +++ b/scm.py @@ -781,93 +781,83 @@ 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 - # 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 + # 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) @staticmethod def _DiffItemInternal(filename, cwd, info, diff_command, full_move, revision):