~jelmer/bzr-svn/0.6

« back to all changes in this revision

Viewing changes to send.py

  • Committer: Jelmer Vernooij
  • Date: 2009-07-24 18:07:59 UTC
  • mto: This revision was merged to the branch mainline in revision 3136.
  • Revision ID: jelmer@samba.org-20090724180759-isium5399rp9vm28
Finish 'svn diff' send format.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
import time
18
18
from bzrlib import (
19
19
    branch as _mod_branch,
 
20
    diff as _mod_diff,
20
21
    errors,
21
22
    merge_directive,
22
23
    osutils,
23
 
    revision as _mod_revision
24
 
    )
25
 
 
26
 
 
27
 
class SvnDiffWriter(object):
28
 
 
29
 
    def __init__(self, repository, base_revision_id):
30
 
        self.repository = repository
31
 
        self.base_revision_id = base_revision_id
32
 
        self.tree_rev_info = {}
33
 
 
34
 
    def get_svn_rev_info(self, tree):
35
 
        if tree in self.tree_rev_info:
36
 
            return self.tree_rev_info[tree]
37
 
        revision_id = tree.get_revision_id()
38
 
        if revision_id == self.base_revision_id:
39
 
            rev_info = '(working copy)'
40
 
        elif _mod_revision.is_null(revision_id):
41
 
            rev_info = '(revision 0)'
42
 
        else:
43
 
            info = self.repository.lookup_revision_id(revision_id)
44
 
            rev_info = '(revision %d)' % info[0][2]
45
 
        self.tree_rev_info[tree] = rev_info
46
 
        return rev_info
47
 
 
48
 
    def diff_text(self, difftext, file_id, old_path, new_path, old_kind, new_kind):
49
 
        if 'file' not in (old_kind, new_kind):
50
 
            return difftext.CANNOT_DIFF
51
 
        from_file_id = to_file_id = file_id
52
 
        if old_kind == 'file':
53
 
            old_date = self.get_svn_rev_info(difftext.old_tree)
54
 
        elif old_kind is None:
55
 
            old_date = None
56
 
            from_file_id = None
57
 
        else:
58
 
            return difftext.CANNOT_DIFF
59
 
        if new_kind == 'file':
60
 
            new_date = self.get_svn_rev_info(difftext.new_tree)
61
 
        elif new_kind is None:
62
 
            new_date = None
63
 
            to_file_id = None
64
 
        else:
65
 
            return difftext.CANNOT_DIFF
66
 
        from_label = '%s%s\t%s' % (difftext.old_label, old_path, old_date or '(revision 0)')
67
 
        to_label = '%s%s\t%s' % (difftext.new_label, new_path, new_date or '(revision 0)')
68
 
        return difftext.diff_text(from_file_id, to_file_id, from_label, to_label)
 
24
    )
 
25
 
 
26
from cStringIO import StringIO
 
27
 
 
28
from subvertpy import (
 
29
    properties,
 
30
    )
 
31
 
 
32
 
 
33
class SvnDiffTree(_mod_diff.DiffTree):
 
34
    """Provides a text representation between two trees, formatted for svn."""
 
35
 
 
36
    def _get_svn_rev_info(self, tree, file_id):
 
37
        try:
 
38
            rev = tree.inventory[file_id].revision
 
39
        except errors.NoSuchId:
 
40
            return '(revision 0)'
 
41
        if rev is None:
 
42
            return '(working copy)'
 
43
        try:
 
44
            info = self.repository.lookup_revision_id(rev)
 
45
        except errors.NoSuchRevision:
 
46
            return '(working copy)'
 
47
        return '(revision %d)' % info[0][2]
 
48
 
 
49
    def _write_contents_diff(self, path, old_version, old_contents, new_version, new_contents):
 
50
        if path is None:
 
51
            return 
 
52
        self.to_file.write("Index: %s\n" % path)
 
53
        self.to_file.write("=" * 67 + "\n")
 
54
        old_label = '%s\t%s' % (path, old_version)
 
55
        new_label = '%s\t%s' % (path, new_version)
 
56
        _mod_diff.internal_diff(old_label, old_contents,
 
57
                                new_label, new_contents,
 
58
                                self.to_file)
 
59
 
 
60
    def _write_properties_diff(self, path, old_properties, new_properties):
 
61
        if new_properties is None:
 
62
            return
 
63
        if old_properties is None:
 
64
            old_properties = {}
 
65
        changed = []
 
66
        for name in set(old_properties.keys() + new_properties.keys()):
 
67
            oldval = old_properties.get(name)
 
68
            newval = new_properties.get(name)
 
69
            if oldval != newval:
 
70
                changed.append((name, oldval, newval))
 
71
        if changed == []:
 
72
            return
 
73
        self.to_file.write("Property changes on: %s\n" % path)
 
74
        self.to_file.write("_" * 67 + "\n")
 
75
        for (name, old_value, new_value) in changed:
 
76
            if old_value is None:
 
77
                self.to_file.write("Added: %s\n\t+%s\n" % (name, new_value))
 
78
            elif new_value is None:
 
79
                self.to_file.write("Removed: %s\n\t-%s\n" % (name, old_value))
 
80
            else:
 
81
                self.to_file.write("Changed: %s\n\t-%s\n\t+%s\n" % (name, old_value, new_value))
 
82
 
 
83
    def _get_file_properties(self, tree, path, kind, executable):
 
84
        if kind in (None, "directory"):
 
85
            return None
 
86
        ret = {}
 
87
        if executable:
 
88
            ret[properties.PROP_EXECUTABLE] = properties.PROP_EXECUTABLE_VALUE
 
89
        if kind == "symlink":
 
90
            ret[properties.PROP_SPECIAL] = properties.PROP_SPECIAL_VALUE
 
91
        return ret
 
92
 
 
93
    def _get_file_contents(self, tree, file_id, path, kind):
 
94
        if kind in (None, "directory"):
 
95
            return None
 
96
        if kind == "symlink":
 
97
            return ["link %s" % tree.get_symlink_target(file_id)]
 
98
        return tree.get_file(file_id).readlines()
 
99
 
 
100
    def _show_diff(self, specific_files, extra_trees):
 
101
        iterator = self.new_tree.iter_changes(self.old_tree,
 
102
                                               specific_files=specific_files,
 
103
                                               extra_trees=extra_trees,
 
104
                                               require_versioned=True)
 
105
        has_changes = 0
 
106
        def get_encoded_path(path):
 
107
            if path is not None:
 
108
                return path.encode(self.path_encoding, "replace")
 
109
        for (file_id, paths, changed_content, versioned, parent, name, kind,
 
110
             executable) in iterator:
 
111
            # The root does not get diffed, and items with no known kind (that
 
112
            # is, missing) in both trees are skipped as well.
 
113
            if parent == (None, None) or kind == (None, None):
 
114
                continue
 
115
            oldpath, newpath = paths
 
116
            oldpath_encoded = get_encoded_path(paths[0])
 
117
            newpath_encoded = get_encoded_path(paths[1])
 
118
            old_present = (kind[0] is not None and versioned[0])
 
119
            new_present = (kind[1] is not None and versioned[1])
 
120
            renamed = (parent[0], name[0]) != (parent[1], name[1])
 
121
            old_properties = self._get_file_properties(self.old_tree, oldpath_encoded, kind[0], executable[0])
 
122
            new_properties = self._get_file_properties(self.new_tree, newpath_encoded, kind[1], executable[1])
 
123
            old_version = self._get_svn_rev_info(self.old_tree, file_id)
 
124
            new_version = self._get_svn_rev_info(self.new_tree, file_id)
 
125
 
 
126
            if oldpath_encoded == newpath_encoded:
 
127
                if changed_content:
 
128
                    old_contents = self._get_file_contents(self.old_tree, file_id, oldpath_encoded, kind[0])
 
129
                    new_contents = self._get_file_contents(self.new_tree, file_id, newpath_encoded, kind[1])
 
130
                    self._write_contents_diff(oldpath_encoded, old_version, old_contents, new_version, new_contents)
 
131
                self._write_properties_diff(oldpath_encoded, old_properties, new_properties)
 
132
            else:
 
133
                old_contents = self._get_file_contents(self.old_tree, file_id, oldpath_encoded, kind[0])
 
134
                new_contents = self._get_file_contents(self.new_tree, file_id, newpath_encoded, kind[1])
 
135
                self._write_contents_diff(oldpath_encoded, old_version, old_contents, new_version, [])
 
136
                self._write_contents_diff(newpath_encoded, old_version, [], new_version, new_contents)
 
137
                self._write_properties_diff(newpath_encoded, {}, new_properties)
 
138
 
 
139
            has_changes = (changed_content or renamed)
 
140
 
 
141
        return has_changes
69
142
 
70
143
 
71
144
class SvnMergeDirective(merge_directive._BaseMergeDirective):
75
148
 
76
149
    @classmethod
77
150
    def _generate_diff(cls, repository, svn_repository, revision_id, ancestor_id):
78
 
        from bzrlib.diff import DiffText
79
 
        writer = SvnDiffWriter(svn_repository, revision_id)
80
 
        def DiffText_diff(self, file_id, old_path, new_path, old_kind, new_kind):
81
 
            return writer.diff_text(self, file_id, old_path, new_path, old_kind, new_kind)
82
 
        old_DiffText_diff = DiffText.diff
83
 
        DiffText.diff = DiffText_diff
84
 
        patch = merge_directive._BaseMergeDirective._generate_diff(
85
 
            repository, revision_id, ancestor_id)
86
 
        DiffText.diff = old_DiffText_diff
87
 
        return patch
 
151
        tree_1 = repository.revision_tree(ancestor_id)
 
152
        tree_2 = repository.revision_tree(revision_id)
 
153
        s = StringIO()
 
154
        differ = SvnDiffTree.from_trees_options(tree_1, tree_2, s, 'utf8', None,
 
155
            '', '', None)
 
156
        differ.repository = svn_repository
 
157
        differ.show_diff(None, None)
 
158
        return s.getvalue()
88
159
 
89
160
    @classmethod
90
161
    def from_objects(cls, repository, revision_id, time, timezone,
92
163
                     public_branch=None, message=None):
93
164
        from bzrlib.plugins.svn.repository import SvnRepository
94
165
        submit_branch = _mod_branch.Branch.open(target_branch)
95
 
        if submit_branch.get_parent() is not None:
96
 
            submit_branch = _mod_branch.Branch.open(submit_branch.get_parent())
97
166
        if not isinstance(submit_branch.repository, SvnRepository):
98
167
            raise errors.BzrError("Not a Subversion repository")
99
168
 
100
169
        submit_branch.lock_read()
101
170
        try:
102
171
            submit_revision_id = submit_branch.last_revision()
103
 
            submit_revision_id = _mod_revision.ensure_null(submit_revision_id)
104
172
            repository.fetch(submit_branch.repository, submit_revision_id)
105
173
            graph = repository.get_graph()
106
174
            ancestor_id = graph.find_unique_lca(revision_id,