~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: John Arbash Meinel
  • Date: 2009-12-10 17:16:19 UTC
  • mfrom: (4884 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4889.
  • Revision ID: john@arbash-meinel.com-20091210171619-ehdcxjbl8afhq9g1
Bring in bzr.dev 4884

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.
277
286
                        new_abspath, e)
278
287
 
279
288
 
280
 
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url,
281
 
    apply_view=True):
 
289
def get_trees_and_branches_to_diff(path_list, revision_specs, old_url, new_url,
 
290
                                   apply_view=True):
282
291
    """Get the trees and specific files to diff given a list of paths.
283
292
 
284
293
    This method works out the trees to be diff'ed and the files of
299
308
        if True and a view is set, apply the view or check that the paths
300
309
        are within it
301
310
    :returns:
302
 
        a tuple of (old_tree, new_tree, specific_files, extra_trees) where
303
 
        extra_trees is a sequence of additional trees to search in for
304
 
        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.
305
314
    """
306
315
    # Get the old and new revision specs
307
316
    old_revision_spec = None
341
350
            views.check_path_in_view(working_tree, relpath)
342
351
        specific_files.append(relpath)
343
352
    old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
 
353
    old_branch = branch
344
354
 
345
355
    # Get the new location
346
356
    if new_url is None:
354
364
            specific_files.append(relpath)
355
365
    new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
356
366
        basis_is_default=working_tree is None)
 
367
    new_branch = branch
357
368
 
358
369
    # Get the specific files (all files is None, no files is [])
359
370
    if make_paths_wt_relative and working_tree is not None:
378
389
    extra_trees = None
379
390
    if working_tree is not None and working_tree not in (old_tree, new_tree):
380
391
        extra_trees = (working_tree,)
381
 
    return old_tree, new_tree, specific_files, extra_trees
 
392
    return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
 
393
 
382
394
 
383
395
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
384
396
    if branch is None and tree is not None:
669
681
    def from_string(klass, command_string, old_tree, new_tree, to_file,
670
682
                    path_encoding='utf-8'):
671
683
        command_template = commands.shlex_split_unicode(command_string)
672
 
        command_template.extend(['%(old_path)s', '%(new_path)s'])
 
684
        if '@' not in command_string:
 
685
            command_template.extend(['@old_path', '@new_path'])
673
686
        return klass(command_template, old_tree, new_tree, to_file,
674
687
                     path_encoding)
675
688
 
682
695
 
683
696
    def _get_command(self, old_path, new_path):
684
697
        my_map = {'old_path': old_path, 'new_path': new_path}
685
 
        return [t % my_map for t in self.command_template]
 
698
        return [AtTemplate(t).substitute(my_map) for t in
 
699
                self.command_template]
686
700
 
687
701
    def _execute(self, old_path, new_path):
688
702
        command = self._get_command(old_path, new_path)
708
722
                raise
709
723
        return True
710
724
 
711
 
    def _write_file(self, file_id, tree, prefix, relpath):
 
725
    def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
 
726
                    allow_write=False):
 
727
        if not force_temp and isinstance(tree, WorkingTree):
 
728
            return tree.abspath(tree.id2path(file_id))
 
729
        
712
730
        full_path = osutils.pathjoin(self._root, prefix, relpath)
713
 
        if self._try_symlink_root(tree, prefix):
 
731
        if not force_temp and self._try_symlink_root(tree, prefix):
714
732
            return full_path
715
733
        parent_dir = osutils.dirname(full_path)
716
734
        try:
727
745
                target.close()
728
746
        finally:
729
747
            source.close()
730
 
        osutils.make_readonly(full_path)
 
748
        if not allow_write:
 
749
            osutils.make_readonly(full_path)
731
750
        mtime = tree.get_file_mtime(file_id)
732
751
        os.utime(full_path, (mtime, mtime))
733
752
        return full_path
734
753
 
735
 
    def _prepare_files(self, file_id, old_path, new_path):
 
754
    def _prepare_files(self, file_id, old_path, new_path, force_temp=False,
 
755
                       allow_write_new=False):
736
756
        old_disk_path = self._write_file(file_id, self.old_tree, 'old',
737
 
                                         old_path)
 
757
                                         old_path, force_temp)
738
758
        new_disk_path = self._write_file(file_id, self.new_tree, 'new',
739
 
                                         new_path)
 
759
                                         new_path, force_temp,
 
760
                                         allow_write=allow_write_new)
740
761
        return old_disk_path, new_disk_path
741
762
 
742
763
    def finish(self):
750
771
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
751
772
        if (old_kind, new_kind) != ('file', 'file'):
752
773
            return DiffPath.CANNOT_DIFF
753
 
        self._prepare_files(file_id, old_path, new_path)
754
 
        self._execute(osutils.pathjoin('old', old_path),
755
 
                      osutils.pathjoin('new', new_path))
 
774
        (old_disk_path, new_disk_path) = self._prepare_files(
 
775
                                                file_id, old_path, new_path)
 
776
        self._execute(old_disk_path, new_disk_path)
 
777
 
 
778
    def edit_file(self, file_id):
 
779
        """Use this tool to edit a file.
 
780
 
 
781
        A temporary copy will be edited, and the new contents will be
 
782
        returned.
 
783
 
 
784
        :param file_id: The id of the file to edit.
 
785
        :return: The new contents of the file.
 
786
        """
 
787
        old_path = self.old_tree.id2path(file_id)
 
788
        new_path = self.new_tree.id2path(file_id)
 
789
        new_abs_path = self._prepare_files(file_id, old_path, new_path,
 
790
                                           allow_write_new=True,
 
791
                                           force_temp=True)[1]
 
792
        command = self._get_command(osutils.pathjoin('old', old_path),
 
793
                                    osutils.pathjoin('new', new_path))
 
794
        subprocess.call(command, cwd=self._root)
 
795
        new_file = open(new_abs_path, 'r')
 
796
        try:
 
797
            return new_file.read()
 
798
        finally:
 
799
            new_file.close()
756
800
 
757
801
 
758
802
class DiffTree(object):