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
30
31
from bzrlib import (
31
32
branch as _mod_branch,
43
from bzrlib.workingtree import WorkingTree
42
46
from bzrlib.symbol_versioning import (
46
from bzrlib.trace import mutter, 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
293
305
The url of the new branch or tree. If None, the tree to use is
294
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
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
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.
300
315
# Get the old and new revision specs
301
316
old_revision_spec = None
331
346
working_tree, branch, relpath = \
332
347
bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
333
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)
334
351
specific_files.append(relpath)
335
352
old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
337
355
# Get the new location
338
356
if new_url is None:
341
359
working_tree, branch, relpath = \
342
360
bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
343
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)
344
364
specific_files.append(relpath)
345
365
new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
346
366
basis_is_default=working_tree is None)
348
369
# Get the specific files (all files is None, no files is [])
349
370
if make_paths_wt_relative and working_tree is not None:
350
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")
351
377
specific_files.extend(other_paths)
352
378
if len(specific_files) == 0:
353
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)
355
388
# Get extra trees that ought to be searched for file-ids
356
389
extra_trees = None
357
390
if working_tree is not None and working_tree not in (old_tree, new_tree):
358
391
extra_trees = (working_tree,)
359
return old_tree, new_tree, specific_files, extra_trees
392
return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
362
395
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
370
403
return branch.basis_tree()
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")
406
return spec.as_tree(branch)
394
409
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
439
454
def _patch_header_date(tree, file_id, path):
440
455
"""Returns a timestamp suitable for use in a patch header."""
441
mtime = tree.get_file_mtime(file_id, path)
457
mtime = tree.get_file_mtime(file_id, path)
458
except errors.FileTimestampUnavailable:
442
460
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)"
471
463
def get_executable_change(old_is_x, new_is_x):
472
464
descr = { True:"+x", False:"-x", None:"??" }
473
465
if old_is_x != new_is_x:
648
640
return self.CANNOT_DIFF
649
641
from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
650
642
to_label = '%s%s\t%s' % (self.new_label, new_path, new_date)
651
return self.diff_text(from_file_id, to_file_id, from_label, to_label)
643
return self.diff_text(from_file_id, to_file_id, from_label, to_label,
653
def diff_text(self, 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):
654
648
"""Diff the content of given files in two trees
656
650
:param from_file_id: The id of the file in the from tree. If None,
658
652
:param to_file_id: The id of the file in the to tree. This may refer
659
653
to a different file from from_file_id. If None,
660
654
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.
662
def _get_text(tree, file_id):
658
def _get_text(tree, file_id, path):
663
659
if file_id is not None:
664
return tree.get_file(file_id).readlines()
660
return tree.get_file(file_id, path).readlines()
668
from_text = _get_text(self.old_tree, from_file_id)
669
to_text = _get_text(self.new_tree, to_file_id)
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)
670
666
self.text_differ(from_label, from_text, to_label, to_text,
672
668
except errors.BinaryFile:
688
684
def from_string(klass, command_string, old_tree, new_tree, to_file,
689
685
path_encoding='utf-8'):
690
command_template = commands.shlex_split_unicode(command_string)
691
command_template.extend(['%(old_path)s', '%(new_path)s'])
686
command_template = cmdline.split(command_string)
687
if '@' not in command_string:
688
command_template.extend(['@old_path', '@new_path'])
692
689
return klass(command_template, old_tree, new_tree, to_file,
702
699
def _get_command(self, old_path, new_path):
703
700
my_map = {'old_path': old_path, 'new_path': new_path}
704
return [t % my_map for t in self.command_template]
701
return [AtTemplate(t).substitute(my_map) for t in
702
self.command_template]
706
704
def _execute(self, old_path, new_path):
707
705
command = self._get_command(old_path, new_path)
730
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))
731
733
full_path = osutils.pathjoin(self._root, prefix, relpath)
732
if self._try_symlink_root(tree, prefix):
734
if not force_temp and self._try_symlink_root(tree, prefix):
734
736
parent_dir = osutils.dirname(full_path)
749
osutils.make_readonly(full_path)
750
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:
751
757
os.utime(full_path, (mtime, mtime))
754
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):
755
762
old_disk_path = self._write_file(file_id, self.old_tree, 'old',
763
old_path, force_temp)
757
764
new_disk_path = self._write_file(file_id, self.new_tree, 'new',
765
new_path, force_temp,
766
allow_write=allow_write_new)
759
767
return old_disk_path, new_disk_path
761
769
def finish(self):
762
osutils.rmtree(self._root)
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))
764
777
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
765
778
if (old_kind, new_kind) != ('file', 'file'):
766
779
return DiffPath.CANNOT_DIFF
767
self._prepare_files(file_id, old_path, new_path)
768
self._execute(osutils.pathjoin('old', old_path),
769
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()
772
808
class DiffTree(object):
931
967
new_kind = self.new_tree.kind(file_id)
932
968
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):
935
974
result = DiffPath._diff_many(self.differs, file_id, old_path,
936
975
new_path, old_kind, new_kind)
937
976
if result is DiffPath.CANNOT_DIFF: