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
42
from bzrlib.symbol_versioning import (
49
from bzrlib.trace import mutter, note, warning
52
class AtTemplate(string.Template):
53
"""Templating class that uses @ instead of $."""
46
from bzrlib.trace import mutter, warning
58
49
# TODO: Rather than building a changeset object, we should probably
305
293
The url of the new branch or tree. If None, the tree to use is
306
294
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
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.
296
a tuple of (old_tree, new_tree, specific_files, extra_trees) where
297
extra_trees is a sequence of additional trees to search in for
315
300
# Get the old and new revision specs
316
301
old_revision_spec = None
346
331
working_tree, branch, relpath = \
347
332
bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
348
333
if consider_relpath and relpath != '':
349
if working_tree is not None and apply_view:
350
views.check_path_in_view(working_tree, relpath)
351
334
specific_files.append(relpath)
352
335
old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
355
337
# Get the new location
356
338
if new_url is None:
359
341
working_tree, branch, relpath = \
360
342
bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
361
343
if consider_relpath and relpath != '':
362
if working_tree is not None and apply_view:
363
views.check_path_in_view(working_tree, relpath)
364
344
specific_files.append(relpath)
365
345
new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
366
346
basis_is_default=working_tree is None)
369
348
# Get the specific files (all files is None, no files is [])
370
349
if make_paths_wt_relative and working_tree is not None:
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")
350
other_paths = _relative_paths_in_tree(working_tree, other_paths)
377
351
specific_files.extend(other_paths)
378
352
if len(specific_files) == 0:
379
353
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)
388
355
# Get extra trees that ought to be searched for file-ids
389
356
extra_trees = None
390
357
if working_tree is not None and working_tree not in (old_tree, new_tree):
391
358
extra_trees = (working_tree,)
392
return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
359
return old_tree, new_tree, specific_files, extra_trees
395
362
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
403
370
return branch.basis_tree()
406
return spec.as_tree(branch)
373
if not spec.needs_branch():
374
branch = _mod_branch.Branch.open(spec.get_branch())
375
revision_id = spec.as_revision_id(branch)
376
return branch.repository.revision_tree(revision_id)
379
def _relative_paths_in_tree(tree, paths):
380
"""Get the relative paths within a working tree.
382
Each path may be either an absolute path or a path relative to the
383
current working directory.
386
for filename in paths:
388
result.append(tree.relpath(osutils.dereference_path(filename)))
389
except errors.PathNotChild:
390
raise errors.BzrCommandError("Files are in different branches")
409
394
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
454
439
def _patch_header_date(tree, file_id, path):
455
440
"""Returns a timestamp suitable for use in a patch header."""
457
mtime = tree.get_file_mtime(file_id, path)
458
except errors.FileTimestampUnavailable:
441
mtime = tree.get_file_mtime(file_id, path)
460
442
return timestamp.format_patch_date(mtime)
445
def _raise_if_nonexistent(paths, old_tree, new_tree):
446
"""Complain if paths are not in either inventory or tree.
448
It's OK with the files exist in either tree's inventory, or
449
if they exist in the tree but are not versioned.
451
This can be used by operations such as bzr status that can accept
452
unknown or ignored files.
454
mutter("check paths: %r", paths)
457
s = old_tree.filter_unversioned_files(paths)
458
s = new_tree.filter_unversioned_files(s)
459
s = [path for path in s if not new_tree.has_filename(path)]
461
raise errors.PathsDoNotExist(sorted(s))
464
@deprecated_function(one_three)
465
def get_prop_change(meta_modified):
467
return " (properties changed)"
463
471
def get_executable_change(old_is_x, new_is_x):
464
472
descr = { True:"+x", False:"-x", None:"??" }
465
473
if old_is_x != new_is_x:
640
648
return self.CANNOT_DIFF
641
649
from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
642
650
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,
651
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):
653
def diff_text(self, from_file_id, to_file_id, from_label, to_label):
648
654
"""Diff the content of given files in two trees
650
656
:param from_file_id: The id of the file in the from tree. If None,
652
658
:param to_file_id: The id of the file in the to tree. This may refer
653
659
to a different file from from_file_id. If None,
654
660
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):
662
def _get_text(tree, file_id):
659
663
if file_id is not None:
660
return tree.get_file(file_id, path).readlines()
664
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)
668
from_text = _get_text(self.old_tree, from_file_id)
669
to_text = _get_text(self.new_tree, to_file_id)
666
670
self.text_differ(from_label, from_text, to_label, to_text,
668
672
except errors.BinaryFile:
684
688
def from_string(klass, command_string, old_tree, new_tree, to_file,
685
689
path_encoding='utf-8'):
686
690
command_template = commands.shlex_split_unicode(command_string)
687
if '@' not in command_string:
688
command_template.extend(['@old_path', '@new_path'])
691
command_template.extend(['%(old_path)s', '%(new_path)s'])
689
692
return klass(command_template, old_tree, new_tree, to_file,
699
702
def _get_command(self, old_path, new_path):
700
703
my_map = {'old_path': old_path, 'new_path': new_path}
701
return [AtTemplate(t).substitute(my_map) for t in
702
self.command_template]
704
return [t % my_map for t in self.command_template]
704
706
def _execute(self, old_path, new_path):
705
707
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))
730
def _write_file(self, file_id, tree, prefix, relpath):
733
731
full_path = osutils.pathjoin(self._root, prefix, relpath)
734
if not force_temp and self._try_symlink_root(tree, prefix):
732
if self._try_symlink_root(tree, prefix):
736
734
parent_dir = osutils.dirname(full_path)
752
osutils.make_readonly(full_path)
754
mtime = tree.get_file_mtime(file_id)
755
except errors.FileTimestampUnavailable:
749
osutils.make_readonly(full_path)
750
mtime = tree.get_file_mtime(file_id)
757
751
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):
754
def _prepare_files(self, file_id, old_path, new_path):
762
755
old_disk_path = self._write_file(file_id, self.old_tree, 'old',
763
old_path, force_temp)
764
757
new_disk_path = self._write_file(file_id, self.new_tree, 'new',
765
new_path, force_temp,
766
allow_write=allow_write_new)
767
759
return old_disk_path, new_disk_path
769
761
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))
762
osutils.rmtree(self._root)
777
764
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
778
765
if (old_kind, new_kind) != ('file', 'file'):
779
766
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()
767
self._prepare_files(file_id, old_path, new_path)
768
self._execute(osutils.pathjoin('old', old_path),
769
osutils.pathjoin('new', new_path))
808
772
class DiffTree(object):
967
931
new_kind = self.new_tree.kind(file_id)
968
932
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
935
result = DiffPath._diff_many(self.differs, file_id, old_path,
975
936
new_path, old_kind, new_kind)
976
937
if result is DiffPath.CANNOT_DIFF: