~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-17 17:11:16 UTC
  • mfrom: (4797.2.17 2.1)
  • mto: (4797.2.18 2.1)
  • mto: This revision was merged to the branch mainline in revision 5055.
  • Revision ID: john@arbash-meinel.com-20100217171116-h7t9223ystbnx5h8
merge bzr.2.1 in preparation for NEWS entry.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2006 Canonical Ltd.
 
1
# Copyright (C) 2005-2010 Canonical Ltd.
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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.
444
453
 
445
454
def _patch_header_date(tree, file_id, path):
446
455
    """Returns a timestamp suitable for use in a patch header."""
447
 
    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
448
460
    return timestamp.format_patch_date(mtime)
449
461
 
450
462
 
672
684
    def from_string(klass, command_string, old_tree, new_tree, to_file,
673
685
                    path_encoding='utf-8'):
674
686
        command_template = commands.shlex_split_unicode(command_string)
675
 
        command_template.extend(['%(old_path)s', '%(new_path)s'])
 
687
        if '@' not in command_string:
 
688
            command_template.extend(['@old_path', '@new_path'])
676
689
        return klass(command_template, old_tree, new_tree, to_file,
677
690
                     path_encoding)
678
691
 
685
698
 
686
699
    def _get_command(self, old_path, new_path):
687
700
        my_map = {'old_path': old_path, 'new_path': new_path}
688
 
        return [t % my_map for t in self.command_template]
 
701
        return [AtTemplate(t).substitute(my_map) for t in
 
702
                self.command_template]
689
703
 
690
704
    def _execute(self, old_path, new_path):
691
705
        command = self._get_command(old_path, new_path)
711
725
                raise
712
726
        return True
713
727
 
714
 
    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
        
715
733
        full_path = osutils.pathjoin(self._root, prefix, relpath)
716
 
        if self._try_symlink_root(tree, prefix):
 
734
        if not force_temp and self._try_symlink_root(tree, prefix):
717
735
            return full_path
718
736
        parent_dir = osutils.dirname(full_path)
719
737
        try:
730
748
                target.close()
731
749
        finally:
732
750
            source.close()
733
 
        osutils.make_readonly(full_path)
734
 
        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
735
757
        os.utime(full_path, (mtime, mtime))
736
758
        return full_path
737
759
 
738
 
    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):
739
762
        old_disk_path = self._write_file(file_id, self.old_tree, 'old',
740
 
                                         old_path)
 
763
                                         old_path, force_temp)
741
764
        new_disk_path = self._write_file(file_id, self.new_tree, 'new',
742
 
                                         new_path)
 
765
                                         new_path, force_temp,
 
766
                                         allow_write=allow_write_new)
743
767
        return old_disk_path, new_disk_path
744
768
 
745
769
    def finish(self):
753
777
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
754
778
        if (old_kind, new_kind) != ('file', 'file'):
755
779
            return DiffPath.CANNOT_DIFF
756
 
        self._prepare_files(file_id, old_path, new_path)
757
 
        self._execute(osutils.pathjoin('old', old_path),
758
 
                      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()
759
806
 
760
807
 
761
808
class DiffTree(object):