~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: John Arbash Meinel
  • Date: 2010-02-10 17:52:08 UTC
  • mfrom: (5021 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5023.
  • Revision ID: john@arbash-meinel.com-20100210175208-bubuwav4uqigu291
Merge bzr.dev 5021 to resolve NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
import os
19
19
import re
20
20
import shutil
 
21
import string
21
22
import sys
22
23
 
23
24
from bzrlib.lazy_import import lazy_import
38
39
    timestamp,
39
40
    views,
40
41
    )
 
42
 
 
43
from bzrlib.workingtree import WorkingTree
41
44
""")
42
45
 
43
46
from bzrlib.symbol_versioning import (
46
49
from bzrlib.trace import mutter, note, warning
47
50
 
48
51
 
 
52
class AtTemplate(string.Template):
 
53
    """Templating class that uses @ instead of $."""
 
54
 
 
55
    delimiter = '@'
 
56
 
 
57
 
49
58
# TODO: Rather than building a changeset object, we should probably
50
59
# invoke callbacks on an object.  That object can either accumulate a
51
60
# list, write them out directly, etc etc.
171
180
 
172
181
        if not diff_opts:
173
182
            diff_opts = []
 
183
        if sys.platform == 'win32':
 
184
            # Popen doesn't do the proper encoding for external commands
 
185
            # Since we are dealing with an ANSI api, use mbcs encoding
 
186
            old_filename = old_filename.encode('mbcs')
 
187
            new_filename = new_filename.encode('mbcs')
174
188
        diffcmd = ['diff',
175
189
                   '--label', old_filename,
176
190
                   old_abspath,
272
286
                        new_abspath, e)
273
287
 
274
288
 
275
 
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url,
276
 
    apply_view=True):
 
289
def get_trees_and_branches_to_diff(path_list, revision_specs, old_url, new_url,
 
290
                                   apply_view=True):
277
291
    """Get the trees and specific files to diff given a list of paths.
278
292
 
279
293
    This method works out the trees to be diff'ed and the files of
294
308
        if True and a view is set, apply the view or check that the paths
295
309
        are within it
296
310
    :returns:
297
 
        a tuple of (old_tree, new_tree, specific_files, extra_trees) where
298
 
        extra_trees is a sequence of additional trees to search in for
299
 
        file-ids.
 
311
        a tuple of (old_tree, new_tree, old_branch, new_branch,
 
312
        specific_files, extra_trees) where extra_trees is a sequence of
 
313
        additional trees to search in for file-ids.
300
314
    """
301
315
    # Get the old and new revision specs
302
316
    old_revision_spec = None
336
350
            views.check_path_in_view(working_tree, relpath)
337
351
        specific_files.append(relpath)
338
352
    old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
 
353
    old_branch = branch
339
354
 
340
355
    # Get the new location
341
356
    if new_url is None:
349
364
            specific_files.append(relpath)
350
365
    new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
351
366
        basis_is_default=working_tree is None)
 
367
    new_branch = branch
352
368
 
353
369
    # Get the specific files (all files is None, no files is [])
354
370
    if make_paths_wt_relative and working_tree is not None:
373
389
    extra_trees = None
374
390
    if working_tree is not None and working_tree not in (old_tree, new_tree):
375
391
        extra_trees = (working_tree,)
376
 
    return old_tree, new_tree, specific_files, extra_trees
 
392
    return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
 
393
 
377
394
 
378
395
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
379
396
    if branch is None and tree is not None:
436
453
 
437
454
def _patch_header_date(tree, file_id, path):
438
455
    """Returns a timestamp suitable for use in a patch header."""
439
 
    mtime = tree.get_file_mtime(file_id, path)
 
456
    try:
 
457
        mtime = tree.get_file_mtime(file_id, path)
 
458
    except errors.FileTimestampUnavailable:
 
459
        mtime = 0
440
460
    return timestamp.format_patch_date(mtime)
441
461
 
442
462
 
664
684
    def from_string(klass, command_string, old_tree, new_tree, to_file,
665
685
                    path_encoding='utf-8'):
666
686
        command_template = commands.shlex_split_unicode(command_string)
667
 
        command_template.extend(['%(old_path)s', '%(new_path)s'])
 
687
        if '@' not in command_string:
 
688
            command_template.extend(['@old_path', '@new_path'])
668
689
        return klass(command_template, old_tree, new_tree, to_file,
669
690
                     path_encoding)
670
691
 
677
698
 
678
699
    def _get_command(self, old_path, new_path):
679
700
        my_map = {'old_path': old_path, 'new_path': new_path}
680
 
        return [t % my_map for t in self.command_template]
 
701
        return [AtTemplate(t).substitute(my_map) for t in
 
702
                self.command_template]
681
703
 
682
704
    def _execute(self, old_path, new_path):
683
705
        command = self._get_command(old_path, new_path)
703
725
                raise
704
726
        return True
705
727
 
706
 
    def _write_file(self, file_id, tree, prefix, relpath):
 
728
    def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
 
729
                    allow_write=False):
 
730
        if not force_temp and isinstance(tree, WorkingTree):
 
731
            return tree.abspath(tree.id2path(file_id))
 
732
        
707
733
        full_path = osutils.pathjoin(self._root, prefix, relpath)
708
 
        if self._try_symlink_root(tree, prefix):
 
734
        if not force_temp and self._try_symlink_root(tree, prefix):
709
735
            return full_path
710
736
        parent_dir = osutils.dirname(full_path)
711
737
        try:
722
748
                target.close()
723
749
        finally:
724
750
            source.close()
725
 
        osutils.make_readonly(full_path)
726
 
        mtime = tree.get_file_mtime(file_id)
 
751
        if not allow_write:
 
752
            osutils.make_readonly(full_path)
 
753
        try:
 
754
            mtime = tree.get_file_mtime(file_id)
 
755
        except errors.FileTimestampUnavailable:
 
756
            mtime = 0
727
757
        os.utime(full_path, (mtime, mtime))
728
758
        return full_path
729
759
 
730
 
    def _prepare_files(self, file_id, old_path, new_path):
 
760
    def _prepare_files(self, file_id, old_path, new_path, force_temp=False,
 
761
                       allow_write_new=False):
731
762
        old_disk_path = self._write_file(file_id, self.old_tree, 'old',
732
 
                                         old_path)
 
763
                                         old_path, force_temp)
733
764
        new_disk_path = self._write_file(file_id, self.new_tree, 'new',
734
 
                                         new_path)
 
765
                                         new_path, force_temp,
 
766
                                         allow_write=allow_write_new)
735
767
        return old_disk_path, new_disk_path
736
768
 
737
769
    def finish(self):
745
777
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
746
778
        if (old_kind, new_kind) != ('file', 'file'):
747
779
            return DiffPath.CANNOT_DIFF
748
 
        self._prepare_files(file_id, old_path, new_path)
749
 
        self._execute(osutils.pathjoin('old', old_path),
750
 
                      osutils.pathjoin('new', new_path))
 
780
        (old_disk_path, new_disk_path) = self._prepare_files(
 
781
                                                file_id, old_path, new_path)
 
782
        self._execute(old_disk_path, new_disk_path)
 
783
 
 
784
    def edit_file(self, file_id):
 
785
        """Use this tool to edit a file.
 
786
 
 
787
        A temporary copy will be edited, and the new contents will be
 
788
        returned.
 
789
 
 
790
        :param file_id: The id of the file to edit.
 
791
        :return: The new contents of the file.
 
792
        """
 
793
        old_path = self.old_tree.id2path(file_id)
 
794
        new_path = self.new_tree.id2path(file_id)
 
795
        new_abs_path = self._prepare_files(file_id, old_path, new_path,
 
796
                                           allow_write_new=True,
 
797
                                           force_temp=True)[1]
 
798
        command = self._get_command(osutils.pathjoin('old', old_path),
 
799
                                    osutils.pathjoin('new', new_path))
 
800
        subprocess.call(command, cwd=self._root)
 
801
        new_file = open(new_abs_path, 'r')
 
802
        try:
 
803
            return new_file.read()
 
804
        finally:
 
805
            new_file.close()
751
806
 
752
807
 
753
808
class DiffTree(object):