##// END OF EJS Templates
revlog: kill from-style imports...
Matt Mackall -
r7634:14a4337a default
parent child Browse files
Show More
@@ -1,197 +1,196 b''
1 1 # changelog.py - changelog class for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import bin, hex, nullid
9 from revlog import revlog, RevlogError
10 9 from i18n import _
11 import util, error
10 import util, error, revlog
12 11
13 12 def _string_escape(text):
14 13 """
15 14 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
16 15 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
17 16 >>> s
18 17 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
19 18 >>> res = _string_escape(s)
20 19 >>> s == res.decode('string_escape')
21 20 True
22 21 """
23 22 # subset of the string_escape codec
24 23 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
25 24 return text.replace('\0', '\\0')
26 25
27 26 class appender:
28 27 '''the changelog index must be update last on disk, so we use this class
29 28 to delay writes to it'''
30 29 def __init__(self, fp, buf):
31 30 self.data = buf
32 31 self.fp = fp
33 32 self.offset = fp.tell()
34 33 self.size = util.fstat(fp).st_size
35 34
36 35 def end(self):
37 36 return self.size + len("".join(self.data))
38 37 def tell(self):
39 38 return self.offset
40 39 def flush(self):
41 40 pass
42 41 def close(self):
43 42 self.fp.close()
44 43
45 44 def seek(self, offset, whence=0):
46 45 '''virtual file offset spans real file and data'''
47 46 if whence == 0:
48 47 self.offset = offset
49 48 elif whence == 1:
50 49 self.offset += offset
51 50 elif whence == 2:
52 51 self.offset = self.end() + offset
53 52 if self.offset < self.size:
54 53 self.fp.seek(self.offset)
55 54
56 55 def read(self, count=-1):
57 56 '''only trick here is reads that span real file and data'''
58 57 ret = ""
59 58 if self.offset < self.size:
60 59 s = self.fp.read(count)
61 60 ret = s
62 61 self.offset += len(s)
63 62 if count > 0:
64 63 count -= len(s)
65 64 if count != 0:
66 65 doff = self.offset - self.size
67 66 self.data.insert(0, "".join(self.data))
68 67 del self.data[1:]
69 68 s = self.data[0][doff:doff+count]
70 69 self.offset += len(s)
71 70 ret += s
72 71 return ret
73 72
74 73 def write(self, s):
75 74 self.data.append(str(s))
76 75 self.offset += len(s)
77 76
78 class changelog(revlog):
77 class changelog(revlog.revlog):
79 78 def __init__(self, opener):
80 revlog.__init__(self, opener, "00changelog.i")
79 revlog.revlog.__init__(self, opener, "00changelog.i")
81 80
82 81 def delayupdate(self):
83 82 "delay visibility of index updates to other readers"
84 83 self._realopener = self.opener
85 84 self.opener = self._delayopener
86 85 self._delaycount = len(self)
87 86 self._delaybuf = []
88 87 self._delayname = None
89 88
90 89 def finalize(self, tr):
91 90 "finalize index updates"
92 91 self.opener = self._realopener
93 92 # move redirected index data back into place
94 93 if self._delayname:
95 94 util.rename(self._delayname + ".a", self._delayname)
96 95 elif self._delaybuf:
97 96 fp = self.opener(self.indexfile, 'a')
98 97 fp.write("".join(self._delaybuf))
99 98 fp.close()
100 99 del self._delaybuf
101 100 # split when we're done
102 101 self.checkinlinesize(tr)
103 102
104 103 def _delayopener(self, name, mode='r'):
105 104 fp = self._realopener(name, mode)
106 105 # only divert the index
107 106 if not name == self.indexfile:
108 107 return fp
109 108 # if we're doing an initial clone, divert to another file
110 109 if self._delaycount == 0:
111 110 self._delayname = fp.name
112 111 if not len(self):
113 112 # make sure to truncate the file
114 113 mode = mode.replace('a', 'w')
115 114 return self._realopener(name + ".a", mode)
116 115 # otherwise, divert to memory
117 116 return appender(fp, self._delaybuf)
118 117
119 118 def checkinlinesize(self, tr, fp=None):
120 119 if self.opener == self._delayopener:
121 120 return
122 return revlog.checkinlinesize(self, tr, fp)
121 return revlog.revlog.checkinlinesize(self, tr, fp)
123 122
124 123 def decode_extra(self, text):
125 124 extra = {}
126 125 for l in text.split('\0'):
127 126 if l:
128 127 k, v = l.decode('string_escape').split(':', 1)
129 128 extra[k] = v
130 129 return extra
131 130
132 131 def encode_extra(self, d):
133 132 # keys must be sorted to produce a deterministic changelog entry
134 133 items = [_string_escape('%s:%s' % (k, d[k])) for k in util.sort(d)]
135 134 return "\0".join(items)
136 135
137 136 def read(self, node):
138 137 """
139 138 format used:
140 139 nodeid\n : manifest node in ascii
141 140 user\n : user, no \n or \r allowed
142 141 time tz extra\n : date (time is int or float, timezone is int)
143 142 : extra is metadatas, encoded and separated by '\0'
144 143 : older versions ignore it
145 144 files\n\n : files modified by the cset, no \n or \r allowed
146 145 (.*) : comment (free text, ideally utf-8)
147 146
148 147 changelog v0 doesn't use extra
149 148 """
150 149 text = self.revision(node)
151 150 if not text:
152 151 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
153 152 last = text.index("\n\n")
154 153 desc = util.tolocal(text[last + 2:])
155 154 l = text[:last].split('\n')
156 155 manifest = bin(l[0])
157 156 user = util.tolocal(l[1])
158 157
159 158 extra_data = l[2].split(' ', 2)
160 159 if len(extra_data) != 3:
161 160 time = float(extra_data.pop(0))
162 161 try:
163 162 # various tools did silly things with the time zone field.
164 163 timezone = int(extra_data[0])
165 164 except:
166 165 timezone = 0
167 166 extra = {}
168 167 else:
169 168 time, timezone, extra = extra_data
170 169 time, timezone = float(time), int(timezone)
171 170 extra = self.decode_extra(extra)
172 171 if not extra.get('branch'):
173 172 extra['branch'] = 'default'
174 173 files = l[3:]
175 174 return (manifest, user, (time, timezone), files, desc, extra)
176 175
177 176 def add(self, manifest, files, desc, transaction, p1=None, p2=None,
178 177 user=None, date=None, extra={}):
179 178
180 179 user = user.strip()
181 180 if "\n" in user:
182 181 raise error.RevlogError(_("username %s contains a newline")
183 182 % repr(user))
184 183 user, desc = util.fromlocal(user), util.fromlocal(desc)
185 184
186 185 if date:
187 186 parseddate = "%d %d" % util.parsedate(date)
188 187 else:
189 188 parseddate = "%d %d" % util.makedate()
190 189 if extra and extra.get("branch") in ("default", ""):
191 190 del extra["branch"]
192 191 if extra:
193 192 extra = self.encode_extra(extra)
194 193 parseddate = "%s %s" % (parseddate, extra)
195 194 l = [hex(manifest), user, parseddate] + util.sort(files) + ["", desc]
196 195 text = "\n".join(l)
197 196 return self.addrevision(text, transaction, len(self), p1, p2)
@@ -1,83 +1,82 b''
1 1 # filelog.py - file history class for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 from node import bin, nullid
9 from revlog import revlog
8 import revlog
10 9
11 class filelog(revlog):
10 class filelog(revlog.revlog):
12 11 def __init__(self, opener, path):
13 revlog.__init__(self, opener,
12 revlog.revlog.__init__(self, opener,
14 13 "/".join(("data", self.encodedir(path + ".i"))))
15 14
16 15 # This avoids a collision between a file named foo and a dir named
17 16 # foo.i or foo.d
18 17 def encodedir(self, path):
19 18 return (path
20 19 .replace(".hg/", ".hg.hg/")
21 20 .replace(".i/", ".i.hg/")
22 21 .replace(".d/", ".d.hg/"))
23 22
24 23 def decodedir(self, path):
25 24 return (path
26 25 .replace(".d.hg/", ".d/")
27 26 .replace(".i.hg/", ".i/")
28 27 .replace(".hg.hg/", ".hg/"))
29 28
30 29 def read(self, node):
31 30 t = self.revision(node)
32 31 if not t.startswith('\1\n'):
33 32 return t
34 33 s = t.index('\1\n', 2)
35 34 return t[s+2:]
36 35
37 36 def _readmeta(self, node):
38 37 t = self.revision(node)
39 38 if not t.startswith('\1\n'):
40 39 return {}
41 40 s = t.index('\1\n', 2)
42 41 mt = t[2:s]
43 42 m = {}
44 43 for l in mt.splitlines():
45 44 k, v = l.split(": ", 1)
46 45 m[k] = v
47 46 return m
48 47
49 48 def add(self, text, meta, transaction, link, p1=None, p2=None):
50 49 if meta or text.startswith('\1\n'):
51 50 mt = ""
52 51 if meta:
53 52 mt = ["%s: %s\n" % (k, v) for k, v in meta.iteritems()]
54 53 text = "\1\n%s\1\n%s" % ("".join(mt), text)
55 54 return self.addrevision(text, transaction, link, p1, p2)
56 55
57 56 def renamed(self, node):
58 if self.parents(node)[0] != nullid:
57 if self.parents(node)[0] != revlog.nullid:
59 58 return False
60 59 m = self._readmeta(node)
61 60 if m and "copy" in m:
62 return (m["copy"], bin(m["copyrev"]))
61 return (m["copy"], revlog.bin(m["copyrev"]))
63 62 return False
64 63
65 64 def size(self, rev):
66 65 """return the size of a given revision"""
67 66
68 67 # for revisions with renames, we have to go the slow way
69 68 node = self.node(rev)
70 69 if self.renamed(node):
71 70 return len(self.read(node))
72 71
73 return revlog.size(self, rev)
72 return revlog.revlog.size(self, rev)
74 73
75 74 def cmp(self, node, text):
76 75 """compare text with a given file revision"""
77 76
78 77 # for renames, we have to go the slow way
79 78 if self.renamed(node):
80 79 t2 = self.read(node)
81 80 return t2 != text
82 81
83 return revlog.cmp(self, node, text)
82 return revlog.revlog.cmp(self, node, text)
@@ -1,200 +1,200 b''
1 1 # manifest.py - manifest revision class for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 from node import bin, hex, nullid
9 from revlog import revlog
10 8 from i18n import _
11 import array, struct, mdiff, parsers, util, error
9 import array, struct, mdiff, parsers, util, error, revlog
12 10
13 11 class manifestdict(dict):
14 12 def __init__(self, mapping=None, flags=None):
15 13 if mapping is None: mapping = {}
16 14 if flags is None: flags = {}
17 15 dict.__init__(self, mapping)
18 16 self._flags = flags
19 17 def flags(self, f):
20 18 return self._flags.get(f, "")
21 19 def set(self, f, flags):
22 20 self._flags[f] = flags
23 21 def copy(self):
24 22 return manifestdict(dict.copy(self), dict.copy(self._flags))
25 23
26 class manifest(revlog):
24 class manifest(revlog.revlog):
27 25 def __init__(self, opener):
28 26 self.mapcache = None
29 27 self.listcache = None
30 revlog.__init__(self, opener, "00manifest.i")
28 revlog.revlog.__init__(self, opener, "00manifest.i")
31 29
32 30 def parse(self, lines):
33 31 mfdict = manifestdict()
34 32 parsers.parse_manifest(mfdict, mfdict._flags, lines)
35 33 return mfdict
36 34
37 35 def readdelta(self, node):
38 36 r = self.rev(node)
39 37 return self.parse(mdiff.patchtext(self.revdiff(r - 1, r)))
40 38
41 39 def read(self, node):
42 if node == nullid: return manifestdict() # don't upset local cache
40 if node == revlog.nullid:
41 return manifestdict() # don't upset local cache
43 42 if self.mapcache and self.mapcache[0] == node:
44 43 return self.mapcache[1]
45 44 text = self.revision(node)
46 45 self.listcache = array.array('c', text)
47 46 mapping = self.parse(text)
48 47 self.mapcache = (node, mapping)
49 48 return mapping
50 49
51 50 def _search(self, m, s, lo=0, hi=None):
52 51 '''return a tuple (start, end) that says where to find s within m.
53 52
54 53 If the string is found m[start:end] are the line containing
55 54 that string. If start == end the string was not found and
56 55 they indicate the proper sorted insertion point. This was
57 56 taken from bisect_left, and modified to find line start/end as
58 57 it goes along.
59 58
60 59 m should be a buffer or a string
61 60 s is a string'''
62 61 def advance(i, c):
63 62 while i < lenm and m[i] != c:
64 63 i += 1
65 64 return i
66 65 if not s:
67 66 return (lo, lo)
68 67 lenm = len(m)
69 68 if not hi:
70 69 hi = lenm
71 70 while lo < hi:
72 71 mid = (lo + hi) // 2
73 72 start = mid
74 73 while start > 0 and m[start-1] != '\n':
75 74 start -= 1
76 75 end = advance(start, '\0')
77 76 if m[start:end] < s:
78 77 # we know that after the null there are 40 bytes of sha1
79 78 # this translates to the bisect lo = mid + 1
80 79 lo = advance(end + 40, '\n') + 1
81 80 else:
82 81 # this translates to the bisect hi = mid
83 82 hi = start
84 83 end = advance(lo, '\0')
85 84 found = m[lo:end]
86 85 if cmp(s, found) == 0:
87 86 # we know that after the null there are 40 bytes of sha1
88 87 end = advance(end + 40, '\n')
89 88 return (lo, end+1)
90 89 else:
91 90 return (lo, lo)
92 91
93 92 def find(self, node, f):
94 93 '''look up entry for a single file efficiently.
95 94 return (node, flags) pair if found, (None, None) if not.'''
96 95 if self.mapcache and node == self.mapcache[0]:
97 96 return self.mapcache[1].get(f), self.mapcache[1].flags(f)
98 97 text = self.revision(node)
99 98 start, end = self._search(text, f)
100 99 if start == end:
101 100 return None, None
102 101 l = text[start:end]
103 102 f, n = l.split('\0')
104 return bin(n[:40]), n[40:-1]
103 return revlog.bin(n[:40]), n[40:-1]
105 104
106 105 def add(self, map, transaction, link, p1=None, p2=None,
107 106 changed=None):
108 107 # apply the changes collected during the bisect loop to our addlist
109 108 # return a delta suitable for addrevision
110 109 def addlistdelta(addlist, x):
111 110 # start from the bottom up
112 111 # so changes to the offsets don't mess things up.
113 112 i = len(x)
114 113 while i > 0:
115 114 i -= 1
116 115 start = x[i][0]
117 116 end = x[i][1]
118 117 if x[i][2]:
119 118 addlist[start:end] = array.array('c', x[i][2])
120 119 else:
121 120 del addlist[start:end]
122 121 return "".join([struct.pack(">lll", d[0], d[1], len(d[2])) + d[2]
123 122 for d in x ])
124 123
125 124 def checkforbidden(l):
126 125 for f in l:
127 126 if '\n' in f or '\r' in f:
128 127 raise error.RevlogError(
129 128 _("'\\n' and '\\r' disallowed in filenames"))
130 129
131 130 # if we're using the listcache, make sure it is valid and
132 131 # parented by the same node we're diffing against
133 132 if not (changed and self.listcache and p1 and self.mapcache[0] == p1):
134 133 files = util.sort(map)
135 134 checkforbidden(files)
136 135
137 136 # if this is changed to support newlines in filenames,
138 137 # be sure to check the templates/ dir again (especially *-raw.tmpl)
139 text = ["%s\000%s%s\n" % (f, hex(map[f]), map.flags(f))
138 hex, flags = revlog.hex, map.flags
139 text = ["%s\000%s%s\n" % (f, hex(map[f]), flags(f))
140 140 for f in files]
141 141 self.listcache = array.array('c', "".join(text))
142 142 cachedelta = None
143 143 else:
144 144 addlist = self.listcache
145 145
146 146 checkforbidden(changed[0])
147 147 # combine the changed lists into one list for sorting
148 148 work = [[x, 0] for x in changed[0]]
149 149 work[len(work):] = [[x, 1] for x in changed[1]]
150 150 work.sort()
151 151
152 152 delta = []
153 153 dstart = None
154 154 dend = None
155 155 dline = [""]
156 156 start = 0
157 157 # zero copy representation of addlist as a buffer
158 158 addbuf = buffer(addlist)
159 159
160 160 # start with a readonly loop that finds the offset of
161 161 # each line and creates the deltas
162 162 for w in work:
163 163 f = w[0]
164 164 # bs will either be the index of the item or the insert point
165 165 start, end = self._search(addbuf, f, start)
166 166 if w[1] == 0:
167 l = "%s\000%s%s\n" % (f, hex(map[f]), map.flags(f))
167 l = "%s\000%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
168 168 else:
169 169 l = ""
170 170 if start == end and w[1] == 1:
171 171 # item we want to delete was not found, error out
172 172 raise AssertionError(
173 173 _("failed to remove %s from manifest") % f)
174 174 if dstart != None and dstart <= start and dend >= start:
175 175 if dend < end:
176 176 dend = end
177 177 if l:
178 178 dline.append(l)
179 179 else:
180 180 if dstart != None:
181 181 delta.append([dstart, dend, "".join(dline)])
182 182 dstart = start
183 183 dend = end
184 184 dline = [l]
185 185
186 186 if dstart != None:
187 187 delta.append([dstart, dend, "".join(dline)])
188 188 # apply the delta to the addlist, and get a delta for addrevision
189 189 cachedelta = addlistdelta(addlist, delta)
190 190
191 191 # the delta is only valid if we've been processing the tip revision
192 192 if self.mapcache[0] != self.tip():
193 193 cachedelta = None
194 194 self.listcache = addlist
195 195
196 196 n = self.addrevision(buffer(self.listcache), transaction, link,
197 197 p1, p2, cachedelta)
198 198 self.mapcache = (n, map)
199 199
200 200 return n
General Comments 0
You need to be logged in to leave comments. Login now