Rev 3145: Implement native support for --using (abentley) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Thu Dec 27 15:01:55 GMT 2007


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 3145
revision-id:pqm at pqm.ubuntu.com-20071227150146-08nqv2gvo5e3i1n3
parent: pqm at pqm.ubuntu.com-20071222080058-lra6luc153ex60w4
parent: aaron.bentley at utoronto.ca-20071227022814-e1ance6116xmbmqk
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Thu 2007-12-27 15:01:46 +0000
message:
  Implement native support for --using (abentley)
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
  bzrlib/commands.py             bzr.py-20050309040720-d10f4714595cf8c3
  bzrlib/diff.py                 diff.py-20050309040759-26944fbbf2ebbf36
  bzrlib/tests/test_diff.py      testdiff.py-20050727164403-d1a3496ebb12e339
    ------------------------------------------------------------
    revno: 3123.4.1.1.3
    revision-id:aaron.bentley at utoronto.ca-20071227022814-e1ance6116xmbmqk
    parent: aaron.bentley at utoronto.ca-20071226211353-zum7avm4gfgyfwam
    committer: Aaron Bentley <aaron.bentley at utoronto.ca>
    branch nick: no-inventory2
    timestamp: Wed 2007-12-26 21:28:14 -0500
    message:
      Fix typo
    modified:
      bzrlib/commands.py             bzr.py-20050309040720-d10f4714595cf8c3
    ------------------------------------------------------------
    revno: 3123.4.1.1.2
    revision-id:aaron.bentley at utoronto.ca-20071226211353-zum7avm4gfgyfwam
    parent: aaron.bentley at utoronto.ca-20071226033623-5wgrblepj4dqlw6r
    committer: Aaron Bentley <aaron.bentley at utoronto.ca>
    branch nick: no-inventory2
    timestamp: Wed 2007-12-26 16:13:53 -0500
    message:
      Implement diff --using natively
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
      bzrlib/commands.py             bzr.py-20050309040720-d10f4714595cf8c3
      bzrlib/diff.py                 diff.py-20050309040759-26944fbbf2ebbf36
      bzrlib/tests/test_diff.py      testdiff.py-20050727164403-d1a3496ebb12e339
    ------------------------------------------------------------
    revno: 3123.4.1.1.1
    revision-id:aaron.bentley at utoronto.ca-20071226033623-5wgrblepj4dqlw6r
    parent: aaron.bentley at utoronto.ca-20071218041534-w9spqi20zck44bvd
    parent: pqm at pqm.ubuntu.com-20071222080058-lra6luc153ex60w4
    committer: Aaron Bentley <aaron.bentley at utoronto.ca>
    branch nick: no-inventory2
    timestamp: Tue 2007-12-25 22:36:23 -0500
    message:
      Merge from bzr.dev
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/_patiencediff_c.c       _patiencediff_c.c-20070721205602-q3imkipwlgagp3cy-1
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
      bzrlib/bundle/serializer/v4.py v10.py-20070611062757-5ggj7k18s9dej0fr-1
      bzrlib/bzrdir.py               bzrdir.py-20060131065624-156dfea39c4387cb
      bzrlib/commit.py               commit.py-20050511101309-79ec1a0168e0e825
      bzrlib/diff.py                 diff.py-20050309040759-26944fbbf2ebbf36
      bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
      bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
      bzrlib/graph.py                graph_walker.py-20070525030359-y852guab65d4wtn0-1
      bzrlib/index.py                index.py-20070712131115-lolkarso50vjr64s-1
      bzrlib/inventory.py            inventory.py-20050309040759-6648b84ca2005b37
      bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
      bzrlib/osutils.py              osutils.py-20050309040759-eeaff12fbf77ac86
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/repofmt/knitrepo.py     knitrepo.py-20070206081537-pyy4a00xdas0j4pf-1
      bzrlib/repofmt/pack_repo.py    pack_repo.py-20070813041115-gjv5ma7ktfqwsjgn-1
      bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
      bzrlib/revision.py             revision.py-20050309040759-e77802c08f3999d5
      bzrlib/smart/medium.py         medium.py-20061103051856-rgu2huy59fkz902q-1
      bzrlib/symbol_versioning.py    symbol_versioning.py-20060105104851-9ecf8af605d15a80
      bzrlib/tests/TestUtil.py       TestUtil.py-20050824080200-5f70140a2d938694
      bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
      bzrlib/tests/blackbox/test_checkout.py test_checkout.py-20060211231752-a5cde67cf70af854
      bzrlib/tests/blackbox/test_merge.py test_merge.py-20060323225809-9bc0459c19917f41
      bzrlib/tests/blackbox/test_non_ascii.py test_non_ascii.py-20060105214030-68010be784a5d854
      bzrlib/tests/blackbox/test_split.py test_split.py-20061008023421-qy0vdpzysh5rriu8-1
      bzrlib/tests/bzrdir_implementations/test_bzrdir.py test_bzrdir.py-20060131065642-0ebeca5e30e30866
      bzrlib/tests/repository_implementations/__init__.py __init__.py-20060131092037-9564957a7d4a841b
      bzrlib/tests/repository_implementations/test_repository.py test_repository.py-20060131092128-ad07f494f5c9d26c
      bzrlib/tests/test_ancestry.py  test_ancestry.py-20050913023709-69768e94848312c6
      bzrlib/tests/test_bzrdir.py    test_bzrdir.py-20060131065654-deba40eef51cf220
      bzrlib/tests/test_commit.py    test_commit.py-20050914060732-279f057f8c295434
      bzrlib/tests/test_diff.py      testdiff.py-20050727164403-d1a3496ebb12e339
      bzrlib/tests/test_extract.py   test_extract.py-20061002214140-qdnnm67q1ov6x6pd-1
      bzrlib/tests/test_graph.py     test_graph_walker.py-20070525030405-enq4r60hhi9xrujc-1
      bzrlib/tests/test_http.py      testhttp.py-20051018020158-b2eef6e867c514d9
      bzrlib/tests/test_merge.py     testmerge.py-20050905070950-c1b5aa49ff911024
      bzrlib/tests/test_msgeditor.py test_msgeditor.py-20051202041359-920315ec6011ee51
      bzrlib/tests/test_revision.py  testrevision.py-20050804210559-46f5e1eb67b01289
      bzrlib/tests/test_selftest.py  test_selftest.py-20051202044319-c110a115d8c0456a
      bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
      bzrlib/tests/test_version_info.py test_version_info.py-20051228204928-2c364e30b702b41b
      bzrlib/tests/test_workingtree.py testworkingtree.py-20051004024258-b88d0fe8f101d468
      bzrlib/tests/tree_implementations/test_inv.py test_inv.py-20070312023226-0cdvk5uwhutis9vg-1
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
      bzrlib/transport/http/__init__.py http_transport.py-20050711212304-506c5fd1059ace96
      bzrlib/transport/http/_pycurl.py pycurlhttp.py-20060110060940-4e2a705911af77a6
      bzrlib/transport/http/_urllib.py _urlgrabber.py-20060113083826-0bbf7d992fbf090c
      bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
      bzrlib/tree.py                 tree.py-20050309040759-9d5f2496be663e77
      bzrlib/version_info_formats/__init__.py generate_version_info.py-20051228204928-8358edabcddcd97e
      bzrlib/versionedfile.py        versionedfile.py-20060222045106-5039c71ee3b65490
      bzrlib/workingtree.py          workingtree.py-20050511021032-29b6ec0a681e02e3
      bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
=== modified file 'NEWS'
--- a/NEWS	2007-12-22 08:00:58 +0000
+++ b/NEWS	2007-12-26 21:13:53 +0000
@@ -19,6 +19,9 @@
    * New option to use custom template-based formats in  ``bzr version-info``.
      (Lukáš Lalinský)
 
+   * diff '--using' allows an external diff tool to be used for files.
+     (Aaron Bentley)
+
   IMPROVEMENTS:
 
    * ``branch`` and ``checkout`` can now use files from a working tree to

=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py	2007-12-21 05:52:38 +0000
+++ b/bzrlib/builtins.py	2007-12-26 21:13:53 +0000
@@ -1454,17 +1454,6 @@
 
             bzr diff --prefix old/:new/
     """
-    # TODO: Option to use external diff command; could be GNU diff, wdiff,
-    #       or a graphical diff.
-
-    # TODO: Python difflib is not exactly the same as unidiff; should
-    #       either fix it up or prefer to use an external diff.
-
-    # TODO: Selected-file diff is inefficient and doesn't show you
-    #       deleted files.
-
-    # TODO: This probably handles non-Unix newlines poorly.
-
     _see_also = ['status']
     takes_args = ['file*']
     takes_options = [
@@ -1484,13 +1473,17 @@
             ),
         'revision',
         'change',
+        Option('using',
+            help='Use this command to compare files.',
+            type=unicode,
+            ),
         ]
     aliases = ['di', 'dif']
     encoding_type = 'exact'
 
     @display_command
     def run(self, revision=None, file_list=None, diff_options=None,
-            prefix=None, old=None, new=None):
+            prefix=None, old=None, new=None, using=None):
         from bzrlib.diff import _get_trees_to_diff, show_diff_trees
 
         if (prefix is None) or (prefix == '0'):
@@ -1517,7 +1510,7 @@
                                specific_files=specific_files,
                                external_diff_options=diff_options,
                                old_label=old_label, new_label=new_label,
-                               extra_trees=extra_trees)
+                               extra_trees=extra_trees, using=using)
 
 
 class cmd_deleted(Command):

=== modified file 'bzrlib/commands.py'
--- a/bzrlib/commands.py	2007-09-24 06:42:21 +0000
+++ b/bzrlib/commands.py	2007-12-27 02:28:14 +0000
@@ -627,6 +627,11 @@
     return ret
 
 
+def shlex_split_unicode(unsplit):
+    import shlex
+    return [u.decode('utf-8') for u in shlex.split(unsplit.encode('utf-8'))]
+
+
 def get_alias(cmd, config=None):
     """Return an expanded alias, or None if no alias exists.
 
@@ -642,8 +647,7 @@
         config = bzrlib.config.GlobalConfig()
     alias = config.get_alias(cmd)
     if (alias):
-        import shlex
-        return [a.decode('utf-8') for a in shlex.split(alias.encode('utf-8'))]
+        return shlex_split_unicode(alias)
     return None
 
 

=== modified file 'bzrlib/diff.py'
--- a/bzrlib/diff.py	2007-12-19 21:17:25 +0000
+++ b/bzrlib/diff.py	2007-12-26 21:13:53 +0000
@@ -17,6 +17,7 @@
 import difflib
 import os
 import re
+import shutil
 import sys
 
 from bzrlib.lazy_import import lazy_import
@@ -28,6 +29,7 @@
 
 from bzrlib import (
     bzrdir,
+    commands,
     errors,
     osutils,
     patiencediff,
@@ -467,7 +469,8 @@
                     external_diff_options=None,
                     old_label='a/', new_label='b/',
                     extra_trees=None,
-                    path_encoding='utf8'):
+                    path_encoding='utf8',
+                    using=None):
     """Show in text form the changes from one tree to another.
 
     to_file
@@ -494,9 +497,9 @@
         new_tree.lock_read()
         try:
             differ = DiffTree.from_trees_options(old_tree, new_tree, to_file,
-                                                   path_encoding,
-                                                   external_diff_options,
-                                                   old_label, new_label)
+                                                 path_encoding,
+                                                 external_diff_options,
+                                                 old_label, new_label, using)
             return differ.show_diff(specific_files, extra_trees)
         finally:
             new_tree.unlock()
@@ -565,6 +568,9 @@
         self.to_file = to_file
         self.path_encoding = path_encoding
 
+    def finish(self):
+        pass
+
     @classmethod
     def from_diff_tree(klass, diff_tree):
         return klass(diff_tree.old_tree, diff_tree.new_tree,
@@ -590,6 +596,9 @@
     def __init__(self, differs):
         self.differs = differs
 
+    def finish(self):
+        pass
+
     @classmethod
     def from_diff_tree(klass, diff_tree):
         return klass(diff_tree.differs)
@@ -736,6 +745,76 @@
         return self.CHANGED
 
 
+class DiffFromTool(DiffPath):
+
+    def __init__(self, command_template, old_tree, new_tree, to_file,
+                 path_encoding='utf-8'):
+        DiffPath.__init__(self, old_tree, new_tree, to_file, path_encoding)
+        self.command_template = command_template
+        self._root = tempfile.mkdtemp(prefix='bzr-diff-')
+
+    @classmethod
+    def from_string(klass, command_string, old_tree, new_tree, to_file,
+                    path_encoding='utf-8'):
+        command_template = commands.shlex_split_unicode(command_string)
+        command_template.extend(['%(old_path)s', '%(new_path)s'])
+        return klass(command_template, old_tree, new_tree, to_file,
+                     path_encoding)
+
+    @classmethod
+    def make_from_diff_tree(klass, command_string):
+        def from_diff_tree(diff_tree):
+            return klass.from_string(command_string, diff_tree.old_tree,
+                                     diff_tree.new_tree, diff_tree.to_file)
+        return from_diff_tree
+
+    def _get_command(self, old_path, new_path):
+        my_map = {'old_path': old_path, 'new_path': new_path}
+        return [t % my_map for t in self.command_template]
+
+    def _execute(self, old_path, new_path):
+        proc = subprocess.Popen(self._get_command(old_path, new_path),
+                                stdout=subprocess.PIPE, cwd=self._root)
+        self.to_file.write(proc.stdout.read())
+        return proc.wait()
+
+    def _write_file(self, file_id, tree, prefix, old_path):
+        full_old_path = osutils.pathjoin(self._root, prefix, old_path)
+        parent_dir = osutils.dirname(full_old_path)
+        try:
+            os.makedirs(parent_dir)
+        except OSError, e:
+            if e.errno != errno.EEXIST:
+                raise
+        source = tree.get_file(file_id)
+        try:
+            target = open(full_old_path, 'wb')
+            try:
+                osutils.pumpfile(source, target)
+            finally:
+                target.close()
+        finally:
+            source.close()
+        return full_old_path
+
+    def _prepare_files(self, file_id, old_path, new_path):
+        old_disk_path = self._write_file(file_id, self.old_tree, 'old',
+                                         old_path)
+        new_disk_path = self._write_file(file_id, self.new_tree, 'new',
+                                         new_path)
+        return old_disk_path, new_disk_path
+
+    def finish(self):
+        shutil.rmtree(self._root)
+
+    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
+        if (old_kind, new_kind) != ('file', 'file'):
+            return DiffPath.CANNOT_DIFF
+        self._prepare_files(file_id, old_path, new_path)
+        self._execute(osutils.pathjoin('old', old_path),
+                      osutils.pathjoin('new', new_path))
+
+
 class DiffTree(object):
     """Provides textual representations of the difference between two trees.
 
@@ -781,7 +860,7 @@
     @classmethod
     def from_trees_options(klass, old_tree, new_tree, to_file,
                            path_encoding, external_diff_options, old_label,
-                           new_label):
+                           new_label, using):
         """Factory for producing a DiffTree.
 
         Designed to accept options used by show_diff_trees.
@@ -793,7 +872,12 @@
             binary to perform file comparison, using supplied options.
         :param old_label: Prefix to use for old file labels
         :param new_label: Prefix to use for new file labels
+        :param using: Commandline to use to invoke an external diff tool
         """
+        if using is not None:
+            extra_factories = [DiffFromTool.make_from_diff_tree(using)]
+        else:
+            extra_factories = []
         if external_diff_options:
             assert isinstance(external_diff_options, basestring)
             opts = external_diff_options.split()
@@ -803,7 +887,8 @@
             diff_file = internal_diff
         diff_text = DiffText(old_tree, new_tree, to_file, path_encoding,
                              old_label, new_label, diff_file)
-        return klass(old_tree, new_tree, to_file, path_encoding, diff_text)
+        return klass(old_tree, new_tree, to_file, path_encoding, diff_text,
+                     extra_factories)
 
     def show_diff(self, specific_files, extra_trees=None):
         """Write tree diff to self.to_file
@@ -811,6 +896,13 @@
         :param sepecific_files: the specific files to compare (recursive)
         :param extra_trees: extra trees to use for mapping paths to file_ids
         """
+        try:
+            return self._show_diff(specific_files, extra_trees)
+        finally:
+            for differ in self.differs:
+                differ.finish()
+
+    def _show_diff(self, specific_files, extra_trees):
         # TODO: Generation of pseudo-diffs for added/deleted files could
         # be usefully made into a much faster special case.
         iterator = self.new_tree._iter_changes(self.old_tree,

=== modified file 'bzrlib/tests/test_diff.py'
--- a/bzrlib/tests/test_diff.py	2007-12-19 23:40:39 +0000
+++ b/bzrlib/tests/test_diff.py	2007-12-26 21:13:53 +0000
@@ -21,13 +21,14 @@
 from tempfile import TemporaryFile
 
 from bzrlib.diff import (
-    internal_diff,
-    external_diff,
+    DiffFromTool,
     DiffPath,
-    show_diff_trees,
     DiffSymlink,
     DiffTree,
     DiffText,
+    external_diff,
+    internal_diff,
+    show_diff_trees,
     )
 from bzrlib.errors import BinaryFile, NoDiff
 import bzrlib.osutils as osutils
@@ -1233,3 +1234,50 @@
             from bzrlib._patiencediff_py import recurse_matches_py
             self.assertIs(recurse_matches_py,
                           bzrlib.patiencediff.recurse_matches)
+
+
+class TestDiffFromTool(TestCaseWithTransport):
+
+    def test_from_string(self):
+        diff_obj = DiffFromTool.from_string('diff', None, None, None)
+        self.addCleanup(diff_obj.finish)
+        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
+            diff_obj.command_template)
+        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
+        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
+                         diff_obj.command_template)
+        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
+                         diff_obj._get_command('old-path', 'new-path'))
+
+    def test_execute(self):
+        output = StringIO()
+        diff_obj = DiffFromTool(['python', '-c',
+                                 'print "%(old_path)s %(new_path)s"'],
+                                None, None, output)
+        self.addCleanup(diff_obj.finish)
+        diff_obj._execute('old', 'new')
+        self.assertEqual(output.getvalue(), 'old new\n')
+
+    def test_prepare_files(self):
+        output = StringIO()
+        tree = self.make_branch_and_tree('tree')
+        self.build_tree_contents([('tree/file', 'oldcontent')])
+        tree.add('file', 'file-id')
+        tree.commit('old tree')
+        self.build_tree_contents([('tree/file', 'newcontent')])
+        old_tree = tree.basis_tree()
+        old_tree.lock_read()
+        self.addCleanup(old_tree.unlock)
+        diff_obj = DiffFromTool(['python', '-c',
+                                 'print "%(old_path)s %(new_path)s"'],
+                                old_tree, tree, output)
+        self.addCleanup(diff_obj.finish)
+        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
+        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
+                                                     'newname')
+        self.assertContainsRe(old_path, 'old/oldname$')
+        self.assertContainsRe(new_path, 'new/newname$')
+        self.assertFileEqual('oldcontent', old_path)
+        self.assertFileEqual('newcontent', new_path)
+        # make sure we can create files with the same parent directories
+        diff_obj._prepare_files('file-id', 'oldname2', 'newname2')




More information about the bazaar-commits mailing list