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