~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: John Arbash Meinel
  • Author(s): Mark Hammond
  • Date: 2008-09-09 17:02:21 UTC
  • mto: This revision was merged to the branch mainline in revision 3697.
  • Revision ID: john@arbash-meinel.com-20080909170221-svim3jw2mrz0amp3
An updated transparent icon for bzr.

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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
import difflib
18
18
import os
36
36
    patiencediff,
37
37
    textfile,
38
38
    timestamp,
39
 
    views,
40
39
    )
41
40
""")
42
41
 
43
42
from bzrlib.symbol_versioning import (
44
 
    deprecated_function,
45
 
    )
46
 
from bzrlib.trace import mutter, note, warning
 
43
        deprecated_function,
 
44
        one_three
 
45
        )
 
46
from bzrlib.trace import mutter, 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'
102
105
 
103
106
    for line in ud:
104
107
        to_file.write(line)
171
174
 
172
175
        if not diff_opts:
173
176
            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')
179
177
        diffcmd = ['diff',
180
178
                   '--label', old_filename,
181
179
                   old_abspath,
204
202
            break
205
203
        else:
206
204
            diffcmd.append('-u')
207
 
 
 
205
                  
208
206
        if diff_opts:
209
207
            diffcmd.extend(diff_opts)
210
208
 
211
209
        pipe = _spawn_external_diff(diffcmd, capture_errors=True)
212
210
        out,err = pipe.communicate()
213
211
        rc = pipe.returncode
214
 
 
 
212
        
215
213
        # internal_diff() adds a trailing newline, add one here for consistency
216
214
        out += '\n'
217
215
        if rc == 2:
252
250
                msg = 'signal %d' % (-rc)
253
251
            else:
254
252
                msg = 'exit code %d' % rc
255
 
 
256
 
            raise errors.BzrError('external diff failed with %s; command: %r'
 
253
                
 
254
            raise errors.BzrError('external diff failed with %s; command: %r' 
257
255
                                  % (rc, diffcmd))
258
256
 
259
257
 
277
275
                        new_abspath, e)
278
276
 
279
277
 
280
 
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url,
281
 
    apply_view=True):
 
278
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url):
282
279
    """Get the trees and specific files to diff given a list of paths.
283
280
 
284
281
    This method works out the trees to be diff'ed and the files of
295
292
    :param new_url:
296
293
        The url of the new branch or tree. If None, the tree to use is
297
294
        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
301
295
    :returns:
302
296
        a tuple of (old_tree, new_tree, specific_files, extra_trees) where
303
297
        extra_trees is a sequence of additional trees to search in for
337
331
    working_tree, branch, relpath = \
338
332
        bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
339
333
    if consider_relpath and relpath != '':
340
 
        if working_tree is not None and apply_view:
341
 
            views.check_path_in_view(working_tree, relpath)
342
334
        specific_files.append(relpath)
343
335
    old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
344
336
 
349
341
        working_tree, branch, relpath = \
350
342
            bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
351
343
        if consider_relpath and relpath != '':
352
 
            if working_tree is not None and apply_view:
353
 
                views.check_path_in_view(working_tree, relpath)
354
344
            specific_files.append(relpath)
355
345
    new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
356
346
        basis_is_default=working_tree is None)
357
347
 
358
348
    # Get the specific files (all files is None, no files is [])
359
349
    if make_paths_wt_relative and working_tree is not None:
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")
 
350
        other_paths = _relative_paths_in_tree(working_tree, other_paths)
366
351
    specific_files.extend(other_paths)
367
352
    if len(specific_files) == 0:
368
353
        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)
376
354
 
377
355
    # Get extra trees that ought to be searched for file-ids
378
356
    extra_trees = None
380
358
        extra_trees = (working_tree,)
381
359
    return old_tree, new_tree, specific_files, extra_trees
382
360
 
 
361
 
383
362
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
384
363
    if branch is None and tree is not None:
385
364
        branch = tree.branch
391
370
                return branch.basis_tree()
392
371
        else:
393
372
            return tree
394
 
    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)
 
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
395
392
 
396
393
 
397
394
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
445
442
    return timestamp.format_patch_date(mtime)
446
443
 
447
444
 
 
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
 
448
471
def get_executable_change(old_is_x, new_is_x):
449
472
    descr = { True:"+x", False:"-x", None:"??" }
450
473
    if old_is_x != new_is_x:
625
648
            return self.CANNOT_DIFF
626
649
        from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
627
650
        to_label = '%s%s\t%s' % (self.new_label, new_path, new_date)
628
 
        return self.diff_text(from_file_id, to_file_id, from_label, to_label,
629
 
            old_path, new_path)
 
651
        return self.diff_text(from_file_id, to_file_id, from_label, to_label)
630
652
 
631
 
    def diff_text(self, from_file_id, to_file_id, from_label, to_label,
632
 
        from_path=None, to_path=None):
 
653
    def diff_text(self, from_file_id, to_file_id, from_label, to_label):
633
654
        """Diff the content of given files in two trees
634
655
 
635
656
        :param from_file_id: The id of the file in the from tree.  If None,
637
658
        :param to_file_id: The id of the file in the to tree.  This may refer
638
659
            to a different file from from_file_id.  If None,
639
660
            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.
642
661
        """
643
 
        def _get_text(tree, file_id, path):
 
662
        def _get_text(tree, file_id):
644
663
            if file_id is not None:
645
 
                return tree.get_file(file_id, path).readlines()
 
664
                return tree.get_file(file_id).readlines()
646
665
            else:
647
666
                return []
648
667
        try:
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)
 
668
            from_text = _get_text(self.old_tree, from_file_id)
 
669
            to_text = _get_text(self.new_tree, to_file_id)
651
670
            self.text_differ(from_label, from_text, to_label, to_text,
652
671
                             self.to_file)
653
672
        except errors.BinaryFile:
740
759
        return old_disk_path, new_disk_path
741
760
 
742
761
    def finish(self):
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))
 
762
        osutils.rmtree(self._root)
749
763
 
750
764
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
751
765
        if (old_kind, new_kind) != ('file', 'file'):
896
910
                self.to_file.write("=== modified %s '%s'%s\n" % (kind[0],
897
911
                                   newpath_encoded, prop_str))
898
912
            if changed_content:
899
 
                self._diff(file_id, oldpath, newpath, kind[0], kind[1])
 
913
                self.diff(file_id, oldpath, newpath)
900
914
                has_changes = 1
901
915
            if renamed:
902
916
                has_changes = 1
917
931
            new_kind = self.new_tree.kind(file_id)
918
932
        except (errors.NoSuchId, errors.NoSuchFile):
919
933
            new_kind = None
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):
 
934
 
924
935
        result = DiffPath._diff_many(self.differs, file_id, old_path,
925
936
                                       new_path, old_kind, new_kind)
926
937
        if result is DiffPath.CANNOT_DIFF: