~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: Aaron Bentley
  • Date: 2009-11-03 15:45:56 UTC
  • mto: (4634.97.2 2.0)
  • mto: This revision was merged to the branch mainline in revision 4798.
  • Revision ID: aaron@aaronbentley.com-20091103154556-e953dmegqbinyokq
Improve patch binary section handling.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd.
 
1
# Copyright (C) 2004, 2005, 2006 Canonical Ltd.
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
18
18
import os
19
19
import re
20
20
import shutil
21
 
import string
22
21
import sys
23
22
 
24
23
from bzrlib.lazy_import import lazy_import
31
30
from bzrlib import (
32
31
    branch as _mod_branch,
33
32
    bzrdir,
34
 
    cmdline,
 
33
    commands,
35
34
    errors,
36
35
    osutils,
37
36
    patiencediff,
39
38
    timestamp,
40
39
    views,
41
40
    )
42
 
 
43
 
from bzrlib.workingtree import WorkingTree
44
41
""")
45
42
 
46
43
from bzrlib.symbol_versioning import (
49
46
from bzrlib.trace import mutter, note, warning
50
47
 
51
48
 
52
 
class AtTemplate(string.Template):
53
 
    """Templating class that uses @ instead of $."""
54
 
 
55
 
    delimiter = '@'
56
 
 
57
 
 
58
49
# TODO: Rather than building a changeset object, we should probably
59
50
# invoke callbacks on an object.  That object can either accumulate a
60
51
# list, write them out directly, etc etc.
286
277
                        new_abspath, e)
287
278
 
288
279
 
289
 
def get_trees_and_branches_to_diff(path_list, revision_specs, old_url, new_url,
290
 
                                   apply_view=True):
 
280
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url,
 
281
    apply_view=True):
291
282
    """Get the trees and specific files to diff given a list of paths.
292
283
 
293
284
    This method works out the trees to be diff'ed and the files of
308
299
        if True and a view is set, apply the view or check that the paths
309
300
        are within it
310
301
    :returns:
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.
 
302
        a tuple of (old_tree, new_tree, specific_files, extra_trees) where
 
303
        extra_trees is a sequence of additional trees to search in for
 
304
        file-ids.
314
305
    """
315
306
    # Get the old and new revision specs
316
307
    old_revision_spec = None
350
341
            views.check_path_in_view(working_tree, relpath)
351
342
        specific_files.append(relpath)
352
343
    old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
353
 
    old_branch = branch
354
344
 
355
345
    # Get the new location
356
346
    if new_url is None:
364
354
            specific_files.append(relpath)
365
355
    new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
366
356
        basis_is_default=working_tree is None)
367
 
    new_branch = branch
368
357
 
369
358
    # Get the specific files (all files is None, no files is [])
370
359
    if make_paths_wt_relative and working_tree is not None:
389
378
    extra_trees = None
390
379
    if working_tree is not None and working_tree not in (old_tree, new_tree):
391
380
        extra_trees = (working_tree,)
392
 
    return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
393
 
 
 
381
    return old_tree, new_tree, specific_files, extra_trees
394
382
 
395
383
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
396
384
    if branch is None and tree is not None:
453
441
 
454
442
def _patch_header_date(tree, file_id, path):
455
443
    """Returns a timestamp suitable for use in a patch header."""
456
 
    try:
457
 
        mtime = tree.get_file_mtime(file_id, path)
458
 
    except errors.FileTimestampUnavailable:
459
 
        mtime = 0
 
444
    mtime = tree.get_file_mtime(file_id, path)
460
445
    return timestamp.format_patch_date(mtime)
461
446
 
462
447
 
683
668
    @classmethod
684
669
    def from_string(klass, command_string, old_tree, new_tree, to_file,
685
670
                    path_encoding='utf-8'):
686
 
        command_template = cmdline.split(command_string)
687
 
        if '@' not in command_string:
688
 
            command_template.extend(['@old_path', '@new_path'])
 
671
        command_template = commands.shlex_split_unicode(command_string)
 
672
        command_template.extend(['%(old_path)s', '%(new_path)s'])
689
673
        return klass(command_template, old_tree, new_tree, to_file,
690
674
                     path_encoding)
691
675
 
698
682
 
699
683
    def _get_command(self, old_path, new_path):
700
684
        my_map = {'old_path': old_path, 'new_path': new_path}
701
 
        return [AtTemplate(t).substitute(my_map) for t in
702
 
                self.command_template]
 
685
        return [t % my_map for t in self.command_template]
703
686
 
704
687
    def _execute(self, old_path, new_path):
705
688
        command = self._get_command(old_path, new_path)
725
708
                raise
726
709
        return True
727
710
 
728
 
    def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
729
 
                    allow_write=False):
730
 
        if not force_temp and isinstance(tree, WorkingTree):
731
 
            return tree.abspath(tree.id2path(file_id))
732
 
        
 
711
    def _write_file(self, file_id, tree, prefix, relpath):
733
712
        full_path = osutils.pathjoin(self._root, prefix, relpath)
734
 
        if not force_temp and self._try_symlink_root(tree, prefix):
 
713
        if self._try_symlink_root(tree, prefix):
735
714
            return full_path
736
715
        parent_dir = osutils.dirname(full_path)
737
716
        try:
748
727
                target.close()
749
728
        finally:
750
729
            source.close()
751
 
        if not allow_write:
752
 
            osutils.make_readonly(full_path)
753
 
        try:
754
 
            mtime = tree.get_file_mtime(file_id)
755
 
        except errors.FileTimestampUnavailable:
756
 
            mtime = 0
 
730
        osutils.make_readonly(full_path)
 
731
        mtime = tree.get_file_mtime(file_id)
757
732
        os.utime(full_path, (mtime, mtime))
758
733
        return full_path
759
734
 
760
 
    def _prepare_files(self, file_id, old_path, new_path, force_temp=False,
761
 
                       allow_write_new=False):
 
735
    def _prepare_files(self, file_id, old_path, new_path):
762
736
        old_disk_path = self._write_file(file_id, self.old_tree, 'old',
763
 
                                         old_path, force_temp)
 
737
                                         old_path)
764
738
        new_disk_path = self._write_file(file_id, self.new_tree, 'new',
765
 
                                         new_path, force_temp,
766
 
                                         allow_write=allow_write_new)
 
739
                                         new_path)
767
740
        return old_disk_path, new_disk_path
768
741
 
769
742
    def finish(self):
777
750
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
778
751
        if (old_kind, new_kind) != ('file', 'file'):
779
752
            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)
783
 
 
784
 
    def edit_file(self, file_id):
785
 
        """Use this tool to edit a file.
786
 
 
787
 
        A temporary copy will be edited, and the new contents will be
788
 
        returned.
789
 
 
790
 
        :param file_id: The id of the file to edit.
791
 
        :return: The new contents of the file.
792
 
        """
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,
797
 
                                           force_temp=True)[1]
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')
802
 
        try:
803
 
            return new_file.read()
804
 
        finally:
805
 
            new_file.close()
 
753
        self._prepare_files(file_id, old_path, new_path)
 
754
        self._execute(osutils.pathjoin('old', old_path),
 
755
                      osutils.pathjoin('new', new_path))
806
756
 
807
757
 
808
758
class DiffTree(object):