13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24
23
from bzrlib.lazy_import import lazy_import
43
from bzrlib.workingtree import WorkingTree
46
43
from bzrlib.symbol_versioning import (
49
47
from bzrlib.trace import mutter, note, warning
52
class AtTemplate(string.Template):
53
"""Templating class that uses @ instead of $."""
58
50
# TODO: Rather than building a changeset object, we should probably
59
51
# invoke callbacks on an object. That object can either accumulate a
60
52
# list, write them out directly, etc etc.
308
295
if True and a view is set, apply the view or check that the paths
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.
298
a tuple of (old_tree, new_tree, specific_files, extra_trees) where
299
extra_trees is a sequence of additional trees to search in for
315
302
# Get the old and new revision specs
316
303
old_revision_spec = None
384
369
specific_files = view_files
385
370
view_str = views.view_display_str(view_files)
386
note("*** Ignoring files outside view. View is %s" % view_str)
371
note("*** ignoring files outside view: %s" % view_str)
388
373
# Get extra trees that ought to be searched for file-ids
389
374
extra_trees = None
390
375
if working_tree is not None and working_tree not in (old_tree, new_tree):
391
376
extra_trees = (working_tree,)
392
return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
377
return old_tree, new_tree, specific_files, extra_trees
395
379
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
396
380
if branch is None and tree is not None:
454
438
def _patch_header_date(tree, file_id, path):
455
439
"""Returns a timestamp suitable for use in a patch header."""
457
mtime = tree.get_file_mtime(file_id, path)
458
except errors.FileTimestampUnavailable:
440
mtime = tree.get_file_mtime(file_id, path)
460
441
return timestamp.format_patch_date(mtime)
444
@deprecated_function(one_three)
445
def get_prop_change(meta_modified):
447
return " (properties changed)"
463
451
def get_executable_change(old_is_x, new_is_x):
464
452
descr = { True:"+x", False:"-x", None:"??" }
465
453
if old_is_x != new_is_x:
640
628
return self.CANNOT_DIFF
641
629
from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
642
630
to_label = '%s%s\t%s' % (self.new_label, new_path, new_date)
643
return self.diff_text(from_file_id, to_file_id, from_label, to_label,
631
return self.diff_text(from_file_id, to_file_id, from_label, to_label)
646
def diff_text(self, from_file_id, to_file_id, from_label, to_label,
647
from_path=None, to_path=None):
633
def diff_text(self, from_file_id, to_file_id, from_label, to_label):
648
634
"""Diff the content of given files in two trees
650
636
:param from_file_id: The id of the file in the from tree. If None,
652
638
:param to_file_id: The id of the file in the to tree. This may refer
653
639
to a different file from from_file_id. If None,
654
640
the file is not present in the to tree.
655
:param from_path: The path in the from tree or None if unknown.
656
:param to_path: The path in the to tree or None if unknown.
658
def _get_text(tree, file_id, path):
642
def _get_text(tree, file_id):
659
643
if file_id is not None:
660
return tree.get_file(file_id, path).readlines()
644
return tree.get_file(file_id).readlines()
664
from_text = _get_text(self.old_tree, from_file_id, from_path)
665
to_text = _get_text(self.new_tree, to_file_id, to_path)
648
from_text = _get_text(self.old_tree, from_file_id)
649
to_text = _get_text(self.new_tree, to_file_id)
666
650
self.text_differ(from_label, from_text, to_label, to_text,
668
652
except errors.BinaryFile:
684
668
def from_string(klass, command_string, old_tree, new_tree, to_file,
685
669
path_encoding='utf-8'):
686
670
command_template = commands.shlex_split_unicode(command_string)
687
if '@' not in command_string:
688
command_template.extend(['@old_path', '@new_path'])
671
command_template.extend(['%(old_path)s', '%(new_path)s'])
689
672
return klass(command_template, old_tree, new_tree, to_file,
699
682
def _get_command(self, old_path, new_path):
700
683
my_map = {'old_path': old_path, 'new_path': new_path}
701
return [AtTemplate(t).substitute(my_map) for t in
702
self.command_template]
684
return [t % my_map for t in self.command_template]
704
686
def _execute(self, old_path, new_path):
705
687
command = self._get_command(old_path, new_path)
728
def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
730
if not force_temp and isinstance(tree, WorkingTree):
731
return tree.abspath(tree.id2path(file_id))
710
def _write_file(self, file_id, tree, prefix, relpath):
733
711
full_path = osutils.pathjoin(self._root, prefix, relpath)
734
if not force_temp and self._try_symlink_root(tree, prefix):
712
if self._try_symlink_root(tree, prefix):
736
714
parent_dir = osutils.dirname(full_path)
752
osutils.make_readonly(full_path)
754
mtime = tree.get_file_mtime(file_id)
755
except errors.FileTimestampUnavailable:
729
osutils.make_readonly(full_path)
730
mtime = tree.get_file_mtime(file_id)
757
731
os.utime(full_path, (mtime, mtime))
760
def _prepare_files(self, file_id, old_path, new_path, force_temp=False,
761
allow_write_new=False):
734
def _prepare_files(self, file_id, old_path, new_path):
762
735
old_disk_path = self._write_file(file_id, self.old_tree, 'old',
763
old_path, force_temp)
764
737
new_disk_path = self._write_file(file_id, self.new_tree, 'new',
765
new_path, force_temp,
766
allow_write=allow_write_new)
767
739
return old_disk_path, new_disk_path
769
741
def finish(self):
771
osutils.rmtree(self._root)
773
if e.errno != errno.ENOENT:
774
mutter("The temporary directory \"%s\" was not "
775
"cleanly removed: %s." % (self._root, e))
742
osutils.rmtree(self._root)
777
744
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
778
745
if (old_kind, new_kind) != ('file', 'file'):
779
746
return DiffPath.CANNOT_DIFF
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)
784
def edit_file(self, file_id):
785
"""Use this tool to edit a file.
787
A temporary copy will be edited, and the new contents will be
790
:param file_id: The id of the file to edit.
791
:return: The new contents of the file.
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,
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')
803
return new_file.read()
747
self._prepare_files(file_id, old_path, new_path)
748
self._execute(osutils.pathjoin('old', old_path),
749
osutils.pathjoin('new', new_path))
808
752
class DiffTree(object):
967
911
new_kind = self.new_tree.kind(file_id)
968
912
except (errors.NoSuchId, errors.NoSuchFile):
970
self._diff(file_id, old_path, new_path, old_kind, new_kind)
973
def _diff(self, file_id, old_path, new_path, old_kind, new_kind):
974
915
result = DiffPath._diff_many(self.differs, file_id, old_path,
975
916
new_path, old_kind, new_kind)
976
917
if result is DiffPath.CANNOT_DIFF: