/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.5 by Robert Collins
Remove unused transaction references from knit.py and the versionedfile interface.
59
    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.
60
        """Add an identical text to old_version_id as new_version_id.
61
62
        Must raise RevisionNotPresent if the old version or any of the
63
        parents are not present in file history.
64
65
        Must raise RevisionAlreadyPresent if the new version is
66
        already present in file history."""
67
        raise NotImplementedError(self.clone_text)
68
69
    def get_text(self, version_id):
70
        """Return version contents as a text string.
71
72
        Raises RevisionNotPresent if version is not present in
73
        file history.
74
        """
75
        return ''.join(self.get_lines(version_id))
76
    get_string = get_text
77
78
    def get_lines(self, version_id):
79
        """Return version contents as a sequence of lines.
80
81
        Raises RevisionNotPresent if version is not present in
82
        file history.
83
        """
84
        raise NotImplementedError(self.get_lines)
85
86
    def get_ancestry(self, version_ids):
87
        """Return a list of all ancestors of given version(s). This
88
        will not include the null revision.
89
90
        Must raise RevisionNotPresent if any of the given versions are
91
        not present in file history."""
92
        if isinstance(version_ids, basestring):
93
            version_ids = [version_ids]
94
        raise NotImplementedError(self.get_ancestry)
95
        
96
    def get_graph(self, version_id):
97
        """Return a graph.
98
99
        Must raise RevisionNotPresent if version is not present in
100
        file history."""
101
        raise NotImplementedError(self.get_graph)
102
103
    def get_parents(self, version_id):
104
        """Return version names for parents of a version.
105
106
        Must raise RevisionNotPresent if version is not present in
107
        file history.
108
        """
109
        raise NotImplementedError(self.get_parents)
110
111
    def annotate_iter(self, version_id):
112
        """Yield list of (version-id, line) pairs for the specified
113
        version.
114
115
        Must raise RevisionNotPresent if any of the given versions are
116
        not present in file history.
117
        """
118
        raise NotImplementedError(self.annotate_iter)
119
120
    def annotate(self, version_id):
121
        return list(self.annotate_iter(version_id))
122
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
123
    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.
124
        """Integrate versions from other into this versioned file.
125
126
        If version_ids is None all versions from other should be
127
        incorporated into this versioned file.
128
129
        Must raise RevisionNotPresent if any of the specified versions
130
        are not present in the other files history."""
131
        raise NotImplementedError(self.join)
132
133
    def walk(self, version_ids=None):
134
        """Walk the versioned file as a weave-like structure, for
135
        versions relative to version_ids.  Yields sequence of (lineno,
136
        insert, deletes, text) for each relevant line.
137
138
        Must raise RevisionNotPresent if any of the specified versions
139
        are not present in the file history.
140
141
        :param version_ids: the version_ids to walk with respect to. If not
142
                            supplied the entire weave-like structure is walked.
143
        """
144
        raise NotImplementedError(self.walk)
145
146
    def plan_merge(self, ver_a, ver_b):
147
        """Return pseudo-annotation indicating how the two versions merge.
148
149
        This is computed between versions a and b and their common
150
        base.
151
152
        Weave lines present in none of them are skipped entirely.
153
        """
154
        inc_a = set(self.inclusions([ver_a]))
155
        inc_b = set(self.inclusions([ver_b]))
156
        inc_c = inc_a & inc_b
157
158
        for lineno, insert, deleteset, line in self.walk():
159
            if deleteset & inc_c:
160
                # killed in parent; can't be in either a or b
161
                # not relevant to our work
162
                yield 'killed-base', line
163
            elif insert in inc_c:
164
                # was inserted in base
165
                killed_a = bool(deleteset & inc_a)
166
                killed_b = bool(deleteset & inc_b)
167
                if killed_a and killed_b:
168
                    yield 'killed-both', line
169
                elif killed_a:
170
                    yield 'killed-a', line
171
                elif killed_b:
172
                    yield 'killed-b', line
173
                else:
174
                    yield 'unchanged', line
175
            elif insert in inc_a:
176
                if deleteset & inc_a:
177
                    yield 'ghost-a', line
178
                else:
179
                    # new in A; not in B
180
                    yield 'new-a', line
181
            elif insert in inc_b:
182
                if deleteset & inc_b:
183
                    yield 'ghost-b', line
184
                else:
185
                    yield 'new-b', line
186
            else:
187
                # not in either revision
188
                yield 'irrelevant', line
189
190
        yield 'unchanged', ''           # terminator
191
1563.2.2 by Robert Collins
merge from bzr.dev
192
    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.
193
        lines_a = []
194
        lines_b = []
195
        ch_a = ch_b = False
196
        # TODO: Return a structured form of the conflicts (e.g. 2-tuples for
197
        # conflicted regions), rather than just inserting the markers.
198
        # 
199
        # TODO: Show some version information (e.g. author, date) on 
200
        # conflicted regions.
201
        for state, line in plan:
202
            if state == 'unchanged' or state == 'killed-both':
203
                # resync and flush queued conflicts changes if any
204
                if not lines_a and not lines_b:
205
                    pass
206
                elif ch_a and not ch_b:
207
                    # one-sided change:                    
208
                    for l in lines_a: yield l
209
                elif ch_b and not ch_a:
210
                    for l in lines_b: yield l
211
                elif lines_a == lines_b:
212
                    for l in lines_a: yield l
213
                else:
1563.2.2 by Robert Collins
merge from bzr.dev
214
                    yield a_marker
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
215
                    for l in lines_a: yield l
216
                    yield '=======\n'
217
                    for l in lines_b: yield l
1563.2.2 by Robert Collins
merge from bzr.dev
218
                    yield b_marker
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
219
220
                del lines_a[:]
221
                del lines_b[:]
222
                ch_a = ch_b = False
223
                
224
            if state == 'unchanged':
225
                if line:
226
                    yield line
227
            elif state == 'killed-a':
228
                ch_a = True
229
                lines_b.append(line)
230
            elif state == 'killed-b':
231
                ch_b = True
232
                lines_a.append(line)
233
            elif state == 'new-a':
234
                ch_a = True
235
                lines_a.append(line)
236
            elif state == 'new-b':
237
                ch_b = True
238
                lines_b.append(line)
239
            else:
240
                assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
241
                                 'killed-both'), \
242
                       state