1
# Copyright (C) 2004, 2005, 2006 Canonical Ltd.
1
# Copyright (C) 2005-2010 Canonical Ltd.
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
46
49
from bzrlib.trace import mutter, note, warning
52
class AtTemplate(string.Template):
53
"""Templating class that uses @ instead of $."""
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.
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)
457
mtime = tree.get_file_mtime(file_id, path)
458
except errors.FileTimestampUnavailable:
448
460
return timestamp.format_patch_date(mtime)
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,
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]
690
704
def _execute(self, old_path, new_path):
691
705
command = self._get_command(old_path, new_path)
714
def _write_file(self, file_id, tree, prefix, relpath):
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))
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):
718
736
parent_dir = osutils.dirname(full_path)
733
osutils.make_readonly(full_path)
734
mtime = tree.get_file_mtime(file_id)
752
osutils.make_readonly(full_path)
754
mtime = tree.get_file_mtime(file_id)
755
except errors.FileTimestampUnavailable:
735
757
os.utime(full_path, (mtime, mtime))
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',
763
old_path, force_temp)
741
764
new_disk_path = self._write_file(file_id, self.new_tree, 'new',
765
new_path, force_temp,
766
allow_write=allow_write_new)
743
767
return old_disk_path, new_disk_path
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)
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()
761
808
class DiffTree(object):