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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
24
from bzrlib.lazy_import import lazy_import
43
from bzrlib.workingtree import WorkingTree
42
46
from bzrlib.symbol_versioning import (
46
from bzrlib.trace import warning
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
290
305
The url of the new branch or tree. If None, the tree to use is
291
306
taken from the first path, if any, or the current working tree.
308
if True and a view is set, apply the view or check that the paths
293
a tuple of (old_tree, new_tree, specific_files, extra_trees) where
294
extra_trees is a sequence of additional trees to search in for
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.
297
315
# Get the old and new revision specs
298
316
old_revision_spec = None
328
346
working_tree, branch, relpath = \
329
347
bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
330
348
if consider_relpath and relpath != '':
349
if working_tree is not None and apply_view:
350
views.check_path_in_view(working_tree, relpath)
331
351
specific_files.append(relpath)
332
352
old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
334
355
# Get the new location
335
356
if new_url is None:
338
359
working_tree, branch, relpath = \
339
360
bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
340
361
if consider_relpath and relpath != '':
362
if working_tree is not None and apply_view:
363
views.check_path_in_view(working_tree, relpath)
341
364
specific_files.append(relpath)
342
365
new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
343
366
basis_is_default=working_tree is None)
345
369
# Get the specific files (all files is None, no files is [])
346
370
if make_paths_wt_relative and working_tree is not None:
347
other_paths = _relative_paths_in_tree(working_tree, other_paths)
372
from bzrlib.builtins import safe_relpath_files
373
other_paths = safe_relpath_files(working_tree, other_paths,
374
apply_view=apply_view)
375
except errors.FileInWrongBranch:
376
raise errors.BzrCommandError("Files are in different branches")
348
377
specific_files.extend(other_paths)
349
378
if len(specific_files) == 0:
350
379
specific_files = None
380
if (working_tree is not None and working_tree.supports_views()
382
view_files = working_tree.views.lookup_view()
384
specific_files = view_files
385
view_str = views.view_display_str(view_files)
386
note("*** Ignoring files outside view. View is %s" % view_str)
352
388
# Get extra trees that ought to be searched for file-ids
353
389
extra_trees = None
354
390
if working_tree is not None and working_tree not in (old_tree, new_tree):
355
391
extra_trees = (working_tree,)
356
return old_tree, new_tree, specific_files, extra_trees
392
return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
359
395
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
370
406
return spec.as_tree(branch)
373
def _relative_paths_in_tree(tree, paths):
374
"""Get the relative paths within a working tree.
376
Each path may be either an absolute path or a path relative to the
377
current working directory.
380
for filename in paths:
382
result.append(tree.relpath(osutils.dereference_path(filename)))
383
except errors.PathNotChild:
384
raise errors.BzrCommandError("Files are in different branches")
388
409
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
389
410
external_diff_options=None,
390
411
old_label='a/', new_label='b/',
623
637
return self.CANNOT_DIFF
624
638
from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
625
639
to_label = '%s%s\t%s' % (self.new_label, new_path, new_date)
626
return self.diff_text(from_file_id, to_file_id, from_label, to_label)
640
return self.diff_text(from_file_id, to_file_id, from_label, to_label,
628
def diff_text(self, from_file_id, to_file_id, from_label, to_label):
643
def diff_text(self, from_file_id, to_file_id, from_label, to_label,
644
from_path=None, to_path=None):
629
645
"""Diff the content of given files in two trees
631
647
:param from_file_id: The id of the file in the from tree. If None,
633
649
:param to_file_id: The id of the file in the to tree. This may refer
634
650
to a different file from from_file_id. If None,
635
651
the file is not present in the to tree.
652
:param from_path: The path in the from tree or None if unknown.
653
:param to_path: The path in the to tree or None if unknown.
637
def _get_text(tree, file_id):
655
def _get_text(tree, file_id, path):
638
656
if file_id is not None:
639
return tree.get_file(file_id).readlines()
657
return tree.get_file(file_id, path).readlines()
643
from_text = _get_text(self.old_tree, from_file_id)
644
to_text = _get_text(self.new_tree, to_file_id)
661
from_text = _get_text(self.old_tree, from_file_id, from_path)
662
to_text = _get_text(self.new_tree, to_file_id, to_path)
645
663
self.text_differ(from_label, from_text, to_label, to_text,
647
665
except errors.BinaryFile:
663
681
def from_string(klass, command_string, old_tree, new_tree, to_file,
664
682
path_encoding='utf-8'):
665
683
command_template = commands.shlex_split_unicode(command_string)
666
command_template.extend(['%(old_path)s', '%(new_path)s'])
684
if '@' not in command_string:
685
command_template.extend(['@old_path', '@new_path'])
667
686
return klass(command_template, old_tree, new_tree, to_file,
677
696
def _get_command(self, old_path, new_path):
678
697
my_map = {'old_path': old_path, 'new_path': new_path}
679
return [t % my_map for t in self.command_template]
698
return [AtTemplate(t).substitute(my_map) for t in
699
self.command_template]
681
701
def _execute(self, old_path, new_path):
682
702
command = self._get_command(old_path, new_path)
705
def _write_file(self, file_id, tree, prefix, relpath):
725
def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
727
if not force_temp and isinstance(tree, WorkingTree):
728
return tree.abspath(tree.id2path(file_id))
706
730
full_path = osutils.pathjoin(self._root, prefix, relpath)
707
if self._try_symlink_root(tree, prefix):
731
if not force_temp and self._try_symlink_root(tree, prefix):
709
733
parent_dir = osutils.dirname(full_path)
724
osutils.make_readonly(full_path)
749
osutils.make_readonly(full_path)
725
750
mtime = tree.get_file_mtime(file_id)
726
751
os.utime(full_path, (mtime, mtime))
729
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):
730
756
old_disk_path = self._write_file(file_id, self.old_tree, 'old',
757
old_path, force_temp)
732
758
new_disk_path = self._write_file(file_id, self.new_tree, 'new',
759
new_path, force_temp,
760
allow_write=allow_write_new)
734
761
return old_disk_path, new_disk_path
736
763
def finish(self):
737
osutils.rmtree(self._root)
765
osutils.rmtree(self._root)
767
if e.errno != errno.ENOENT:
768
mutter("The temporary directory \"%s\" was not "
769
"cleanly removed: %s." % (self._root, e))
739
771
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
740
772
if (old_kind, new_kind) != ('file', 'file'):
741
773
return DiffPath.CANNOT_DIFF
742
self._prepare_files(file_id, old_path, new_path)
743
self._execute(osutils.pathjoin('old', old_path),
744
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)
778
def edit_file(self, file_id):
779
"""Use this tool to edit a file.
781
A temporary copy will be edited, and the new contents will be
784
:param file_id: The id of the file to edit.
785
:return: The new contents of the file.
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,
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')
797
return new_file.read()
747
802
class DiffTree(object):
906
961
new_kind = self.new_tree.kind(file_id)
907
962
except (errors.NoSuchId, errors.NoSuchFile):
964
self._diff(file_id, old_path, new_path, old_kind, new_kind)
967
def _diff(self, file_id, old_path, new_path, old_kind, new_kind):
910
968
result = DiffPath._diff_many(self.differs, file_id, old_path,
911
969
new_path, old_kind, new_kind)
912
970
if result is DiffPath.CANNOT_DIFF: