Mercurial > hg
comparison mercurial/manifest.py @ 1089:142b5d5ec9cc
Break apart hg.py
- move the various parts of hg.py into their own files
- create node.py to store node manipulation functions
author | mpm@selenic.com |
---|---|
date | Sat, 27 Aug 2005 14:21:25 -0700 |
parents | mercurial/hg.py@05dc7aba22eb |
children | 50a0a36dd48a |
comparison
equal
deleted
inserted
replaced
1088:39b916b1d8e4 | 1089:142b5d5ec9cc |
---|---|
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 |