|
1 # manifest.py - manifest revision class for mercurial |
|
2 # |
|
3 # Copyright 2005 Matt Mackall <mpm@selenic.com> |
|
4 # |
|
5 # This software may be used and distributed according to the terms |
|
6 # of the GNU General Public License, incorporated herein by reference. |
|
7 |
|
8 import sys, struct |
|
9 from revlog import * |
|
10 from demandload import * |
|
11 demandload(globals(), "bisect") |
|
12 |
|
13 class manifest(revlog): |
|
14 def __init__(self, opener): |
|
15 self.mapcache = None |
|
16 self.listcache = None |
|
17 self.addlist = None |
|
18 revlog.__init__(self, opener, "00manifest.i", "00manifest.d") |
|
19 |
|
20 def read(self, node): |
|
21 if node == nullid: return {} # don't upset local cache |
|
22 if self.mapcache and self.mapcache[0] == node: |
|
23 return self.mapcache[1] |
|
24 text = self.revision(node) |
|
25 map = {} |
|
26 flag = {} |
|
27 self.listcache = (text, text.splitlines(1)) |
|
28 for l in self.listcache[1]: |
|
29 (f, n) = l.split('\0') |
|
30 map[f] = bin(n[:40]) |
|
31 flag[f] = (n[40:-1] == "x") |
|
32 self.mapcache = (node, map, flag) |
|
33 return map |
|
34 |
|
35 def readflags(self, node): |
|
36 if node == nullid: return {} # don't upset local cache |
|
37 if not self.mapcache or self.mapcache[0] != node: |
|
38 self.read(node) |
|
39 return self.mapcache[2] |
|
40 |
|
41 def diff(self, a, b): |
|
42 # this is sneaky, as we're not actually using a and b |
|
43 if self.listcache and self.addlist and self.listcache[0] == a: |
|
44 d = mdiff.diff(self.listcache[1], self.addlist, 1) |
|
45 if mdiff.patch(a, d) != b: |
|
46 sys.stderr.write("*** sortdiff failed, falling back ***\n") |
|
47 return mdiff.textdiff(a, b) |
|
48 return d |
|
49 else: |
|
50 return mdiff.textdiff(a, b) |
|
51 |
|
52 def add(self, map, flags, transaction, link, p1=None, p2=None, |
|
53 changed=None): |
|
54 # directly generate the mdiff delta from the data collected during |
|
55 # the bisect loop below |
|
56 def gendelta(delta): |
|
57 i = 0 |
|
58 result = [] |
|
59 while i < len(delta): |
|
60 start = delta[i][2] |
|
61 end = delta[i][3] |
|
62 l = delta[i][4] |
|
63 if l == None: |
|
64 l = "" |
|
65 while i < len(delta) - 1 and start <= delta[i+1][2] \ |
|
66 and end >= delta[i+1][2]: |
|
67 if delta[i+1][3] > end: |
|
68 end = delta[i+1][3] |
|
69 if delta[i+1][4]: |
|
70 l += delta[i+1][4] |
|
71 i += 1 |
|
72 result.append(struct.pack(">lll", start, end, len(l)) + l) |
|
73 i += 1 |
|
74 return result |
|
75 |
|
76 # apply the changes collected during the bisect loop to our addlist |
|
77 def addlistdelta(addlist, delta): |
|
78 # apply the deltas to the addlist. start from the bottom up |
|
79 # so changes to the offsets don't mess things up. |
|
80 i = len(delta) |
|
81 while i > 0: |
|
82 i -= 1 |
|
83 start = delta[i][0] |
|
84 end = delta[i][1] |
|
85 if delta[i][4]: |
|
86 addlist[start:end] = [delta[i][4]] |
|
87 else: |
|
88 del addlist[start:end] |
|
89 return addlist |
|
90 |
|
91 # calculate the byte offset of the start of each line in the |
|
92 # manifest |
|
93 def calcoffsets(addlist): |
|
94 offsets = [0] * (len(addlist) + 1) |
|
95 offset = 0 |
|
96 i = 0 |
|
97 while i < len(addlist): |
|
98 offsets[i] = offset |
|
99 offset += len(addlist[i]) |
|
100 i += 1 |
|
101 offsets[i] = offset |
|
102 return offsets |
|
103 |
|
104 # if we're using the listcache, make sure it is valid and |
|
105 # parented by the same node we're diffing against |
|
106 if not changed or not self.listcache or not p1 or \ |
|
107 self.mapcache[0] != p1: |
|
108 files = map.keys() |
|
109 files.sort() |
|
110 |
|
111 self.addlist = ["%s\000%s%s\n" % |
|
112 (f, hex(map[f]), flags[f] and "x" or '') |
|
113 for f in files] |
|
114 cachedelta = None |
|
115 else: |
|
116 addlist = self.listcache[1] |
|
117 |
|
118 # find the starting offset for each line in the add list |
|
119 offsets = calcoffsets(addlist) |
|
120 |
|
121 # combine the changed lists into one list for sorting |
|
122 work = [[x, 0] for x in changed[0]] |
|
123 work[len(work):] = [[x, 1] for x in changed[1]] |
|
124 work.sort() |
|
125 |
|
126 delta = [] |
|
127 bs = 0 |
|
128 |
|
129 for w in work: |
|
130 f = w[0] |
|
131 # bs will either be the index of the item or the insert point |
|
132 bs = bisect.bisect(addlist, f, bs) |
|
133 if bs < len(addlist): |
|
134 fn = addlist[bs][:addlist[bs].index('\0')] |
|
135 else: |
|
136 fn = None |
|
137 if w[1] == 0: |
|
138 l = "%s\000%s%s\n" % (f, hex(map[f]), |
|
139 flags[f] and "x" or '') |
|
140 else: |
|
141 l = None |
|
142 start = bs |
|
143 if fn != f: |
|
144 # item not found, insert a new one |
|
145 end = bs |
|
146 if w[1] == 1: |
|
147 sys.stderr.write("failed to remove %s from manifest\n" |
|
148 % f) |
|
149 sys.exit(1) |
|
150 else: |
|
151 # item is found, replace/delete the existing line |
|
152 end = bs + 1 |
|
153 delta.append([start, end, offsets[start], offsets[end], l]) |
|
154 |
|
155 self.addlist = addlistdelta(addlist, delta) |
|
156 if self.mapcache[0] == self.tip(): |
|
157 cachedelta = "".join(gendelta(delta)) |
|
158 else: |
|
159 cachedelta = None |
|
160 |
|
161 text = "".join(self.addlist) |
|
162 if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text: |
|
163 sys.stderr.write("manifest delta failure\n") |
|
164 sys.exit(1) |
|
165 n = self.addrevision(text, transaction, link, p1, p2, cachedelta) |
|
166 self.mapcache = (n, map, flags) |
|
167 self.listcache = (text, self.addlist) |
|
168 self.addlist = None |
|
169 |
|
170 return n |