3
# Copyright (C) 2005 Canonical Ltd
7
# Author: Martin Pool <mbp@canonical.com>
10
"""knit - a weave-like structure"""
14
"""knit - versioned text file storage.
16
A Knit manages versions of line-based text files, keeping track of the
17
originating version for each line.
19
Texts can be identified in either of two ways:
21
* a nonnegative index number.
23
* a version-id string.
25
Typically the index number will be valid only inside this knit and
26
the version-id is used to reference it in the larger world.
29
List of edit instructions.
31
Each line is stored as a tuple of (index-id, text). The line
32
is present in the version equal to index-id.
35
List of versions, indexed by index number. Each one is an empty
36
tuple because the version_id isn't stored yet.
44
"""Add a single text on top of the weave.
46
Returns the index number of the newly added version."""
47
if not isinstance(text, list):
48
raise ValueError("text should be a list, not %s" % type(text))
52
# all of the previous texts are turned off; just append lines at the bottom
54
self._l.append((idx, line))
60
def annotate(self, index):
61
return list(self.annotate_iter(index))
64
def annotate_iter(self, index):
65
"""Yield list of (index-id, line) pairs for the specified version.
67
The index indicates when the line originated in the weave."""
68
self._v[index] # check index is valid
70
for origin, line in self._l:
75
def getiter(self, index):
76
"""Yield lines for the specified version."""
77
for origin, line in self.annotate_iter(index):
82
return list(self.getiter(index))
85
def dump(self, to_file):
86
from pprint import pprint
87
print >>to_file, "knit lines:"
88
pprint(self._l, to_file)
92
def update_knit(knit, new_vers, new_lines):
93
"""Return a new knit whose text matches new_lines.
95
First of all the knit is diffed against the new lines, considering
96
only the text of the lines from the knit. This identifies lines
97
unchanged from the knit, plus insertions and deletions.
99
The deletions are marked as deleted. The insertions are added
100
with their new values.
104
if not isinstance(new_vers, int):
105
raise TypeError('new version-id must be an int: %r' % new_vers)
107
from difflib import SequenceMatcher
108
knit_lines = knit2text(knit)
109
m = SequenceMatcher(None, knit_lines, new_lines)
111
for block in m.get_matching_blocks():
112
print "a[%d] and b[%d] match for %d elements" % block
115
for tag, i1, i2, j1, j2 in m.get_opcodes():
116
print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
117
(tag, i1, i2, knit_lines[i1:i2], j1, j2, new_lines[j1:j2]))
120
new_knit.extend(knit[i1:i2])
121
elif tag == 'delete':
122
for i in range(i1, i2):
124
new_knit.append((kl[0], kl[1], False))