~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-08-24 21:25:26 UTC
  • mfrom: (4641.1.1 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20090824212526-5j41jw5zsciji66e
(robertc) Back out draft documentation change landed as 'selftest
        improvements'. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
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
16
16
 
17
17
import difflib
18
18
import os
36
36
    patiencediff,
37
37
    textfile,
38
38
    timestamp,
 
39
    views,
39
40
    )
40
41
""")
41
42
 
42
43
from bzrlib.symbol_versioning import (
43
 
        deprecated_function,
44
 
        one_three
45
 
        )
46
 
from bzrlib.trace import mutter, warning
 
44
    deprecated_function,
 
45
    )
 
46
from bzrlib.trace import mutter, note, warning
47
47
 
48
48
 
49
49
# TODO: Rather than building a changeset object, we should probably
78
78
    # both sequences are empty.
79
79
    if not oldlines and not newlines:
80
80
        return
81
 
    
 
81
 
82
82
    if allow_binary is False:
83
83
        textfile.check_text_lines(oldlines)
84
84
        textfile.check_text_lines(newlines)
99
99
        ud[2] = ud[2].replace('-1,0', '-0,0')
100
100
    elif not newlines:
101
101
        ud[2] = ud[2].replace('+1,0', '+0,0')
102
 
    # work around for difflib emitting random spaces after the label
103
 
    ud[0] = ud[0][:-2] + '\n'
104
 
    ud[1] = ud[1][:-2] + '\n'
105
102
 
106
103
    for line in ud:
107
104
        to_file.write(line)
174
171
 
175
172
        if not diff_opts:
176
173
            diff_opts = []
 
174
        if sys.platform == 'win32':
 
175
            # Popen doesn't do the proper encoding for external commands
 
176
            # Since we are dealing with an ANSI api, use mbcs encoding
 
177
            old_filename = old_filename.encode('mbcs')
 
178
            new_filename = new_filename.encode('mbcs')
177
179
        diffcmd = ['diff',
178
180
                   '--label', old_filename,
179
181
                   old_abspath,
202
204
            break
203
205
        else:
204
206
            diffcmd.append('-u')
205
 
                  
 
207
 
206
208
        if diff_opts:
207
209
            diffcmd.extend(diff_opts)
208
210
 
209
211
        pipe = _spawn_external_diff(diffcmd, capture_errors=True)
210
212
        out,err = pipe.communicate()
211
213
        rc = pipe.returncode
212
 
        
 
214
 
213
215
        # internal_diff() adds a trailing newline, add one here for consistency
214
216
        out += '\n'
215
217
        if rc == 2:
250
252
                msg = 'signal %d' % (-rc)
251
253
            else:
252
254
                msg = 'exit code %d' % rc
253
 
                
254
 
            raise errors.BzrError('external diff failed with %s; command: %r' 
 
255
 
 
256
            raise errors.BzrError('external diff failed with %s; command: %r'
255
257
                                  % (rc, diffcmd))
256
258
 
257
259
 
275
277
                        new_abspath, e)
276
278
 
277
279
 
278
 
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url):
 
280
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url,
 
281
    apply_view=True):
279
282
    """Get the trees and specific files to diff given a list of paths.
280
283
 
281
284
    This method works out the trees to be diff'ed and the files of
292
295
    :param new_url:
293
296
        The url of the new branch or tree. If None, the tree to use is
294
297
        taken from the first path, if any, or the current working tree.
 
298
    :param apply_view:
 
299
        if True and a view is set, apply the view or check that the paths
 
300
        are within it
295
301
    :returns:
296
302
        a tuple of (old_tree, new_tree, specific_files, extra_trees) where
297
303
        extra_trees is a sequence of additional trees to search in for
331
337
    working_tree, branch, relpath = \
332
338
        bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
333
339
    if consider_relpath and relpath != '':
 
340
        if working_tree is not None and apply_view:
 
341
            views.check_path_in_view(working_tree, relpath)
334
342
        specific_files.append(relpath)
335
343
    old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
336
344
 
341
349
        working_tree, branch, relpath = \
342
350
            bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
343
351
        if consider_relpath and relpath != '':
 
352
            if working_tree is not None and apply_view:
 
353
                views.check_path_in_view(working_tree, relpath)
344
354
            specific_files.append(relpath)
345
355
    new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
346
356
        basis_is_default=working_tree is None)
347
357
 
348
358
    # Get the specific files (all files is None, no files is [])
349
359
    if make_paths_wt_relative and working_tree is not None:
350
 
        other_paths = _relative_paths_in_tree(working_tree, other_paths)
 
360
        try:
 
361
            from bzrlib.builtins import safe_relpath_files
 
362
            other_paths = safe_relpath_files(working_tree, other_paths,
 
363
            apply_view=apply_view)
 
364
        except errors.FileInWrongBranch:
 
365
            raise errors.BzrCommandError("Files are in different branches")
351
366
    specific_files.extend(other_paths)
352
367
    if len(specific_files) == 0:
353
368
        specific_files = None
 
369
        if (working_tree is not None and working_tree.supports_views()
 
370
            and apply_view):
 
371
            view_files = working_tree.views.lookup_view()
 
372
            if view_files:
 
373
                specific_files = view_files
 
374
                view_str = views.view_display_str(view_files)
 
375
                note("*** Ignoring files outside view. View is %s" % view_str)
354
376
 
355
377
    # Get extra trees that ought to be searched for file-ids
356
378
    extra_trees = None
358
380
        extra_trees = (working_tree,)
359
381
    return old_tree, new_tree, specific_files, extra_trees
360
382
 
361
 
 
362
383
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
363
384
    if branch is None and tree is not None:
364
385
        branch = tree.branch
370
391
                return branch.basis_tree()
371
392
        else:
372
393
            return 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)
377
 
 
378
 
 
379
 
def _relative_paths_in_tree(tree, paths):
380
 
    """Get the relative paths within a working tree.
381
 
 
382
 
    Each path may be either an absolute path or a path relative to the
383
 
    current working directory.
384
 
    """
385
 
    result = []
386
 
    for filename in paths:
387
 
        try:
388
 
            result.append(tree.relpath(osutils.dereference_path(filename)))
389
 
        except errors.PathNotChild:
390
 
            raise errors.BzrCommandError("Files are in different branches")
391
 
    return result
 
394
    return spec.as_tree(branch)
392
395
 
393
396
 
394
397
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
442
445
    return timestamp.format_patch_date(mtime)
443
446
 
444
447
 
445
 
def _raise_if_nonexistent(paths, old_tree, new_tree):
446
 
    """Complain if paths are not in either inventory or tree.
447
 
 
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.
450
 
    
451
 
    This can be used by operations such as bzr status that can accept
452
 
    unknown or ignored files.
453
 
    """
454
 
    mutter("check paths: %r", paths)
455
 
    if not paths:
456
 
        return
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)]
460
 
    if s:
461
 
        raise errors.PathsDoNotExist(sorted(s))
462
 
 
463
 
 
464
 
@deprecated_function(one_three)
465
 
def get_prop_change(meta_modified):
466
 
    if meta_modified:
467
 
        return " (properties changed)"
468
 
    else:
469
 
        return  ""
470
 
 
471
448
def get_executable_change(old_is_x, new_is_x):
472
449
    descr = { True:"+x", False:"-x", None:"??" }
473
450
    if old_is_x != new_is_x:
648
625
            return self.CANNOT_DIFF
649
626
        from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
650
627
        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)
 
628
        return self.diff_text(from_file_id, to_file_id, from_label, to_label,
 
629
            old_path, new_path)
652
630
 
653
 
    def diff_text(self, from_file_id, to_file_id, from_label, to_label):
 
631
    def diff_text(self, from_file_id, to_file_id, from_label, to_label,
 
632
        from_path=None, to_path=None):
654
633
        """Diff the content of given files in two trees
655
634
 
656
635
        :param from_file_id: The id of the file in the from tree.  If None,
658
637
        :param to_file_id: The id of the file in the to tree.  This may refer
659
638
            to a different file from from_file_id.  If None,
660
639
            the file is not present in the to tree.
 
640
        :param from_path: The path in the from tree or None if unknown.
 
641
        :param to_path: The path in the to tree or None if unknown.
661
642
        """
662
 
        def _get_text(tree, file_id):
 
643
        def _get_text(tree, file_id, path):
663
644
            if file_id is not None:
664
 
                return tree.get_file(file_id).readlines()
 
645
                return tree.get_file(file_id, path).readlines()
665
646
            else:
666
647
                return []
667
648
        try:
668
 
            from_text = _get_text(self.old_tree, from_file_id)
669
 
            to_text = _get_text(self.new_tree, to_file_id)
 
649
            from_text = _get_text(self.old_tree, from_file_id, from_path)
 
650
            to_text = _get_text(self.new_tree, to_file_id, to_path)
670
651
            self.text_differ(from_label, from_text, to_label, to_text,
671
652
                             self.to_file)
672
653
        except errors.BinaryFile:
759
740
        return old_disk_path, new_disk_path
760
741
 
761
742
    def finish(self):
762
 
        osutils.rmtree(self._root)
 
743
        try:
 
744
            osutils.rmtree(self._root)
 
745
        except OSError, e:
 
746
            if e.errno != errno.ENOENT:
 
747
                mutter("The temporary directory \"%s\" was not "
 
748
                        "cleanly removed: %s." % (self._root, e))
763
749
 
764
750
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
765
751
        if (old_kind, new_kind) != ('file', 'file'):
910
896
                self.to_file.write("=== modified %s '%s'%s\n" % (kind[0],
911
897
                                   newpath_encoded, prop_str))
912
898
            if changed_content:
913
 
                self.diff(file_id, oldpath, newpath)
 
899
                self._diff(file_id, oldpath, newpath, kind[0], kind[1])
914
900
                has_changes = 1
915
901
            if renamed:
916
902
                has_changes = 1
931
917
            new_kind = self.new_tree.kind(file_id)
932
918
        except (errors.NoSuchId, errors.NoSuchFile):
933
919
            new_kind = None
934
 
 
 
920
        self._diff(file_id, old_path, new_path, old_kind, new_kind)
 
921
 
 
922
 
 
923
    def _diff(self, file_id, old_path, new_path, old_kind, new_kind):
935
924
        result = DiffPath._diff_many(self.differs, file_id, old_path,
936
925
                                       new_path, old_kind, new_kind)
937
926
        if result is DiffPath.CANNOT_DIFF: