/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
1
# Copyright (C) 2005 by Canonical Ltd
2
#
3
# Authors:
4
#   Johan Rydberg <jrydberg@gnu.org>
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
# Remaing to do is to figure out if get_graph should return a simple
21
# map, or a graph object of some kind.
22
23
24
"""Versioned text file storage api."""
25
26
27
class VersionedFile(object):
28
    """Versioned text file storage.
29
    
30
    A versioned file manages versions of line-based text files,
31
    keeping track of the originating version for each line.
32
33
    To clients the "lines" of the file are represented as a list of
34
    strings. These strings will typically have terminal newline
35
    characters, but this is not required.  In particular files commonly
36
    do not have a newline at the end of the file.
37
38
    Texts are identified by a version-id string.
39
    """
40
41
    def versions(self):
42
        """Return a unsorted list of versions."""
43
        raise NotImplementedError(self.versions)
44
45
    def has_version(self, version_id):
46
        """Returns whether version is present."""
47
        raise NotImplementedError(self.has_version)
48
49
    def add_lines(self, version_id, parents, lines):
50
        """Add a single text on top of the versioned file.
51
52
        Must raise RevisionAlreadyPresent if the new version is
53
        already present in file history.
54
55
        Must raise RevisionNotPresent if any of the given parents are
56
        not present in file history."""
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
57
        raise NotImplementedError(self.add_lines)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
58
1563.2.7 by Robert Collins
add versioned file clear_cache entry.
59
    def clear_cache(self):
60
        """Remove any data cached in the versioned file object."""
61
1563.2.5 by Robert Collins
Remove unused transaction references from knit.py and the versionedfile interface.
62
    def clone_text(self, new_version_id, old_version_id, parents):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
63
        """Add an identical text to old_version_id as new_version_id.
64
65
        Must raise RevisionNotPresent if the old version or any of the
66
        parents are not present in file history.
67
68
        Must raise RevisionAlreadyPresent if the new version is
69
        already present in file history."""
70
        raise NotImplementedError(self.clone_text)
71
72
    def get_text(self, version_id):
73
        """Return version contents as a text string.
74
75
        Raises RevisionNotPresent if version is not present in
76
        file history.
77
        """
78
        return ''.join(self.get_lines(version_id))
79
    get_string = get_text
80
81
    def get_lines(self, version_id):
82
        """Return version contents as a sequence of lines.
83
84
        Raises RevisionNotPresent if version is not present in
85
        file history.
86
        """
87
        raise NotImplementedError(self.get_lines)
88
89
    def get_ancestry(self, version_ids):
90
        """Return a list of all ancestors of given version(s). This
91
        will not include the null revision.
92
93
        Must raise RevisionNotPresent if any of the given versions are
94
        not present in file history."""
95
        if isinstance(version_ids, basestring):
96
            version_ids = [version_ids]
97
        raise NotImplementedError(self.get_ancestry)
98
        
99
    def get_graph(self, version_id):
100
        """Return a graph.
101
102
        Must raise RevisionNotPresent if version is not present in
103
        file history."""
104
        raise NotImplementedError(self.get_graph)
105
106
    def get_parents(self, version_id):
107
        """Return version names for parents of a version.
108
109
        Must raise RevisionNotPresent if version is not present in
110
        file history.
111
        """
112
        raise NotImplementedError(self.get_parents)
113
114
    def annotate_iter(self, version_id):
115
        """Yield list of (version-id, line) pairs for the specified
116
        version.
117
118
        Must raise RevisionNotPresent if any of the given versions are
119
        not present in file history.
120
        """
121
        raise NotImplementedError(self.annotate_iter)
122
123
    def annotate(self, version_id):
124
        return list(self.annotate_iter(version_id))
125
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
126
    def join(self, other, pb=None, msg=None, version_ids=None):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
127
        """Integrate versions from other into this versioned file.
128
129
        If version_ids is None all versions from other should be
130
        incorporated into this versioned file.
131
132
        Must raise RevisionNotPresent if any of the specified versions
133
        are not present in the other files history."""
134
        raise NotImplementedError(self.join)
135
136
    def walk(self, version_ids=None):
137
        """Walk the versioned file as a weave-like structure, for
138
        versions relative to version_ids.  Yields sequence of (lineno,
139
        insert, deletes, text) for each relevant line.
140
141
        Must raise RevisionNotPresent if any of the specified versions
142
        are not present in the file history.
143
144
        :param version_ids: the version_ids to walk with respect to. If not
145
                            supplied the entire weave-like structure is walked.
146
        """
147
        raise NotImplementedError(self.walk)
148
149
    def plan_merge(self, ver_a, ver_b):
150
        """Return pseudo-annotation indicating how the two versions merge.
151
152
        This is computed between versions a and b and their common
153
        base.
154
155
        Weave lines present in none of them are skipped entirely.
156
        """
157
        inc_a = set(self.inclusions([ver_a]))
158
        inc_b = set(self.inclusions([ver_b]))
159
        inc_c = inc_a & inc_b
160
161
        for lineno, insert, deleteset, line in self.walk():
162
            if deleteset & inc_c:
163
                # killed in parent; can't be in either a or b
164
                # not relevant to our work
165
                yield 'killed-base', line
166
            elif insert in inc_c:
167
                # was inserted in base
168
                killed_a = bool(deleteset & inc_a)
169
                killed_b = bool(deleteset & inc_b)
170
                if killed_a and killed_b:
171
                    yield 'killed-both', line
172
                elif killed_a:
173
                    yield 'killed-a', line
174
                elif killed_b:
175
                    yield 'killed-b', line
176
                else:
177
                    yield 'unchanged', line
178
            elif insert in inc_a:
179
                if deleteset & inc_a:
180
                    yield 'ghost-a', line
181
                else:
182
                    # new in A; not in B
183
                    yield 'new-a', line
184
            elif insert in inc_b:
185
                if deleteset & inc_b:
186
                    yield 'ghost-b', line
187
                else:
188
                    yield 'new-b', line
189
            else:
190
                # not in either revision
191
                yield 'irrelevant', line
192
193
        yield 'unchanged', ''           # terminator
194
1563.2.2 by Robert Collins
merge from bzr.dev
195
    def weave_merge(self, plan, a_marker='<<<<<<< \n', b_marker='>>>>>>> \n'):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
196
        lines_a = []
197
        lines_b = []
198
        ch_a = ch_b = False
199
        # TODO: Return a structured form of the conflicts (e.g. 2-tuples for
200
        # conflicted regions), rather than just inserting the markers.
201
        # 
202
        # TODO: Show some version information (e.g. author, date) on 
203
        # conflicted regions.
204
        for state, line in plan:
205
            if state == 'unchanged' or state == 'killed-both':
206
                # resync and flush queued conflicts changes if any
207
                if not lines_a and not lines_b:
208
                    pass
209
                elif ch_a and not ch_b:
210
                    # one-sided change:                    
211
                    for l in lines_a: yield l
212
                elif ch_b and not ch_a:
213
                    for l in lines_b: yield l
214
                elif lines_a == lines_b:
215
                    for l in lines_a: yield l
216
                else:
1563.2.2 by Robert Collins
merge from bzr.dev
217
                    yield a_marker
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
218
                    for l in lines_a: yield l
219
                    yield '=======\n'
220
                    for l in lines_b: yield l
1563.2.2 by Robert Collins
merge from bzr.dev
221
                    yield b_marker
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
222
223
                del lines_a[:]
224
                del lines_b[:]
225
                ch_a = ch_b = False
226
                
227
            if state == 'unchanged':
228
                if line:
229
                    yield line
230
            elif state == 'killed-a':
231
                ch_a = True
232
                lines_b.append(line)
233
            elif state == 'killed-b':
234
                ch_b = True
235
                lines_a.append(line)
236
            elif state == 'new-a':
237
                ch_a = True
238
                lines_a.append(line)
239
            elif state == 'new-b':
240
                ch_b = True
241
                lines_b.append(line)
242
            else:
243
                assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
244
                                 'killed-both'), \
245
                       state