##// END OF EJS Templates
filelog: raise CensoredNodeError when hash checks fail with censor metadata...
Mike Edgar -
r22596:27e2317e default
parent child Browse files
Show More
@@ -1,90 +1,102 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import revlog
8 import error, revlog
9 import re
9 import re
10
10
11 _mdre = re.compile('\1\n')
11 _mdre = re.compile('\1\n')
12 def parsemeta(text):
12 def parsemeta(text):
13 """return (metadatadict, keylist, metadatasize)"""
13 """return (metadatadict, keylist, metadatasize)"""
14 # text can be buffer, so we can't use .startswith or .index
14 # text can be buffer, so we can't use .startswith or .index
15 if text[:2] != '\1\n':
15 if text[:2] != '\1\n':
16 return None, None
16 return None, None
17 s = _mdre.search(text, 2).start()
17 s = _mdre.search(text, 2).start()
18 mtext = text[2:s]
18 mtext = text[2:s]
19 meta = {}
19 meta = {}
20 for l in mtext.splitlines():
20 for l in mtext.splitlines():
21 k, v = l.split(": ", 1)
21 k, v = l.split(": ", 1)
22 meta[k] = v
22 meta[k] = v
23 return meta, (s + 2)
23 return meta, (s + 2)
24
24
25 def packmeta(meta, text):
25 def packmeta(meta, text):
26 keys = sorted(meta.iterkeys())
26 keys = sorted(meta.iterkeys())
27 metatext = "".join("%s: %s\n" % (k, meta[k]) for k in keys)
27 metatext = "".join("%s: %s\n" % (k, meta[k]) for k in keys)
28 return "\1\n%s\1\n%s" % (metatext, text)
28 return "\1\n%s\1\n%s" % (metatext, text)
29
29
30 def _censoredtext(text):
31 m, offs = parsemeta(text)
32 return m and "censored" in m and not text[offs:]
33
30 class filelog(revlog.revlog):
34 class filelog(revlog.revlog):
31 def __init__(self, opener, path):
35 def __init__(self, opener, path):
32 super(filelog, self).__init__(opener,
36 super(filelog, self).__init__(opener,
33 "/".join(("data", path + ".i")))
37 "/".join(("data", path + ".i")))
34
38
35 def read(self, node):
39 def read(self, node):
36 t = self.revision(node)
40 t = self.revision(node)
37 if not t.startswith('\1\n'):
41 if not t.startswith('\1\n'):
38 return t
42 return t
39 s = t.index('\1\n', 2)
43 s = t.index('\1\n', 2)
40 return t[s + 2:]
44 return t[s + 2:]
41
45
42 def add(self, text, meta, transaction, link, p1=None, p2=None):
46 def add(self, text, meta, transaction, link, p1=None, p2=None):
43 if meta or text.startswith('\1\n'):
47 if meta or text.startswith('\1\n'):
44 text = packmeta(meta, text)
48 text = packmeta(meta, text)
45 return self.addrevision(text, transaction, link, p1, p2)
49 return self.addrevision(text, transaction, link, p1, p2)
46
50
47 def renamed(self, node):
51 def renamed(self, node):
48 if self.parents(node)[0] != revlog.nullid:
52 if self.parents(node)[0] != revlog.nullid:
49 return False
53 return False
50 t = self.revision(node)
54 t = self.revision(node)
51 m = parsemeta(t)[0]
55 m = parsemeta(t)[0]
52 if m and "copy" in m:
56 if m and "copy" in m:
53 return (m["copy"], revlog.bin(m["copyrev"]))
57 return (m["copy"], revlog.bin(m["copyrev"]))
54 return False
58 return False
55
59
56 def size(self, rev):
60 def size(self, rev):
57 """return the size of a given revision"""
61 """return the size of a given revision"""
58
62
59 # for revisions with renames, we have to go the slow way
63 # for revisions with renames, we have to go the slow way
60 node = self.node(rev)
64 node = self.node(rev)
61 if self.renamed(node):
65 if self.renamed(node):
62 return len(self.read(node))
66 return len(self.read(node))
63
67
64 # XXX if self.read(node).startswith("\1\n"), this returns (size+4)
68 # XXX if self.read(node).startswith("\1\n"), this returns (size+4)
65 return super(filelog, self).size(rev)
69 return super(filelog, self).size(rev)
66
70
67 def cmp(self, node, text):
71 def cmp(self, node, text):
68 """compare text with a given file revision
72 """compare text with a given file revision
69
73
70 returns True if text is different than what is stored.
74 returns True if text is different than what is stored.
71 """
75 """
72
76
73 t = text
77 t = text
74 if text.startswith('\1\n'):
78 if text.startswith('\1\n'):
75 t = '\1\n\1\n' + text
79 t = '\1\n\1\n' + text
76
80
77 samehashes = not super(filelog, self).cmp(node, t)
81 samehashes = not super(filelog, self).cmp(node, t)
78 if samehashes:
82 if samehashes:
79 return False
83 return False
80
84
81 # renaming a file produces a different hash, even if the data
85 # renaming a file produces a different hash, even if the data
82 # remains unchanged. Check if it's the case (slow):
86 # remains unchanged. Check if it's the case (slow):
83 if self.renamed(node):
87 if self.renamed(node):
84 t2 = self.read(node)
88 t2 = self.read(node)
85 return t2 != text
89 return t2 != text
86
90
87 return True
91 return True
88
92
93 def checkhash(self, text, p1, p2, node, rev=None):
94 try:
95 super(filelog, self).checkhash(text, p1, p2, node, rev=rev)
96 except error.RevlogError:
97 if _censoredtext(text):
98 raise error.CensoredNodeError(self.indexfile, node)
99 raise
100
89 def _file(self, f):
101 def _file(self, f):
90 return filelog(self.opener, f)
102 return filelog(self.opener, f)
General Comments 0
You need to be logged in to leave comments. Login now