~bzr-pqm/bzr/bzr.dev

0.1.1 by Martin Pool
Check in old existing knit code.
1
#! /usr/bin/python
2
3
# Copyright (C) 2005 Canonical Ltd
4
0.1.33 by Martin Pool
add gpl text
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0.1.1 by Martin Pool
Check in old existing knit code.
18
19
# Author: Martin Pool <mbp@canonical.com>
20
21
0.1.38 by Martin Pool
Rename knit to weave. (I don't think there's an existing module called weave.)
22
"""Weave - storage of related text file versions"""
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
23
938 by Martin Pool
- various optimizations to weave add code
24
0.1.58 by Martin Pool
doc
25
# XXX: If we do weaves this way, will a merge still behave the same
26
# way if it's done in a different order?  That's a pretty desirable
27
# property.
28
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
29
# TODO: Nothing here so far assumes the lines are really \n newlines,
30
# rather than being split up in some other way.  We could accomodate
31
# binaries, perhaps by naively splitting on \n or perhaps using
32
# something like a rolling checksum.
33
0.1.85 by Martin Pool
doc
34
# TODO: End marker for each version so we can stop reading?
0.1.69 by Martin Pool
Simple text-based format for storing weaves, cleaner than
35
36
# TODO: Check that no insertion occurs inside a deletion that was
37
# active in the version of the insertion.
38
912 by Martin Pool
- update todos for weave
39
# TODO: In addition to the SHA-1 check, perhaps have some code that
40
# checks structural constraints of the weave: ie that insertions are
41
# properly nested, that there is no text outside of an insertion, that
42
# insertions or deletions are not repeated, etc.
0.1.85 by Martin Pool
doc
43
918 by Martin Pool
- start doing new weave-merge algorithm
44
# TODO: Parallel-extract that passes back each line along with a
45
# description of which revisions include it.  Nice for checking all
1393.1.25 by Martin Pool
- comments in weave code
46
# shas or calculating stats in parallel.
918 by Martin Pool
- start doing new weave-merge algorithm
47
1082 by Martin Pool
- lift imports
48
# TODO: Using a single _extract routine and then processing the output
49
# is probably inefficient.  It's simple enough that we can afford to
50
# have slight specializations for different ways its used: annotate,
51
# basis for add, get, etc.
52
1083 by Martin Pool
- add space to store revision-id in weave files
53
# TODO: Perhaps the API should work only in names to hide the integer
54
# indexes from the user?
55
1328 by Martin Pool
- slight refactoring of weave code
56
# TODO: Is there any potential performance win by having an add()
57
# variant that is passed a pre-cooked version of the single basis
58
# version?
59
1393.1.49 by Martin Pool
- add Weave._check_consistent_with
60
# TODO: Have a way to go back and insert a revision V1 that is a parent 
61
# of an already-stored revision V2.  This means some lines previously 
62
# counted as new in V2 will be discovered to have actually come from V1.  
63
# It is probably necessary to insert V1, then compute a whole new diff 
64
# from the mashed ancestors to V2.  This must be repeated for every 
65
# direct child of V1.  The deltas from V2 to its descendents won't change,
66
# although their location within the weave may change.  It may be possible
67
# to just adjust the location of those instructions rather than 
68
# re-weaving the whole thing.  This is expected to be a fairly rare
69
# operation, only used when inserting data that was previously a ghost.
70
71
1083 by Martin Pool
- add space to store revision-id in weave files
72
1082 by Martin Pool
- lift imports
73
74
import sha
1322 by Martin Pool
- tidy up import
75
from difflib import SequenceMatcher
1237 by Martin Pool
- allow the same version to be repeatedly added to a weave
76
1393.1.68 by Martin Pool
- add reweave that joins with ghosts
77
from bzrlib.trace import mutter
1237 by Martin Pool
- allow the same version to be repeatedly added to a weave
78
924 by Martin Pool
- Add IntSet class
79
0.1.47 by Martin Pool
New WeaveError and WeaveFormatError rather than assertions.
80
class WeaveError(Exception):
81
    """Exception in processing weave"""
82
83
84
class WeaveFormatError(WeaveError):
85
    """Weave invariant violated"""
1185.13.4 by Robert Collins
make reweave visible as a weave method, and quickly integrate into fetch
86
87
88
class WeaveParentMismatch(WeaveError):
89
    """Parents are mismatched between two revisions."""
0.1.47 by Martin Pool
New WeaveError and WeaveFormatError rather than assertions.
90
    
91
0.1.38 by Martin Pool
Rename knit to weave. (I don't think there's an existing module called weave.)
92
class Weave(object):
93
    """weave - versioned text file storage.
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
94
    
0.1.72 by Martin Pool
Go back to weave lines normally having newlines at the end.
95
    A Weave manages versions of line-based text files, keeping track
96
    of the originating version for each line.
97
98
    To clients the "lines" of the file are represented as a list of strings.
99
    These strings  will typically have terminal newline characters, but
100
    this is not required.  In particular files commonly do not have a newline
101
    at the end of the file.
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
102
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
103
    Texts can be identified in either of two ways:
104
105
    * a nonnegative index number.
106
1324 by Martin Pool
- doc
107
    * a version-id string.
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
108
0.1.38 by Martin Pool
Rename knit to weave. (I don't think there's an existing module called weave.)
109
    Typically the index number will be valid only inside this weave and
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
110
    the version-id is used to reference it in the larger world.
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
111
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
112
    The weave is represented as a list mixing edit instructions and
944 by Martin Pool
- refactor member names in Weave code
113
    literal text.  Each entry in _weave can be either a string (or
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
114
    unicode), or a tuple.  If a string, it means that the given line
115
    should be output in the currently active revisions.
116
117
    If a tuple, it gives a processing instruction saying in which
118
    revisions the enclosed lines are active.  The tuple has the form
119
    (instruction, version).
120
121
    The instruction can be '{' or '}' for an insertion block, and '['
122
    and ']' for a deletion block respectively.  The version is the
0.1.45 by Martin Pool
doc
123
    integer version index.  There is no replace operator, only deletes
1075 by Martin Pool
- don't store redundant version number at end of insert blocks
124
    and inserts.  For '}', the end of an insertion, there is no
125
    version parameter because it always closes the most recently
126
    opened insertion.
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
127
0.1.41 by Martin Pool
Doc
128
    Constraints/notes:
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
129
130
    * A later version can delete lines that were introduced by any
131
      number of ancestor versions; this implies that deletion
132
      instructions can span insertion blocks without regard to the
133
      insertion block's nesting.
134
0.1.41 by Martin Pool
Doc
135
    * Similarly, deletions need not be properly nested with regard to
136
      each other, because they might have been generated by
137
      independent revisions.
138
0.1.45 by Martin Pool
doc
139
    * Insertions are always made by inserting a new bracketed block
140
      into a single point in the previous weave.  This implies they
141
      can nest but not overlap, and the nesting must always have later
142
      insertions on the inside.
143
0.1.41 by Martin Pool
Doc
144
    * It doesn't seem very useful to have an active insertion
145
      inside an inactive insertion, but it might happen.
0.1.45 by Martin Pool
doc
146
      
0.1.41 by Martin Pool
Doc
147
    * Therefore, all instructions are always"considered"; that
148
      is passed onto and off the stack.  An outer inactive block
149
      doesn't disable an inner block.
150
151
    * Lines are enabled if the most recent enclosing insertion is
152
      active and none of the enclosing deletions are active.
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
153
0.1.49 by Martin Pool
Add another constraint: revisions should not delete text that they
154
    * There is no point having a deletion directly inside its own
155
      insertion; you might as well just not write it.  And there
156
      should be no way to get an earlier version deleting a later
157
      version.
158
944 by Martin Pool
- refactor member names in Weave code
159
    _weave
160
        Text of the weave; list of control instruction tuples and strings.
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
161
944 by Martin Pool
- refactor member names in Weave code
162
    _parents
892 by Martin Pool
- weave stores only direct parents, and calculates and memoizes expansion as needed
163
        List of parents, indexed by version number.
164
        It is only necessary to store the minimal set of parents for
165
        each version; the parent's parents are implied.
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
166
0.1.89 by Martin Pool
Store SHA1 in weave file for later verification
167
    _sha1s
1083 by Martin Pool
- add space to store revision-id in weave files
168
        List of hex SHA-1 of each version.
169
170
    _names
171
        List of symbolic names for each version.  Each should be unique.
172
173
    _name_map
174
        For each name, the version number.
1209 by Martin Pool
- Add Weave._weave_name for debugging purposes
175
176
    _weave_name
177
        Descriptive name of this weave; typically the filename if known.
178
        Set by read_weave.
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
179
    """
938 by Martin Pool
- various optimizations to weave add code
180
1209 by Martin Pool
- Add Weave._weave_name for debugging purposes
181
    __slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
182
                 '_weave_name']
938 by Martin Pool
- various optimizations to weave add code
183
    
1209 by Martin Pool
- Add Weave._weave_name for debugging purposes
184
    def __init__(self, weave_name=None):
944 by Martin Pool
- refactor member names in Weave code
185
        self._weave = []
186
        self._parents = []
0.1.89 by Martin Pool
Store SHA1 in weave file for later verification
187
        self._sha1s = []
1083 by Martin Pool
- add space to store revision-id in weave files
188
        self._names = []
189
        self._name_map = {}
1209 by Martin Pool
- Add Weave._weave_name for debugging purposes
190
        self._weave_name = weave_name
0.1.60 by Martin Pool
Weave eq and ne methods
191
192
1393.1.51 by Martin Pool
- new Weave.copy()
193
    def copy(self):
194
        """Return a deep copy of self.
195
        
196
        The copy can be modified without affecting the original weave."""
197
        other = Weave()
198
        other._weave = self._weave[:]
199
        other._parents = self._parents[:]
200
        other._sha1s = self._sha1s[:]
201
        other._names = self._names[:]
202
        other._name_map = self._name_map.copy()
203
        other._weave_name = self._weave_name
204
        return other
205
0.1.60 by Martin Pool
Weave eq and ne methods
206
    def __eq__(self, other):
207
        if not isinstance(other, Weave):
208
            return False
944 by Martin Pool
- refactor member names in Weave code
209
        return self._parents == other._parents \
1083 by Martin Pool
- add space to store revision-id in weave files
210
               and self._weave == other._weave \
211
               and self._sha1s == other._sha1s 
212
0.1.60 by Martin Pool
Weave eq and ne methods
213
    
214
    def __ne__(self, other):
215
        return not self.__eq__(other)
216
1083 by Martin Pool
- add space to store revision-id in weave files
217
1269 by Martin Pool
- some weave operations automatically look up symbolic names if supplied
218
    def maybe_lookup(self, name_or_index):
219
        """Convert possible symbolic name to index, or pass through indexes."""
220
        if isinstance(name_or_index, (int, long)):
221
            return name_or_index
222
        else:
223
            return self.lookup(name_or_index)
224
225
        
1083 by Martin Pool
- add space to store revision-id in weave files
226
    def lookup(self, name):
1269 by Martin Pool
- some weave operations automatically look up symbolic names if supplied
227
        """Convert symbolic version name to index."""
1083 by Martin Pool
- add space to store revision-id in weave files
228
        try:
229
            return self._name_map[name]
230
        except KeyError:
1260 by Martin Pool
- some updates for fetch/update function
231
            raise WeaveError("name %r not present in weave %r" %
1209 by Martin Pool
- Add Weave._weave_name for debugging purposes
232
                             (name, self._weave_name))
1083 by Martin Pool
- add space to store revision-id in weave files
233
1229 by Martin Pool
- new Weave.idx_to_name and .parents methods
234
1393.1.49 by Martin Pool
- add Weave._check_consistent_with
235
    def iter_names(self):
236
        """Yield a list of all names in this weave."""
237
        return iter(self._names)
238
1229 by Martin Pool
- new Weave.idx_to_name and .parents methods
239
    def idx_to_name(self, version):
240
        return self._names[version]
241
1237 by Martin Pool
- allow the same version to be repeatedly added to a weave
242
1323 by Martin Pool
- caller can pass SHA-1 to Weave.add for efficiency
243
    def _check_repeated_add(self, name, parents, text, sha1):
1237 by Martin Pool
- allow the same version to be repeatedly added to a weave
244
        """Check that a duplicated add is OK.
245
246
        If it is, return the (old) index; otherwise raise an exception.
247
        """
248
        idx = self.lookup(name)
249
        if sorted(self._parents[idx]) != sorted(parents):
250
            raise WeaveError("name \"%s\" already present in weave "
251
                             "with different parents" % name)
1323 by Martin Pool
- caller can pass SHA-1 to Weave.add for efficiency
252
        if sha1 != self._sha1s[idx]:
1237 by Martin Pool
- allow the same version to be repeatedly added to a weave
253
            raise WeaveError("name \"%s\" already present in weave "
254
                             "with different text" % name)            
255
        return idx
256
        
257
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
258
        
1323 by Martin Pool
- caller can pass SHA-1 to Weave.add for efficiency
259
    def add(self, name, parents, text, sha1=None):
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
260
        """Add a single text on top of the weave.
0.1.36 by Martin Pool
doc
261
  
0.1.26 by Martin Pool
Refactor parameters to add command
262
        Returns the index number of the newly added version.
263
1083 by Martin Pool
- add space to store revision-id in weave files
264
        name
265
            Symbolic name for this version.
266
            (Typically the revision-id of the revision that added it.)
267
0.1.26 by Martin Pool
Refactor parameters to add command
268
        parents
892 by Martin Pool
- weave stores only direct parents, and calculates and memoizes expansion as needed
269
            List or set of direct parent version numbers.
270
            
0.1.26 by Martin Pool
Refactor parameters to add command
271
        text
1323 by Martin Pool
- caller can pass SHA-1 to Weave.add for efficiency
272
            Sequence of lines to be added in the new version.
273
274
        sha -- SHA-1 of the file, if known.  This is trusted to be
275
            correct if supplied.
276
        """
1393.1.4 by Martin Pool
- weave tool now finds bzrlib if it's not on the default path
277
        from bzrlib.osutils import sha_strings
938 by Martin Pool
- various optimizations to weave add code
278
1083 by Martin Pool
- add space to store revision-id in weave files
279
        assert isinstance(name, basestring)
1323 by Martin Pool
- caller can pass SHA-1 to Weave.add for efficiency
280
        if sha1 is None:
281
            sha1 = sha_strings(text)
1083 by Martin Pool
- add space to store revision-id in weave files
282
        if name in self._name_map:
1323 by Martin Pool
- caller can pass SHA-1 to Weave.add for efficiency
283
            return self._check_repeated_add(name, parents, text, sha1)
1269 by Martin Pool
- some weave operations automatically look up symbolic names if supplied
284
285
        parents = map(self.maybe_lookup, parents)
938 by Martin Pool
- various optimizations to weave add code
286
        self._check_versions(parents)
0.1.82 by Martin Pool
Small weave optimizations
287
        ## self._check_lines(text)
944 by Martin Pool
- refactor member names in Weave code
288
        new_version = len(self._parents)
0.1.5 by Martin Pool
Add test for storing two text versions.
289
0.1.89 by Martin Pool
Store SHA1 in weave file for later verification
290
1083 by Martin Pool
- add space to store revision-id in weave files
291
        # if we abort after here the (in-memory) weave will be corrupt because only
292
        # some fields are updated
293
        self._parents.append(parents[:])
0.1.89 by Martin Pool
Store SHA1 in weave file for later verification
294
        self._sha1s.append(sha1)
1083 by Martin Pool
- add space to store revision-id in weave files
295
        self._names.append(name)
296
        self._name_map[name] = new_version
938 by Martin Pool
- various optimizations to weave add code
297
298
            
299
        if not parents:
300
            # special case; adding with no parents revision; can do
301
            # this more quickly by just appending unconditionally.
302
            # even more specially, if we're adding an empty text we
303
            # need do nothing at all.
304
            if text:
944 by Martin Pool
- refactor member names in Weave code
305
                self._weave.append(('{', new_version))
306
                self._weave.extend(text)
1075 by Martin Pool
- don't store redundant version number at end of insert blocks
307
                self._weave.append(('}', None))
938 by Martin Pool
- various optimizations to weave add code
308
        
309
            return new_version
310
941 by Martin Pool
- allow for parents specified to Weave.add to be a set
311
        if len(parents) == 1:
312
            pv = list(parents)[0]
313
            if sha1 == self._sha1s[pv]:
314
                # special case: same as the single parent
315
                return new_version
938 by Martin Pool
- various optimizations to weave add code
316
            
317
318
        ancestors = self.inclusions(parents)
319
944 by Martin Pool
- refactor member names in Weave code
320
        l = self._weave
938 by Martin Pool
- various optimizations to weave add code
321
322
        # basis a list of (origin, lineno, line)
323
        basis_lineno = []
324
        basis_lines = []
325
        for origin, lineno, line in self._extract(ancestors):
326
            basis_lineno.append(lineno)
327
            basis_lines.append(line)
328
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
329
        # another small special case: a merge, producing the same text
330
        # as auto-merge
938 by Martin Pool
- various optimizations to weave add code
331
        if text == basis_lines:
332
            return new_version            
333
334
        # add a sentinal, because we can also match against the final line
944 by Martin Pool
- refactor member names in Weave code
335
        basis_lineno.append(len(self._weave))
938 by Martin Pool
- various optimizations to weave add code
336
337
        # XXX: which line of the weave should we really consider
338
        # matches the end of the file?  the current code says it's the
339
        # last line of the weave?
340
341
        #print 'basis_lines:', basis_lines
342
        #print 'new_lines:  ', lines
343
344
        s = SequenceMatcher(None, basis_lines, text)
345
346
        # offset gives the number of lines that have been inserted
347
        # into the weave up to the current point; if the original edit instruction
348
        # says to change line A then we actually change (A+offset)
349
        offset = 0
350
351
        for tag, i1, i2, j1, j2 in s.get_opcodes():
352
            # i1,i2 are given in offsets within basis_lines; we need to map them
353
            # back to offsets within the entire weave
354
            #print 'raw match', tag, i1, i2, j1, j2
355
            if tag == 'equal':
356
                continue
357
358
            i1 = basis_lineno[i1]
359
            i2 = basis_lineno[i2]
360
361
            assert 0 <= j1 <= j2 <= len(text)
362
363
            #print tag, i1, i2, j1, j2
364
365
            # the deletion and insertion are handled separately.
366
            # first delete the region.
367
            if i1 != i2:
944 by Martin Pool
- refactor member names in Weave code
368
                self._weave.insert(i1+offset, ('[', new_version))
369
                self._weave.insert(i2+offset+1, (']', new_version))
938 by Martin Pool
- various optimizations to weave add code
370
                offset += 2
371
372
            if j1 != j2:
373
                # there may have been a deletion spanning up to
374
                # i2; we want to insert after this region to make sure
375
                # we don't destroy ourselves
376
                i = i2 + offset
944 by Martin Pool
- refactor member names in Weave code
377
                self._weave[i:i] = ([('{', new_version)] 
1075 by Martin Pool
- don't store redundant version number at end of insert blocks
378
                                    + text[j1:j2] 
379
                                    + [('}', None)])
938 by Martin Pool
- various optimizations to weave add code
380
                offset += 2 + (j2 - j1)
381
382
        return new_version
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
383
1092.2.22 by Robert Collins
text_version and name_version unification looking reasonable
384
    def add_identical(self, old_rev_id, new_rev_id, parents):
385
        """Add an identical text to old_rev_id as new_rev_id."""
386
        old_lines = self.get(self.lookup(old_rev_id))
387
        self.add(new_rev_id, parents, old_lines)
0.1.27 by Martin Pool
Check that version numbers passed in are reasonable
388
0.1.78 by Martin Pool
Rename Weave.get_included to inclusions and getiter to get_iter
389
    def inclusions(self, versions):
893 by Martin Pool
- Refactor weave calculation of inclusions
390
        """Return set of all ancestors of given version(s)."""
928 by Martin Pool
- go back to using plain builtin set()
391
        i = set(versions)
1328 by Martin Pool
- slight refactoring of weave code
392
        for v in xrange(max(versions), 0, -1):
393
            if v in i:
394
                # include all its parents
395
                i.update(self._parents[v])
396
        return i
397
        ## except IndexError:
398
        ##     raise ValueError("version %d not present in weave" % v)
0.1.77 by Martin Pool
New Weave.get_included() does transitive expansion
399
400
1229 by Martin Pool
- new Weave.idx_to_name and .parents methods
401
    def parents(self, version):
402
        return self._parents[version]
403
404
1393.1.69 by Martin Pool
- new Weave.parent_names
405
    def parent_names(self, version):
406
        """Return version names for parents of a version."""
407
        return map(self.idx_to_name, self._parents[self.lookup(version)])
408
409
890 by Martin Pool
- weave info should show minimal expression of parents
410
    def minimal_parents(self, version):
411
        """Find the minimal set of parents for the version."""
944 by Martin Pool
- refactor member names in Weave code
412
        included = self._parents[version]
890 by Martin Pool
- weave info should show minimal expression of parents
413
        if not included:
414
            return []
415
        
416
        li = list(included)
893 by Martin Pool
- Refactor weave calculation of inclusions
417
        li.sort(reverse=True)
890 by Martin Pool
- weave info should show minimal expression of parents
418
419
        mininc = []
928 by Martin Pool
- go back to using plain builtin set()
420
        gotit = set()
890 by Martin Pool
- weave info should show minimal expression of parents
421
422
        for pv in li:
423
            if pv not in gotit:
424
                mininc.append(pv)
893 by Martin Pool
- Refactor weave calculation of inclusions
425
                gotit.update(self.inclusions(pv))
890 by Martin Pool
- weave info should show minimal expression of parents
426
427
        assert mininc[0] >= 0
428
        assert mininc[-1] < version
429
        return mininc
430
431
0.1.75 by Martin Pool
Remove VerInfo class; just store sets directly in the list of
432
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
433
    def _check_lines(self, text):
434
        if not isinstance(text, list):
435
            raise ValueError("text should be a list, not %s" % type(text))
436
437
        for l in text:
438
            if not isinstance(l, basestring):
869 by Martin Pool
- more weave.py command line options
439
                raise ValueError("text line should be a string or unicode, not %s"
440
                                 % type(l))
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
441
        
442
443
0.1.27 by Martin Pool
Check that version numbers passed in are reasonable
444
    def _check_versions(self, indexes):
445
        """Check everything in the sequence of indexes is valid"""
446
        for i in indexes:
447
            try:
944 by Martin Pool
- refactor member names in Weave code
448
                self._parents[i]
0.1.27 by Martin Pool
Check that version numbers passed in are reasonable
449
            except IndexError:
450
                raise IndexError("invalid version number %r" % i)
451
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
452
    
1269 by Martin Pool
- some weave operations automatically look up symbolic names if supplied
453
    def annotate(self, name_or_index):
454
        return list(self.annotate_iter(name_or_index))
455
456
457
    def annotate_iter(self, name_or_index):
0.1.7 by Martin Pool
Add trivial annotate text
458
        """Yield list of (index-id, line) pairs for the specified version.
459
460
        The index indicates when the line originated in the weave."""
1269 by Martin Pool
- some weave operations automatically look up symbolic names if supplied
461
        incls = [self.maybe_lookup(name_or_index)]
462
        for origin, lineno, text in self._extract(incls):
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
463
            yield origin, text
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
464
465
918 by Martin Pool
- start doing new weave-merge algorithm
466
    def _walk(self):
467
        """Walk the weave.
468
469
        Yields sequence of
470
        (lineno, insert, deletes, text)
471
        for each literal line.
472
        """
473
        
474
        istack = []
928 by Martin Pool
- go back to using plain builtin set()
475
        dset = set()
918 by Martin Pool
- start doing new weave-merge algorithm
476
477
        lineno = 0         # line of weave, 0-based
478
944 by Martin Pool
- refactor member names in Weave code
479
        for l in self._weave:
918 by Martin Pool
- start doing new weave-merge algorithm
480
            if isinstance(l, tuple):
481
                c, v = l
482
                isactive = None
483
                if c == '{':
484
                    istack.append(v)
485
                elif c == '}':
1075 by Martin Pool
- don't store redundant version number at end of insert blocks
486
                    istack.pop()
918 by Martin Pool
- start doing new weave-merge algorithm
487
                elif c == '[':
926 by Martin Pool
- update more weave code to use intsets
488
                    assert v not in dset
489
                    dset.add(v)
918 by Martin Pool
- start doing new weave-merge algorithm
490
                elif c == ']':
926 by Martin Pool
- update more weave code to use intsets
491
                    dset.remove(v)
918 by Martin Pool
- start doing new weave-merge algorithm
492
                else:
493
                    raise WeaveFormatError('unexpected instruction %r'
494
                                           % v)
495
            else:
496
                assert isinstance(l, basestring)
497
                assert istack
498
                yield lineno, istack[-1], dset, l
499
            lineno += 1
500
501
502
893 by Martin Pool
- Refactor weave calculation of inclusions
503
    def _extract(self, versions):
0.1.20 by Martin Pool
Factor out Knit.extract() method
504
        """Yield annotation of lines in included set.
505
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
506
        Yields a sequence of tuples (origin, lineno, text), where
507
        origin is the origin version, lineno the index in the weave,
508
        and text the text of the line.
509
0.1.20 by Martin Pool
Factor out Knit.extract() method
510
        The set typically but not necessarily corresponds to a version.
511
        """
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
512
        for i in versions:
513
            if not isinstance(i, int):
514
                raise ValueError(i)
515
            
893 by Martin Pool
- Refactor weave calculation of inclusions
516
        included = self.inclusions(versions)
881 by Martin Pool
- faster weave extraction
517
518
        istack = []
928 by Martin Pool
- go back to using plain builtin set()
519
        dset = set()
0.1.48 by Martin Pool
Basic parsing of delete instructions.
520
521
        lineno = 0         # line of weave, 0-based
891 by Martin Pool
- fix up refactoring of weave
522
894 by Martin Pool
- small optimization for weave extract
523
        isactive = None
0.1.85 by Martin Pool
doc
524
931 by Martin Pool
- experiment with making Weave._extract() return a list, not a generator - slightly faster
525
        result = []
526
0.1.63 by Martin Pool
Abbreviate WeaveFormatError in some code
527
        WFE = WeaveFormatError
0.1.95 by Martin Pool
- preliminary merge conflict detection
528
944 by Martin Pool
- refactor member names in Weave code
529
        for l in self._weave:
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
530
            if isinstance(l, tuple):
531
                c, v = l
894 by Martin Pool
- small optimization for weave extract
532
                isactive = None
891 by Martin Pool
- fix up refactoring of weave
533
                if c == '{':
534
                    assert v not in istack
535
                    istack.append(v)
536
                elif c == '}':
1075 by Martin Pool
- don't store redundant version number at end of insert blocks
537
                    istack.pop()
891 by Martin Pool
- fix up refactoring of weave
538
                elif c == '[':
539
                    if v in included:
881 by Martin Pool
- faster weave extraction
540
                        assert v not in dset
0.1.48 by Martin Pool
Basic parsing of delete instructions.
541
                        dset.add(v)
891 by Martin Pool
- fix up refactoring of weave
542
                else:
543
                    assert c == ']'
544
                    if v in included:
881 by Martin Pool
- faster weave extraction
545
                        assert v in dset
0.1.48 by Martin Pool
Basic parsing of delete instructions.
546
                        dset.remove(v)
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
547
            else:
548
                assert isinstance(l, basestring)
894 by Martin Pool
- small optimization for weave extract
549
                if isactive is None:
550
                    isactive = (not dset) and istack and (istack[-1] in included)
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
551
                if isactive:
931 by Martin Pool
- experiment with making Weave._extract() return a list, not a generator - slightly faster
552
                    result.append((istack[-1], lineno, l))
0.1.39 by Martin Pool
Change to a more realistic weave structure which can represent insertions and
553
            lineno += 1
0.1.7 by Martin Pool
Add trivial annotate text
554
0.1.46 by Martin Pool
More constraints on structure of weave, and checks that they work
555
        if istack:
0.1.63 by Martin Pool
Abbreviate WeaveFormatError in some code
556
            raise WFE("unclosed insertion blocks at end of weave",
0.1.47 by Martin Pool
New WeaveError and WeaveFormatError rather than assertions.
557
                                   istack)
0.1.48 by Martin Pool
Basic parsing of delete instructions.
558
        if dset:
0.1.63 by Martin Pool
Abbreviate WeaveFormatError in some code
559
            raise WFE("unclosed deletion blocks at end of weave",
0.1.48 by Martin Pool
Basic parsing of delete instructions.
560
                                   dset)
0.1.40 by Martin Pool
Add test for extracting from weave with nested insertions
561
931 by Martin Pool
- experiment with making Weave._extract() return a list, not a generator - slightly faster
562
        return result
563
    
564
0.1.7 by Martin Pool
Add trivial annotate text
565
1269 by Martin Pool
- some weave operations automatically look up symbolic names if supplied
566
    def get_iter(self, name_or_index):
0.1.5 by Martin Pool
Add test for storing two text versions.
567
        """Yield lines for the specified version."""
1269 by Martin Pool
- some weave operations automatically look up symbolic names if supplied
568
        incls = [self.maybe_lookup(name_or_index)]
569
        for origin, lineno, line in self._extract(incls):
0.1.8 by Martin Pool
Unify get/annotate code
570
            yield line
0.1.5 by Martin Pool
Add test for storing two text versions.
571
572
1369 by Martin Pool
- try to avoid redundant conversion of strings when retrieving from weaves
573
    def get_text(self, name_or_index):
574
        return ''.join(self.get_iter(name_or_index))
575
        assert isinstance(version, int)
576
577
578
    def get_lines(self, name_or_index):
1269 by Martin Pool
- some weave operations automatically look up symbolic names if supplied
579
        return list(self.get_iter(name_or_index))
0.1.1 by Martin Pool
Check in old existing knit code.
580
581
1369 by Martin Pool
- try to avoid redundant conversion of strings when retrieving from weaves
582
    get = get_lines
583
584
0.1.95 by Martin Pool
- preliminary merge conflict detection
585
    def mash_iter(self, included):
0.1.65 by Martin Pool
Add Weave.merge_iter to get automerged lines
586
        """Return composed version of multiple included versions."""
1338 by Martin Pool
- Weave.mash_iter optionally takes names rather than indexes
587
        included = map(self.maybe_lookup, included)
893 by Martin Pool
- Refactor weave calculation of inclusions
588
        for origin, lineno, text in self._extract(included):
0.1.65 by Martin Pool
Add Weave.merge_iter to get automerged lines
589
            yield text
590
591
0.1.11 by Martin Pool
Add Knit.dump method
592
    def dump(self, to_file):
593
        from pprint import pprint
944 by Martin Pool
- refactor member names in Weave code
594
        print >>to_file, "Weave._weave = ",
595
        pprint(self._weave, to_file)
596
        print >>to_file, "Weave._parents = ",
597
        pprint(self._parents, to_file)
0.1.11 by Martin Pool
Add Knit.dump method
598
599
0.1.91 by Martin Pool
Update Weave.check
600
601
    def numversions(self):
944 by Martin Pool
- refactor member names in Weave code
602
        l = len(self._parents)
0.1.91 by Martin Pool
Update Weave.check
603
        assert l == len(self._sha1s)
604
        return l
605
606
946 by Martin Pool
- weave info only shows the weave headers, doesn't extract every version:
607
    def __len__(self):
608
        return self.numversions()
609
610
894 by Martin Pool
- small optimization for weave extract
611
    def check(self, progress_bar=None):
0.1.91 by Martin Pool
Update Weave.check
612
        # check no circular inclusions
613
        for version in range(self.numversions()):
944 by Martin Pool
- refactor member names in Weave code
614
            inclusions = list(self._parents[version])
0.1.91 by Martin Pool
Update Weave.check
615
            if inclusions:
616
                inclusions.sort()
617
                if inclusions[-1] >= version:
0.1.47 by Martin Pool
New WeaveError and WeaveFormatError rather than assertions.
618
                    raise WeaveFormatError("invalid included version %d for index %d"
0.1.91 by Martin Pool
Update Weave.check
619
                                           % (inclusions[-1], version))
620
621
        # try extracting all versions; this is a bit slow and parallel
622
        # extraction could be used
894 by Martin Pool
- small optimization for weave extract
623
        nv = self.numversions()
624
        for version in range(nv):
625
            if progress_bar:
626
                progress_bar.update('checking text', version, nv)
0.1.91 by Martin Pool
Update Weave.check
627
            s = sha.new()
628
            for l in self.get_iter(version):
629
                s.update(l)
630
            hd = s.hexdigest()
631
            expected = self._sha1s[version]
632
            if hd != expected:
633
                raise WeaveError("mismatched sha1 for version %d; "
634
                                 "got %s, expected %s"
635
                                 % (version, hd, expected))
0.1.18 by Martin Pool
Better Knit.dump method
636
881 by Martin Pool
- faster weave extraction
637
        # TODO: check insertions are properly nested, that there are
638
        # no lines outside of insertion blocks, that deletions are
639
        # properly paired, etc.
640
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
641
642
0.1.95 by Martin Pool
- preliminary merge conflict detection
643
    def merge(self, merge_versions):
644
        """Automerge and mark conflicts between versions.
645
646
        This returns a sequence, each entry describing alternatives
647
        for a chunk of the file.  Each of the alternatives is given as
648
        a list of lines.
649
650
        If there is a chunk of the file where there's no diagreement,
651
        only one alternative is given.
652
        """
653
        # approach: find the included versions common to all the
654
        # merged versions
655
        raise NotImplementedError()
656
657
658
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
659
    def _delta(self, included, lines):
660
        """Return changes from basis to new revision.
661
662
        The old text for comparison is the union of included revisions.
663
664
        This is used in inserting a new text.
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
665
0.1.55 by Martin Pool
doc
666
        Delta is returned as a sequence of
667
        (weave1, weave2, newlines).
668
669
        This indicates that weave1:weave2 of the old weave should be
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
670
        replaced by the sequence of lines in newlines.  Note that
671
        these line numbers are positions in the total weave and don't
672
        correspond to the lines in any extracted version, or even the
673
        extracted union of included versions.
674
675
        If line1=line2, this is a pure insert; if newlines=[] this is a
676
        pure delete.  (Similar to difflib.)
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
677
        """
1393.1.47 by Martin Pool
- Weave._delta is not implemented at the moment
678
        raise NotImplementedError()
0.1.1 by Martin Pool
Check in old existing knit code.
679
918 by Martin Pool
- start doing new weave-merge algorithm
680
            
681
    def plan_merge(self, ver_a, ver_b):
682
        """Return pseudo-annotation indicating how the two versions merge.
683
684
        This is computed between versions a and b and their common
685
        base.
686
687
        Weave lines present in none of them are skipped entirely.
688
        """
926 by Martin Pool
- update more weave code to use intsets
689
        inc_a = self.inclusions([ver_a])
690
        inc_b = self.inclusions([ver_b])
918 by Martin Pool
- start doing new weave-merge algorithm
691
        inc_c = inc_a & inc_b
692
693
        for lineno, insert, deleteset, line in self._walk():
694
            if deleteset & inc_c:
695
                # killed in parent; can't be in either a or b
696
                # not relevant to our work
697
                yield 'killed-base', line
926 by Martin Pool
- update more weave code to use intsets
698
            elif insert in inc_c:
918 by Martin Pool
- start doing new weave-merge algorithm
699
                # was inserted in base
700
                killed_a = bool(deleteset & inc_a)
701
                killed_b = bool(deleteset & inc_b)
702
                if killed_a and killed_b:
703
                    yield 'killed-both', line
704
                elif killed_a:
705
                    yield 'killed-a', line
706
                elif killed_b:
707
                    yield 'killed-b', line
708
                else:
709
                    yield 'unchanged', line
926 by Martin Pool
- update more weave code to use intsets
710
            elif insert in inc_a:
918 by Martin Pool
- start doing new weave-merge algorithm
711
                if deleteset & inc_a:
712
                    yield 'ghost-a', line
713
                else:
714
                    # new in A; not in B
715
                    yield 'new-a', line
926 by Martin Pool
- update more weave code to use intsets
716
            elif insert in inc_b:
918 by Martin Pool
- start doing new weave-merge algorithm
717
                if deleteset & inc_b:
718
                    yield 'ghost-b', line
719
                else:
720
                    yield 'new-b', line
721
            else:
722
                # not in either revision
723
                yield 'irrelevant', line
724
919 by Martin Pool
- more development of weave-merge
725
        yield 'unchanged', ''           # terminator
726
727
728
729
    def weave_merge(self, plan):
730
        lines_a = []
731
        lines_b = []
732
        ch_a = ch_b = False
733
734
        for state, line in plan:
735
            if state == 'unchanged' or state == 'killed-both':
736
                # resync and flush queued conflicts changes if any
737
                if not lines_a and not lines_b:
738
                    pass
739
                elif ch_a and not ch_b:
740
                    # one-sided change:                    
741
                    for l in lines_a: yield l
742
                elif ch_b and not ch_a:
743
                    for l in lines_b: yield l
744
                elif lines_a == lines_b:
745
                    for l in lines_a: yield l
746
                else:
747
                    yield '<<<<\n'
748
                    for l in lines_a: yield l
749
                    yield '====\n'
750
                    for l in lines_b: yield l
751
                    yield '>>>>\n'
752
753
                del lines_a[:]
754
                del lines_b[:]
755
                ch_a = ch_b = False
756
                
757
            if state == 'unchanged':
758
                if line:
759
                    yield line
760
            elif state == 'killed-a':
761
                ch_a = True
762
                lines_b.append(line)
763
            elif state == 'killed-b':
764
                ch_b = True
765
                lines_a.append(line)
766
            elif state == 'new-a':
767
                ch_a = True
768
                lines_a.append(line)
769
            elif state == 'new-b':
770
                ch_b = True
771
                lines_b.append(line)
772
            else:
920 by Martin Pool
- add more test cases for weave_merge
773
                assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
774
                                 'killed-both'), \
919 by Martin Pool
- more development of weave-merge
775
                       state
776
777
                
1393.1.48 by Martin Pool
- Add stub Weave.join() method
778
    def join(self, other):
779
        """Integrate versions from other into this weave.
780
1393.1.49 by Martin Pool
- add Weave._check_consistent_with
781
        The resulting weave contains all the history of both weaves; 
782
        any version you could retrieve from either self or other can be 
783
        retrieved from self after this call.
1393.1.48 by Martin Pool
- Add stub Weave.join() method
784
1393.1.49 by Martin Pool
- add Weave._check_consistent_with
785
        It is illegal for the two weaves to contain different values 
1393.1.68 by Martin Pool
- add reweave that joins with ghosts
786
        or different parents for any version.  See also reweave().
1393.1.48 by Martin Pool
- Add stub Weave.join() method
787
        """
788
        if other.numversions() == 0:
789
            return          # nothing to update, easy
1185.13.4 by Robert Collins
make reweave visible as a weave method, and quickly integrate into fetch
790
        # two loops so that we do not change ourselves before verifying it
791
        # will be ok
1393.1.50 by Martin Pool
- more development of Weave.join()
792
        # work through in index order to make sure we get all dependencies
793
        for other_idx, name in enumerate(other._names):
1185.13.4 by Robert Collins
make reweave visible as a weave method, and quickly integrate into fetch
794
            if self._check_version_consistent(other, other_idx, name):
795
                continue
796
        for other_idx, name in enumerate(other._names):
1393.1.50 by Martin Pool
- more development of Weave.join()
797
            # TODO: If all the parents of the other version are already 
798
            # present then we can avoid some work by just taking the delta
799
            # and adjusting the offsets.
1393.1.51 by Martin Pool
- new Weave.copy()
800
            new_parents = self._imported_parents(other, other_idx)
1393.1.50 by Martin Pool
- more development of Weave.join()
801
            lines = other.get_lines(other_idx)
802
            sha1 = other._sha1s[other_idx]
803
            self.add(name, new_parents, lines, sha1)
804
1393.1.51 by Martin Pool
- new Weave.copy()
805
806
    def _imported_parents(self, other, other_idx):
807
        """Return list of parents in self corresponding to indexes in other."""
808
        new_parents = []
809
        for parent_idx in other._parents[other_idx]:
810
            parent_name = other._names[parent_idx]
811
            if parent_name not in self._names:
812
                # should not be possible
813
                raise WeaveError("missing parent {%s} of {%s} in %r" 
814
                                 % (parent_name, other._name_map[other_idx], self))
815
            new_parents.append(self._name_map[parent_name])
816
        return new_parents
817
1393.1.50 by Martin Pool
- more development of Weave.join()
818
    def _check_version_consistent(self, other, other_idx, name):
819
        """Check if a version in consistent in this and other.
1393.1.66 by Martin Pool
- fix join of weaves where parents occur at different offsets
820
821
        To be consistent it must have:
822
823
         * the same text
824
         * the same direct parents (by name, not index, and disregarding
825
           order)
1393.1.50 by Martin Pool
- more development of Weave.join()
826
        
827
        If present & correct return True;
828
        if not present in self return False; 
829
        if inconsistent raise error."""
830
        this_idx = self._name_map.get(name, -1)
831
        if this_idx != -1:
832
            if self._sha1s[this_idx] != other._sha1s[other_idx]:
1393.1.66 by Martin Pool
- fix join of weaves where parents occur at different offsets
833
                raise WeaveError("inconsistent texts for version {%s} "
834
                                 "when joining weaves"
835
                                 % (name))
836
            self_parents = self._parents[this_idx]
837
            other_parents = other._parents[other_idx]
838
            n1 = [self._names[i] for i in self_parents]
839
            n2 = [other._names[i] for i in other_parents]
840
            n1.sort()
841
            n2.sort()
842
            if n1 != n2:
1185.13.4 by Robert Collins
make reweave visible as a weave method, and quickly integrate into fetch
843
                raise WeaveParentMismatch("inconsistent parents "
844
                    "for version {%s}: %s vs %s" % (name, n1, n2))
1393.1.50 by Martin Pool
- more development of Weave.join()
845
            else:
846
                return True         # ok!
847
        else:
848
            return False
918 by Martin Pool
- start doing new weave-merge algorithm
849
1185.13.4 by Robert Collins
make reweave visible as a weave method, and quickly integrate into fetch
850
    def reweave(self, other):
851
        """Reweave self with other."""
852
        new_weave = reweave(self, other)
853
        for attr in self.__slots__:
854
            setattr(self, attr, getattr(new_weave, attr))
855
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
856
1393.1.68 by Martin Pool
- add reweave that joins with ghosts
857
def reweave(wa, wb):
858
    """Combine two weaves and return the result.
859
860
    This works even if a revision R has different parents in 
861
    wa and wb.  In the resulting weave all the parents are given.
862
863
    This is done by just building up a new weave, maintaining ordering 
864
    of the versions in the two inputs.  More efficient approaches
865
    might be possible but it should only be necessary to do 
866
    this operation rarely, when a new previously ghost version is 
867
    inserted.
868
    """
869
    wr = Weave()
870
    ia = ib = 0
871
    queue_a = range(wa.numversions())
872
    queue_b = range(wb.numversions())
873
    # first determine combined parents of all versions
874
    # map from version name -> all parent names
875
    combined_parents = _reweave_parent_graphs(wa, wb)
876
    mutter("combined parents: %r", combined_parents)
877
    order = _make_reweave_order(wa._names, wb._names, combined_parents)
878
    mutter("order to reweave: %r", order)
879
    for name in order:
880
        if name in wa._name_map:
881
            lines = wa.get_lines(name)
882
            if name in wb._name_map:
883
                assert lines == wb.get_lines(name)
884
        else:
885
            lines = wb.get_lines(name)
886
        wr.add(name, combined_parents[name], lines)
887
    return wr
888
889
890
def _reweave_parent_graphs(wa, wb):
891
    """Return combined parent ancestry for two weaves.
892
    
893
    Returned as a list of (version_name, set(parent_names))"""
894
    combined = {}
895
    for weave in [wa, wb]:
896
        for idx, name in enumerate(weave._names):
897
            p = combined.setdefault(name, set())
898
            p.update(map(weave.idx_to_name, weave._parents[idx]))
899
    return combined
900
901
902
def _make_reweave_order(wa_order, wb_order, combined_parents):
903
    """Return an order to reweave versions respecting parents."""
904
    done = set()
905
    result = []
906
    ia = ib = 0
907
    next_a = next_b = None
908
    len_a = len(wa_order)
909
    len_b = len(wb_order)
910
    while ia < len(wa_order) or ib < len(wb_order):
911
        if ia < len_a:
912
            next_a = wa_order[ia]
913
            if next_a in done:
914
                ia += 1
915
                continue
916
            if combined_parents[next_a].issubset(done):
917
                ia += 1
918
                result.append(next_a)
919
                done.add(next_a)
920
                continue
921
        if ib < len_b:
922
            next_b = wb_order[ib]
923
            if next_b in done:
924
                ib += 1
925
                continue
926
            elif combined_parents[next_b].issubset(done):
927
                ib += 1
928
                result.append(next_b)
929
                done.add(next_b)
930
                continue
931
        raise WeaveError("don't know how to reweave at {%s} and {%s}"
932
                         % (next_a, next_b))
933
    return result
934
935
1081 by Martin Pool
- if weave tool is invoked with no arguments, show help
936
def weave_toc(w):
937
    """Show the weave's table-of-contents"""
1083 by Martin Pool
- add space to store revision-id in weave files
938
    print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
939
    for i in (6, 50, 10, 10):
870 by Martin Pool
- better weave info display
940
        print '-' * i,
941
    print
946 by Martin Pool
- weave info only shows the weave headers, doesn't extract every version:
942
    for i in range(w.numversions()):
0.1.91 by Martin Pool
Update Weave.check
943
        sha1 = w._sha1s[i]
1083 by Martin Pool
- add space to store revision-id in weave files
944
        name = w._names[i]
945
        parent_str = ' '.join(map(str, w._parents[i]))
946
        print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
0.1.88 by Martin Pool
Add weave info command.
947
869 by Martin Pool
- more weave.py command line options
948
949
1393.1.4 by Martin Pool
- weave tool now finds bzrlib if it's not on the default path
950
def weave_stats(weave_file, pb):
947 by Martin Pool
- new 'weave stats' command
951
    from bzrlib.weavefile import read_weave
952
953
    wf = file(weave_file, 'rb')
954
    w = read_weave(wf)
955
    # FIXME: doesn't work on pipes
956
    weave_size = wf.tell()
957
958
    total = 0
959
    vers = len(w)
960
    for i in range(vers):
961
        pb.update('checking sizes', i, vers)
1393.1.24 by Martin Pool
- slight speedup for weave stats
962
        for origin, lineno, line in w._extract([i]):
947 by Martin Pool
- new 'weave stats' command
963
            total += len(line)
964
965
    pb.clear()
966
967
    print 'versions          %9d' % vers
968
    print 'weave file        %9d bytes' % weave_size
969
    print 'total contents    %9d bytes' % total
970
    print 'compression ratio %9.2fx' % (float(total) / float(weave_size))
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
971
    if vers:
972
        avg = total/vers
973
        print 'average size      %9d bytes' % avg
974
        print 'relative size     %9.2fx' % (float(weave_size) / float(avg))
947 by Martin Pool
- new 'weave stats' command
975
976
869 by Martin Pool
- more weave.py command line options
977
def usage():
871 by Martin Pool
- add command for merge-based weave
978
    print """bzr weave tool
979
980
Experimental tool for weave algorithm.
981
869 by Martin Pool
- more weave.py command line options
982
usage:
983
    weave init WEAVEFILE
984
        Create an empty weave file
985
    weave get WEAVEFILE VERSION
986
        Write out specified version.
987
    weave check WEAVEFILE
988
        Check consistency of all versions.
1081 by Martin Pool
- if weave tool is invoked with no arguments, show help
989
    weave toc WEAVEFILE
869 by Martin Pool
- more weave.py command line options
990
        Display table of contents.
1084 by Martin Pool
- weave add command needs to take a symbolic name too
991
    weave add WEAVEFILE NAME [BASE...] < NEWTEXT
869 by Martin Pool
- more weave.py command line options
992
        Add NEWTEXT, with specified parent versions.
993
    weave annotate WEAVEFILE VERSION
994
        Display origin of each line.
995
    weave mash WEAVEFILE VERSION...
996
        Display composite of all selected versions.
997
    weave merge WEAVEFILE VERSION1 VERSION2 > OUT
998
        Auto-merge two versions and display conflicts.
1329 by Martin Pool
- add diff command to weave utility
999
    weave diff WEAVEFILE VERSION1 VERSION2 
1000
        Show differences between two versions.
871 by Martin Pool
- add command for merge-based weave
1001
1002
example:
1003
1004
    % weave init foo.weave
1005
    % vi foo.txt
1084 by Martin Pool
- weave add command needs to take a symbolic name too
1006
    % weave add foo.weave ver0 < foo.txt
871 by Martin Pool
- add command for merge-based weave
1007
    added version 0
1008
1009
    (create updated version)
1010
    % vi foo.txt
1011
    % weave get foo.weave 0 | diff -u - foo.txt
1084 by Martin Pool
- weave add command needs to take a symbolic name too
1012
    % weave add foo.weave ver1 0 < foo.txt
871 by Martin Pool
- add command for merge-based weave
1013
    added version 1
1014
1015
    % weave get foo.weave 0 > foo.txt       (create forked version)
1016
    % vi foo.txt
1084 by Martin Pool
- weave add command needs to take a symbolic name too
1017
    % weave add foo.weave ver2 0 < foo.txt
871 by Martin Pool
- add command for merge-based weave
1018
    added version 2
1019
1020
    % weave merge foo.weave 1 2 > foo.txt   (merge them)
1021
    % vi foo.txt                            (resolve conflicts)
1084 by Martin Pool
- weave add command needs to take a symbolic name too
1022
    % weave add foo.weave merged 1 2 < foo.txt     (commit merged version)     
871 by Martin Pool
- add command for merge-based weave
1023
    
869 by Martin Pool
- more weave.py command line options
1024
"""
0.1.88 by Martin Pool
Add weave info command.
1025
    
1026
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
1027
1028
def main(argv):
1029
    import sys
1030
    import os
1393.1.4 by Martin Pool
- weave tool now finds bzrlib if it's not on the default path
1031
    try:
1032
        import bzrlib
1033
    except ImportError:
1034
        # in case we're run directly from the subdirectory
1035
        sys.path.append('..')
1036
        import bzrlib
1336 by Martin Pool
- tidy up import
1037
    from bzrlib.weavefile import write_weave, read_weave
894 by Martin Pool
- small optimization for weave extract
1038
    from bzrlib.progress import ProgressBar
1039
1078 by Martin Pool
- use psyco for weave if possible
1040
    try:
1041
        import psyco
1042
        psyco.full()
1043
    except ImportError:
1044
        pass
894 by Martin Pool
- small optimization for weave extract
1045
1081 by Martin Pool
- if weave tool is invoked with no arguments, show help
1046
    if len(argv) < 2:
1047
        usage()
1048
        return 0
1049
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
1050
    cmd = argv[1]
869 by Martin Pool
- more weave.py command line options
1051
1052
    def readit():
1053
        return read_weave(file(argv[2], 'rb'))
1054
    
1055
    if cmd == 'help':
1056
        usage()
1057
    elif cmd == 'add':
1058
        w = readit()
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
1059
        # at the moment, based on everything in the file
1084 by Martin Pool
- weave add command needs to take a symbolic name too
1060
        name = argv[3]
1061
        parents = map(int, argv[4:])
0.1.72 by Martin Pool
Go back to weave lines normally having newlines at the end.
1062
        lines = sys.stdin.readlines()
1084 by Martin Pool
- weave add command needs to take a symbolic name too
1063
        ver = w.add(name, parents, lines)
869 by Martin Pool
- more weave.py command line options
1064
        write_weave(w, file(argv[2], 'wb'))
1084 by Martin Pool
- weave add command needs to take a symbolic name too
1065
        print 'added version %r %d' % (name, ver)
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
1066
    elif cmd == 'init':
1067
        fn = argv[2]
1068
        if os.path.exists(fn):
1069
            raise IOError("file exists")
1070
        w = Weave()
869 by Martin Pool
- more weave.py command line options
1071
        write_weave(w, file(fn, 'wb'))
1072
    elif cmd == 'get': # get one version
1073
        w = readit()
0.1.94 by Martin Pool
Fix get_iter call
1074
        sys.stdout.writelines(w.get_iter(int(argv[3])))
869 by Martin Pool
- more weave.py command line options
1075
        
1076
    elif cmd == 'mash': # get composite
1077
        w = readit()
1078
        sys.stdout.writelines(w.mash_iter(map(int, argv[3:])))
1079
1329 by Martin Pool
- add diff command to weave utility
1080
    elif cmd == 'diff':
1081
        from difflib import unified_diff
1082
        w = readit()
1083
        fn = argv[2]
1084
        v1, v2 = map(int, argv[3:5])
1085
        lines1 = w.get(v1)
1086
        lines2 = w.get(v2)
1087
        diff_gen = unified_diff(lines1, lines2,
1088
                                '%s version %d' % (fn, v1),
1089
                                '%s version %d' % (fn, v2))
1090
        sys.stdout.writelines(diff_gen)
1091
            
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
1092
    elif cmd == 'annotate':
869 by Martin Pool
- more weave.py command line options
1093
        w = readit()
0.1.72 by Martin Pool
Go back to weave lines normally having newlines at the end.
1094
        # newline is added to all lines regardless; too hard to get
1095
        # reasonable formatting otherwise
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
1096
        lasto = None
1097
        for origin, text in w.annotate(int(argv[3])):
0.1.72 by Martin Pool
Go back to weave lines normally having newlines at the end.
1098
            text = text.rstrip('\r\n')
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
1099
            if origin == lasto:
1100
                print '      | %s' % (text)
1101
            else:
1102
                print '%5d | %s' % (origin, text)
1103
                lasto = origin
871 by Martin Pool
- add command for merge-based weave
1104
                
1081 by Martin Pool
- if weave tool is invoked with no arguments, show help
1105
    elif cmd == 'toc':
1106
        weave_toc(readit())
947 by Martin Pool
- new 'weave stats' command
1107
1108
    elif cmd == 'stats':
1393.1.4 by Martin Pool
- weave tool now finds bzrlib if it's not on the default path
1109
        weave_stats(argv[2], ProgressBar())
871 by Martin Pool
- add command for merge-based weave
1110
        
0.1.91 by Martin Pool
Update Weave.check
1111
    elif cmd == 'check':
869 by Martin Pool
- more weave.py command line options
1112
        w = readit()
894 by Martin Pool
- small optimization for weave extract
1113
        pb = ProgressBar()
1114
        w.check(pb)
1115
        pb.clear()
938 by Martin Pool
- various optimizations to weave add code
1116
        print '%d versions ok' % w.numversions()
871 by Martin Pool
- add command for merge-based weave
1117
892 by Martin Pool
- weave stores only direct parents, and calculates and memoizes expansion as needed
1118
    elif cmd == 'inclusions':
1119
        w = readit()
1120
        print ' '.join(map(str, w.inclusions([int(argv[3])])))
1121
1122
    elif cmd == 'parents':
1123
        w = readit()
944 by Martin Pool
- refactor member names in Weave code
1124
        print ' '.join(map(str, w._parents[int(argv[3])]))
892 by Martin Pool
- weave stores only direct parents, and calculates and memoizes expansion as needed
1125
918 by Martin Pool
- start doing new weave-merge algorithm
1126
    elif cmd == 'plan-merge':
1127
        w = readit()
1128
        for state, line in w.plan_merge(int(argv[3]), int(argv[4])):
919 by Martin Pool
- more development of weave-merge
1129
            if line:
1130
                print '%14s | %s' % (state, line),
918 by Martin Pool
- start doing new weave-merge algorithm
1131
871 by Martin Pool
- add command for merge-based weave
1132
    elif cmd == 'merge':
919 by Martin Pool
- more development of weave-merge
1133
        w = readit()
1134
        p = w.plan_merge(int(argv[3]), int(argv[4]))
1135
        sys.stdout.writelines(w.weave_merge(p))
1136
            
1137
    elif cmd == 'mash-merge':
871 by Martin Pool
- add command for merge-based weave
1138
        if len(argv) != 5:
1139
            usage()
1140
            return 1
1141
1142
        w = readit()
1143
        v1, v2 = map(int, argv[3:5])
1144
1145
        basis = w.inclusions([v1]).intersection(w.inclusions([v2]))
1146
1147
        base_lines = list(w.mash_iter(basis))
1148
        a_lines = list(w.get(v1))
1149
        b_lines = list(w.get(v2))
1150
1151
        from bzrlib.merge3 import Merge3
1152
        m3 = Merge3(base_lines, a_lines, b_lines)
1153
1154
        name_a = 'version %d' % v1
1155
        name_b = 'version %d' % v2
1156
        sys.stdout.writelines(m3.merge_lines(name_a=name_a, name_b=name_b))
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
1157
    else:
1158
        raise ValueError('unknown command %r' % cmd)
1159
    
1160
1076 by Martin Pool
- add code to run weave utility under profiler
1161
1162
def profile_main(argv): 
1163
    import tempfile, hotshot, hotshot.stats
1164
1165
    prof_f = tempfile.NamedTemporaryFile()
1166
1167
    prof = hotshot.Profile(prof_f.name)
1168
1169
    ret = prof.runcall(main, argv)
1170
    prof.close()
1171
1172
    stats = hotshot.stats.load(prof_f.name)
1173
    #stats.strip_dirs()
1079 by Martin Pool
- weavefile can just use lists for read-in ancestry, not frozensets
1174
    stats.sort_stats('cumulative')
1076 by Martin Pool
- add code to run weave utility under profiler
1175
    ## XXX: Might like to write to stderr or the trace file instead but
1176
    ## print_stats seems hardcoded to stdout
1177
    stats.print_stats(20)
1178
            
1179
    return ret
1180
1181
0.1.62 by Martin Pool
Lame command-line client for reading and writing weaves.
1182
if __name__ == '__main__':
1183
    import sys
1081 by Martin Pool
- if weave tool is invoked with no arguments, show help
1184
    if '--profile' in sys.argv:
1078 by Martin Pool
- use psyco for weave if possible
1185
        args = sys.argv[:]
1081 by Martin Pool
- if weave tool is invoked with no arguments, show help
1186
        args.remove('--profile')
1078 by Martin Pool
- use psyco for weave if possible
1187
        sys.exit(profile_main(args))
1188
    else:
1189
        sys.exit(main(sys.argv))
1076 by Martin Pool
- add code to run weave utility under profiler
1190