~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: Jelmer Vernooij
  • Date: 2011-05-10 07:46:15 UTC
  • mfrom: (5844 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5845.
  • Revision ID: jelmer@samba.org-20110510074615-eptod049ndjxc4i7
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
17
import difflib
20
18
import os
21
19
import re
29
27
import tempfile
30
28
 
31
29
from bzrlib import (
 
30
    bzrdir,
 
31
    cmdline,
32
32
    cleanup,
33
 
    cmdline,
34
 
    controldir,
35
33
    errors,
36
34
    osutils,
37
35
    patiencediff,
41
39
    )
42
40
 
43
41
from bzrlib.workingtree import WorkingTree
44
 
from bzrlib.i18n import gettext
45
42
""")
46
43
 
47
44
from bzrlib.registry import (
48
45
    Registry,
49
46
    )
 
47
from bzrlib.symbol_versioning import (
 
48
    deprecated_function,
 
49
    deprecated_in,
 
50
    )
50
51
from bzrlib.trace import mutter, note, warning
51
52
 
52
53
 
287
288
                        new_abspath, e)
288
289
 
289
290
 
 
291
@deprecated_function(deprecated_in((2, 2, 0)))
 
292
def get_trees_and_branches_to_diff(path_list, revision_specs, old_url, new_url,
 
293
                                   apply_view=True):
 
294
    """Get the trees and specific files to diff given a list of paths.
 
295
 
 
296
    This method works out the trees to be diff'ed and the files of
 
297
    interest within those trees.
 
298
 
 
299
    :param path_list:
 
300
        the list of arguments passed to the diff command
 
301
    :param revision_specs:
 
302
        Zero, one or two RevisionSpecs from the diff command line,
 
303
        saying what revisions to compare.
 
304
    :param old_url:
 
305
        The url of the old branch or tree. If None, the tree to use is
 
306
        taken from the first path, if any, or the current working tree.
 
307
    :param new_url:
 
308
        The url of the new branch or tree. If None, the tree to use is
 
309
        taken from the first path, if any, or the current working tree.
 
310
    :param apply_view:
 
311
        if True and a view is set, apply the view or check that the paths
 
312
        are within it
 
313
    :returns:
 
314
        a tuple of (old_tree, new_tree, old_branch, new_branch,
 
315
        specific_files, extra_trees) where extra_trees is a sequence of
 
316
        additional trees to search in for file-ids.  The trees and branches
 
317
        are not locked.
 
318
    """
 
319
    op = cleanup.OperationWithCleanups(get_trees_and_branches_to_diff_locked)
 
320
    return op.run_simple(path_list, revision_specs, old_url, new_url,
 
321
            op.add_cleanup, apply_view=apply_view)
 
322
    
 
323
 
290
324
def get_trees_and_branches_to_diff_locked(
291
325
    path_list, revision_specs, old_url, new_url, add_cleanup, apply_view=True):
292
326
    """Get the trees and specific files to diff given a list of paths.
358
392
    if old_url is None:
359
393
        old_url = default_location
360
394
    working_tree, branch, relpath = \
361
 
        controldir.ControlDir.open_containing_tree_or_branch(old_url)
 
395
        bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
362
396
    lock_tree_or_branch(working_tree, branch)
363
397
    if consider_relpath and relpath != '':
364
398
        if working_tree is not None and apply_view:
372
406
        new_url = default_location
373
407
    if new_url != old_url:
374
408
        working_tree, branch, relpath = \
375
 
            controldir.ControlDir.open_containing_tree_or_branch(new_url)
 
409
            bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
376
410
        lock_tree_or_branch(working_tree, branch)
377
411
        if consider_relpath and relpath != '':
378
412
            if working_tree is not None and apply_view:
396
430
            if view_files:
397
431
                specific_files = view_files
398
432
                view_str = views.view_display_str(view_files)
399
 
                note(gettext("*** Ignoring files outside view. View is %s") % view_str)
 
433
                note("*** Ignoring files outside view. View is %s" % view_str)
400
434
 
401
435
    # Get extra trees that ought to be searched for file-ids
402
436
    extra_trees = None
403
437
    if working_tree is not None and working_tree not in (old_tree, new_tree):
404
438
        extra_trees = (working_tree,)
405
 
    return (old_tree, new_tree, old_branch, new_branch,
406
 
            specific_files, extra_trees)
 
439
    return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
407
440
 
408
441
 
409
442
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
430
463
    """Show in text form the changes from one tree to another.
431
464
 
432
465
    :param to_file: The output stream.
433
 
    :param specific_files: Include only changes to these files - None for all
 
466
    :param specific_files:Include only changes to these files - None for all
434
467
        changes.
435
468
    :param external_diff_options: If set, use an external GNU diff and pass 
436
469
        these options.
712
745
 
713
746
    def _get_command(self, old_path, new_path):
714
747
        my_map = {'old_path': old_path, 'new_path': new_path}
715
 
        command = [AtTemplate(t).substitute(my_map) for t in
716
 
                   self.command_template]
717
 
        if sys.platform == 'win32': # Popen doesn't accept unicode on win32
718
 
            command_encoded = []
719
 
            for c in command:
720
 
                if isinstance(c, unicode):
721
 
                    command_encoded.append(c.encode('mbcs'))
722
 
                else:
723
 
                    command_encoded.append(c)
724
 
            return command_encoded
725
 
        else:
726
 
            return command
 
748
        return [AtTemplate(t).substitute(my_map) for t in
 
749
                self.command_template]
727
750
 
728
751
    def _execute(self, old_path, new_path):
729
752
        command = self._get_command(old_path, new_path)
749
772
                raise
750
773
        return True
751
774
 
752
 
    @staticmethod
753
 
    def _fenc():
754
 
        """Returns safe encoding for passing file path to diff tool"""
755
 
        if sys.platform == 'win32':
756
 
            return 'mbcs'
757
 
        else:
758
 
            # Don't fallback to 'utf-8' because subprocess may not be able to
759
 
            # handle utf-8 correctly when locale is not utf-8.
760
 
            return sys.getfilesystemencoding() or 'ascii'
761
 
 
762
 
    def _is_safepath(self, path):
763
 
        """Return true if `path` may be able to pass to subprocess."""
764
 
        fenc = self._fenc()
765
 
        try:
766
 
            return path == path.encode(fenc).decode(fenc)
767
 
        except UnicodeError:
768
 
            return False
769
 
 
770
 
    def _safe_filename(self, prefix, relpath):
771
 
        """Replace unsafe character in `relpath` then join `self._root`,
772
 
        `prefix` and `relpath`."""
773
 
        fenc = self._fenc()
774
 
        # encoded_str.replace('?', '_') may break multibyte char.
775
 
        # So we should encode, decode, then replace(u'?', u'_')
776
 
        relpath_tmp = relpath.encode(fenc, 'replace').decode(fenc, 'replace')
777
 
        relpath_tmp = relpath_tmp.replace(u'?', u'_')
778
 
        return osutils.pathjoin(self._root, prefix, relpath_tmp)
779
 
 
780
775
    def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
781
776
                    allow_write=False):
782
777
        if not force_temp and isinstance(tree, WorkingTree):
783
 
            full_path = tree.abspath(tree.id2path(file_id))
784
 
            if self._is_safepath(full_path):
785
 
                return full_path
786
 
 
787
 
        full_path = self._safe_filename(prefix, relpath)
 
778
            return tree.abspath(tree.id2path(file_id))
 
779
        
 
780
        full_path = osutils.pathjoin(self._root, prefix, relpath)
788
781
        if not force_temp and self._try_symlink_root(tree, prefix):
789
782
            return full_path
790
783
        parent_dir = osutils.dirname(full_path)
847
840
        """
848
841
        old_path = self.old_tree.id2path(file_id)
849
842
        new_path = self.new_tree.id2path(file_id)
850
 
        old_abs_path, new_abs_path = self._prepare_files(
851
 
                                            file_id, old_path, new_path,
852
 
                                            allow_write_new=True,
853
 
                                            force_temp=True)
854
 
        command = self._get_command(old_abs_path, new_abs_path)
 
843
        new_abs_path = self._prepare_files(file_id, old_path, new_path,
 
844
                                           allow_write_new=True,
 
845
                                           force_temp=True)[1]
 
846
        command = self._get_command(osutils.pathjoin('old', old_path),
 
847
                                    osutils.pathjoin('new', new_path))
855
848
        subprocess.call(command, cwd=self._root)
856
 
        new_file = open(new_abs_path, 'rb')
 
849
        new_file = open(new_abs_path, 'r')
857
850
        try:
858
851
            return new_file.read()
859
852
        finally:
909
902
        """Factory for producing a DiffTree.
910
903
 
911
904
        Designed to accept options used by show_diff_trees.
912
 
 
913
905
        :param old_tree: The tree to show as old in the comparison
914
906
        :param new_tree: The tree to show as new in the comparison
915
907
        :param to_file: File to write comparisons to