##// END OF EJS Templates
filelog: use absolute_import
Gregory Szorc -
r25948:34bd1a5e default
parent child Browse files
Show More
@@ -1,129 +1,137
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 error, mdiff, revlog
8 from __future__ import absolute_import
9 import re, struct
9
10 import re
11 import struct
12
13 from . import (
14 error,
15 mdiff,
16 revlog,
17 )
10
18
11 _mdre = re.compile('\1\n')
19 _mdre = re.compile('\1\n')
12 def parsemeta(text):
20 def parsemeta(text):
13 """return (metadatadict, keylist, metadatasize)"""
21 """return (metadatadict, keylist, metadatasize)"""
14 # text can be buffer, so we can't use .startswith or .index
22 # text can be buffer, so we can't use .startswith or .index
15 if text[:2] != '\1\n':
23 if text[:2] != '\1\n':
16 return None, None
24 return None, None
17 s = _mdre.search(text, 2).start()
25 s = _mdre.search(text, 2).start()
18 mtext = text[2:s]
26 mtext = text[2:s]
19 meta = {}
27 meta = {}
20 for l in mtext.splitlines():
28 for l in mtext.splitlines():
21 k, v = l.split(": ", 1)
29 k, v = l.split(": ", 1)
22 meta[k] = v
30 meta[k] = v
23 return meta, (s + 2)
31 return meta, (s + 2)
24
32
25 def packmeta(meta, text):
33 def packmeta(meta, text):
26 keys = sorted(meta.iterkeys())
34 keys = sorted(meta.iterkeys())
27 metatext = "".join("%s: %s\n" % (k, meta[k]) for k in keys)
35 metatext = "".join("%s: %s\n" % (k, meta[k]) for k in keys)
28 return "\1\n%s\1\n%s" % (metatext, text)
36 return "\1\n%s\1\n%s" % (metatext, text)
29
37
30 def _censoredtext(text):
38 def _censoredtext(text):
31 m, offs = parsemeta(text)
39 m, offs = parsemeta(text)
32 return m and "censored" in m
40 return m and "censored" in m
33
41
34 class filelog(revlog.revlog):
42 class filelog(revlog.revlog):
35 def __init__(self, opener, path):
43 def __init__(self, opener, path):
36 super(filelog, self).__init__(opener,
44 super(filelog, self).__init__(opener,
37 "/".join(("data", path + ".i")))
45 "/".join(("data", path + ".i")))
38
46
39 def read(self, node):
47 def read(self, node):
40 t = self.revision(node)
48 t = self.revision(node)
41 if not t.startswith('\1\n'):
49 if not t.startswith('\1\n'):
42 return t
50 return t
43 s = t.index('\1\n', 2)
51 s = t.index('\1\n', 2)
44 return t[s + 2:]
52 return t[s + 2:]
45
53
46 def add(self, text, meta, transaction, link, p1=None, p2=None):
54 def add(self, text, meta, transaction, link, p1=None, p2=None):
47 if meta or text.startswith('\1\n'):
55 if meta or text.startswith('\1\n'):
48 text = packmeta(meta, text)
56 text = packmeta(meta, text)
49 return self.addrevision(text, transaction, link, p1, p2)
57 return self.addrevision(text, transaction, link, p1, p2)
50
58
51 def renamed(self, node):
59 def renamed(self, node):
52 if self.parents(node)[0] != revlog.nullid:
60 if self.parents(node)[0] != revlog.nullid:
53 return False
61 return False
54 t = self.revision(node)
62 t = self.revision(node)
55 m = parsemeta(t)[0]
63 m = parsemeta(t)[0]
56 if m and "copy" in m:
64 if m and "copy" in m:
57 return (m["copy"], revlog.bin(m["copyrev"]))
65 return (m["copy"], revlog.bin(m["copyrev"]))
58 return False
66 return False
59
67
60 def size(self, rev):
68 def size(self, rev):
61 """return the size of a given revision"""
69 """return the size of a given revision"""
62
70
63 # for revisions with renames, we have to go the slow way
71 # for revisions with renames, we have to go the slow way
64 node = self.node(rev)
72 node = self.node(rev)
65 if self.renamed(node):
73 if self.renamed(node):
66 return len(self.read(node))
74 return len(self.read(node))
67 if self.iscensored(rev):
75 if self.iscensored(rev):
68 return 0
76 return 0
69
77
70 # XXX if self.read(node).startswith("\1\n"), this returns (size+4)
78 # XXX if self.read(node).startswith("\1\n"), this returns (size+4)
71 return super(filelog, self).size(rev)
79 return super(filelog, self).size(rev)
72
80
73 def cmp(self, node, text):
81 def cmp(self, node, text):
74 """compare text with a given file revision
82 """compare text with a given file revision
75
83
76 returns True if text is different than what is stored.
84 returns True if text is different than what is stored.
77 """
85 """
78
86
79 t = text
87 t = text
80 if text.startswith('\1\n'):
88 if text.startswith('\1\n'):
81 t = '\1\n\1\n' + text
89 t = '\1\n\1\n' + text
82
90
83 samehashes = not super(filelog, self).cmp(node, t)
91 samehashes = not super(filelog, self).cmp(node, t)
84 if samehashes:
92 if samehashes:
85 return False
93 return False
86
94
87 # censored files compare against the empty file
95 # censored files compare against the empty file
88 if self.iscensored(self.rev(node)):
96 if self.iscensored(self.rev(node)):
89 return text != ''
97 return text != ''
90
98
91 # renaming a file produces a different hash, even if the data
99 # renaming a file produces a different hash, even if the data
92 # remains unchanged. Check if it's the case (slow):
100 # remains unchanged. Check if it's the case (slow):
93 if self.renamed(node):
101 if self.renamed(node):
94 t2 = self.read(node)
102 t2 = self.read(node)
95 return t2 != text
103 return t2 != text
96
104
97 return True
105 return True
98
106
99 def checkhash(self, text, p1, p2, node, rev=None):
107 def checkhash(self, text, p1, p2, node, rev=None):
100 try:
108 try:
101 super(filelog, self).checkhash(text, p1, p2, node, rev=rev)
109 super(filelog, self).checkhash(text, p1, p2, node, rev=rev)
102 except error.RevlogError:
110 except error.RevlogError:
103 if _censoredtext(text):
111 if _censoredtext(text):
104 raise error.CensoredNodeError(self.indexfile, node, text)
112 raise error.CensoredNodeError(self.indexfile, node, text)
105 raise
113 raise
106
114
107 def iscensored(self, rev):
115 def iscensored(self, rev):
108 """Check if a file revision is censored."""
116 """Check if a file revision is censored."""
109 return self.flags(rev) & revlog.REVIDX_ISCENSORED
117 return self.flags(rev) & revlog.REVIDX_ISCENSORED
110
118
111 def _peek_iscensored(self, baserev, delta, flush):
119 def _peek_iscensored(self, baserev, delta, flush):
112 """Quickly check if a delta produces a censored revision."""
120 """Quickly check if a delta produces a censored revision."""
113 # Fragile heuristic: unless new file meta keys are added alphabetically
121 # Fragile heuristic: unless new file meta keys are added alphabetically
114 # preceding "censored", all censored revisions are prefixed by
122 # preceding "censored", all censored revisions are prefixed by
115 # "\1\ncensored:". A delta producing such a censored revision must be a
123 # "\1\ncensored:". A delta producing such a censored revision must be a
116 # full-replacement delta, so we inspect the first and only patch in the
124 # full-replacement delta, so we inspect the first and only patch in the
117 # delta for this prefix.
125 # delta for this prefix.
118 hlen = struct.calcsize(">lll")
126 hlen = struct.calcsize(">lll")
119 if len(delta) <= hlen:
127 if len(delta) <= hlen:
120 return False
128 return False
121
129
122 oldlen = self.rawsize(baserev)
130 oldlen = self.rawsize(baserev)
123 newlen = len(delta) - hlen
131 newlen = len(delta) - hlen
124 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
132 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
125 return False
133 return False
126
134
127 add = "\1\ncensored:"
135 add = "\1\ncensored:"
128 addlen = len(add)
136 addlen = len(add)
129 return newlen >= addlen and delta[hlen:hlen + addlen] == add
137 return newlen >= addlen and delta[hlen:hlen + addlen] == add
General Comments 0
You need to be logged in to leave comments. Login now