14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
43
41
from bzrlib.workingtree import WorkingTree
44
from bzrlib.i18n import gettext
47
44
from bzrlib.registry import (
47
from bzrlib.symbol_versioning import (
50
51
from bzrlib.trace import mutter, note, warning
291
@deprecated_function(deprecated_in((2, 2, 0)))
292
def get_trees_and_branches_to_diff(path_list, revision_specs, old_url, new_url,
294
"""Get the trees and specific files to diff given a list of paths.
296
This method works out the trees to be diff'ed and the files of
297
interest within those trees.
300
the list of arguments passed to the diff command
301
:param revision_specs:
302
Zero, one or two RevisionSpecs from the diff command line,
303
saying what revisions to compare.
305
The url of the old branch or tree. If None, the tree to use is
306
taken from the first path, if any, or the current working tree.
308
The url of the new branch or tree. If None, the tree to use is
309
taken from the first path, if any, or the current working tree.
311
if True and a view is set, apply the view or check that the paths
314
a tuple of (old_tree, new_tree, old_branch, new_branch,
315
specific_files, extra_trees) where extra_trees is a sequence of
316
additional trees to search in for file-ids. The trees and branches
319
op = cleanup.OperationWithCleanups(get_trees_and_branches_to_diff_locked)
320
return op.run_simple(path_list, revision_specs, old_url, new_url,
321
op.add_cleanup, apply_view=apply_view)
290
324
def get_trees_and_branches_to_diff_locked(
291
325
path_list, revision_specs, old_url, new_url, add_cleanup, apply_view=True):
292
326
"""Get the trees and specific files to diff given a list of paths.
358
392
if old_url is None:
359
393
old_url = default_location
360
394
working_tree, branch, relpath = \
361
controldir.ControlDir.open_containing_tree_or_branch(old_url)
395
bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
362
396
lock_tree_or_branch(working_tree, branch)
363
397
if consider_relpath and relpath != '':
364
398
if working_tree is not None and apply_view:
372
406
new_url = default_location
373
407
if new_url != old_url:
374
408
working_tree, branch, relpath = \
375
controldir.ControlDir.open_containing_tree_or_branch(new_url)
409
bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
376
410
lock_tree_or_branch(working_tree, branch)
377
411
if consider_relpath and relpath != '':
378
412
if working_tree is not None and apply_view:
397
431
specific_files = view_files
398
432
view_str = views.view_display_str(view_files)
399
note(gettext("*** Ignoring files outside view. View is %s") % view_str)
433
note("*** Ignoring files outside view. View is %s" % view_str)
401
435
# Get extra trees that ought to be searched for file-ids
402
436
extra_trees = None
403
437
if working_tree is not None and working_tree not in (old_tree, new_tree):
404
438
extra_trees = (working_tree,)
405
return (old_tree, new_tree, old_branch, new_branch,
406
specific_files, extra_trees)
439
return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
409
442
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
430
463
"""Show in text form the changes from one tree to another.
432
465
:param to_file: The output stream.
433
:param specific_files: Include only changes to these files - None for all
466
:param specific_files:Include only changes to these files - None for all
435
468
:param external_diff_options: If set, use an external GNU diff and pass
713
746
def _get_command(self, old_path, new_path):
714
747
my_map = {'old_path': old_path, 'new_path': new_path}
715
command = [AtTemplate(t).substitute(my_map) for t in
716
self.command_template]
717
if sys.platform == 'win32': # Popen doesn't accept unicode on win32
720
if isinstance(c, unicode):
721
command_encoded.append(c.encode('mbcs'))
723
command_encoded.append(c)
724
return command_encoded
748
return [AtTemplate(t).substitute(my_map) for t in
749
self.command_template]
728
751
def _execute(self, old_path, new_path):
729
752
command = self._get_command(old_path, new_path)
754
"""Returns safe encoding for passing file path to diff tool"""
755
if sys.platform == 'win32':
758
# Don't fallback to 'utf-8' because subprocess may not be able to
759
# handle utf-8 correctly when locale is not utf-8.
760
return sys.getfilesystemencoding() or 'ascii'
762
def _is_safepath(self, path):
763
"""Return true if `path` may be able to pass to subprocess."""
766
return path == path.encode(fenc).decode(fenc)
770
def _safe_filename(self, prefix, relpath):
771
"""Replace unsafe character in `relpath` then join `self._root`,
772
`prefix` and `relpath`."""
774
# encoded_str.replace('?', '_') may break multibyte char.
775
# So we should encode, decode, then replace(u'?', u'_')
776
relpath_tmp = relpath.encode(fenc, 'replace').decode(fenc, 'replace')
777
relpath_tmp = relpath_tmp.replace(u'?', u'_')
778
return osutils.pathjoin(self._root, prefix, relpath_tmp)
780
775
def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
781
776
allow_write=False):
782
777
if not force_temp and isinstance(tree, WorkingTree):
783
full_path = tree.abspath(tree.id2path(file_id))
784
if self._is_safepath(full_path):
787
full_path = self._safe_filename(prefix, relpath)
778
return tree.abspath(tree.id2path(file_id))
780
full_path = osutils.pathjoin(self._root, prefix, relpath)
788
781
if not force_temp and self._try_symlink_root(tree, prefix):
790
783
parent_dir = osutils.dirname(full_path)
848
841
old_path = self.old_tree.id2path(file_id)
849
842
new_path = self.new_tree.id2path(file_id)
850
old_abs_path, new_abs_path = self._prepare_files(
851
file_id, old_path, new_path,
852
allow_write_new=True,
854
command = self._get_command(old_abs_path, new_abs_path)
843
new_abs_path = self._prepare_files(file_id, old_path, new_path,
844
allow_write_new=True,
846
command = self._get_command(osutils.pathjoin('old', old_path),
847
osutils.pathjoin('new', new_path))
855
848
subprocess.call(command, cwd=self._root)
856
new_file = open(new_abs_path, 'rb')
849
new_file = open(new_abs_path, 'r')
858
851
return new_file.read()
909
902
"""Factory for producing a DiffTree.
911
904
Designed to accept options used by show_diff_trees.
913
905
:param old_tree: The tree to show as old in the comparison
914
906
:param new_tree: The tree to show as new in the comparison
915
907
:param to_file: File to write comparisons to