##// END OF EJS Templates
git: convert tz offset to int (issue6359)...
Augie Fackler -
r45973:8ddbb75b default
parent child Browse files
Show More
@@ -1,523 +1,523
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 from mercurial.i18n import _
3 from mercurial.i18n import _
4
4
5 from mercurial import (
5 from mercurial import (
6 ancestor,
6 ancestor,
7 changelog as hgchangelog,
7 changelog as hgchangelog,
8 dagop,
8 dagop,
9 encoding,
9 encoding,
10 error,
10 error,
11 manifest,
11 manifest,
12 node as nodemod,
12 node as nodemod,
13 pycompat,
13 pycompat,
14 )
14 )
15 from mercurial.interfaces import (
15 from mercurial.interfaces import (
16 repository,
16 repository,
17 util as interfaceutil,
17 util as interfaceutil,
18 )
18 )
19 from mercurial.utils import stringutil
19 from mercurial.utils import stringutil
20 from . import (
20 from . import (
21 gitutil,
21 gitutil,
22 index,
22 index,
23 manifest as gitmanifest,
23 manifest as gitmanifest,
24 )
24 )
25
25
26 pygit2 = gitutil.get_pygit2()
26 pygit2 = gitutil.get_pygit2()
27
27
28
28
29 class baselog(object): # revlog.revlog):
29 class baselog(object): # revlog.revlog):
30 """Common implementations between changelog and manifestlog."""
30 """Common implementations between changelog and manifestlog."""
31
31
32 def __init__(self, gr, db):
32 def __init__(self, gr, db):
33 self.gitrepo = gr
33 self.gitrepo = gr
34 self._db = db
34 self._db = db
35
35
36 def __len__(self):
36 def __len__(self):
37 return int(
37 return int(
38 self._db.execute('SELECT COUNT(*) FROM changelog').fetchone()[0]
38 self._db.execute('SELECT COUNT(*) FROM changelog').fetchone()[0]
39 )
39 )
40
40
41 def rev(self, n):
41 def rev(self, n):
42 if n == nodemod.nullid:
42 if n == nodemod.nullid:
43 return -1
43 return -1
44 t = self._db.execute(
44 t = self._db.execute(
45 'SELECT rev FROM changelog WHERE node = ?', (gitutil.togitnode(n),)
45 'SELECT rev FROM changelog WHERE node = ?', (gitutil.togitnode(n),)
46 ).fetchone()
46 ).fetchone()
47 if t is None:
47 if t is None:
48 raise error.LookupError(n, b'00changelog.i', _(b'no node %d'))
48 raise error.LookupError(n, b'00changelog.i', _(b'no node %d'))
49 return t[0]
49 return t[0]
50
50
51 def node(self, r):
51 def node(self, r):
52 if r == nodemod.nullrev:
52 if r == nodemod.nullrev:
53 return nodemod.nullid
53 return nodemod.nullid
54 t = self._db.execute(
54 t = self._db.execute(
55 'SELECT node FROM changelog WHERE rev = ?', (r,)
55 'SELECT node FROM changelog WHERE rev = ?', (r,)
56 ).fetchone()
56 ).fetchone()
57 if t is None:
57 if t is None:
58 raise error.LookupError(r, b'00changelog.i', _(b'no node'))
58 raise error.LookupError(r, b'00changelog.i', _(b'no node'))
59 return nodemod.bin(t[0])
59 return nodemod.bin(t[0])
60
60
61 def hasnode(self, n):
61 def hasnode(self, n):
62 t = self._db.execute(
62 t = self._db.execute(
63 'SELECT node FROM changelog WHERE node = ?', (n,)
63 'SELECT node FROM changelog WHERE node = ?', (n,)
64 ).fetchone()
64 ).fetchone()
65 return t is not None
65 return t is not None
66
66
67
67
68 class baselogindex(object):
68 class baselogindex(object):
69 def __init__(self, log):
69 def __init__(self, log):
70 self._log = log
70 self._log = log
71
71
72 def has_node(self, n):
72 def has_node(self, n):
73 return self._log.rev(n) != -1
73 return self._log.rev(n) != -1
74
74
75 def __len__(self):
75 def __len__(self):
76 return len(self._log)
76 return len(self._log)
77
77
78 def __getitem__(self, idx):
78 def __getitem__(self, idx):
79 p1rev, p2rev = self._log.parentrevs(idx)
79 p1rev, p2rev = self._log.parentrevs(idx)
80 # TODO: it's messy that the index leaks so far out of the
80 # TODO: it's messy that the index leaks so far out of the
81 # storage layer that we have to implement things like reading
81 # storage layer that we have to implement things like reading
82 # this raw tuple, which exposes revlog internals.
82 # this raw tuple, which exposes revlog internals.
83 return (
83 return (
84 # Pretend offset is just the index, since we don't really care.
84 # Pretend offset is just the index, since we don't really care.
85 idx,
85 idx,
86 # Same with lengths
86 # Same with lengths
87 idx, # length
87 idx, # length
88 idx, # rawsize
88 idx, # rawsize
89 -1, # delta base
89 -1, # delta base
90 idx, # linkrev TODO is this right?
90 idx, # linkrev TODO is this right?
91 p1rev,
91 p1rev,
92 p2rev,
92 p2rev,
93 self._log.node(idx),
93 self._log.node(idx),
94 )
94 )
95
95
96
96
97 # TODO: an interface for the changelog type?
97 # TODO: an interface for the changelog type?
98 class changelog(baselog):
98 class changelog(baselog):
99 def __contains__(self, rev):
99 def __contains__(self, rev):
100 try:
100 try:
101 self.node(rev)
101 self.node(rev)
102 return True
102 return True
103 except error.LookupError:
103 except error.LookupError:
104 return False
104 return False
105
105
106 def __iter__(self):
106 def __iter__(self):
107 return iter(pycompat.xrange(len(self)))
107 return iter(pycompat.xrange(len(self)))
108
108
109 @property
109 @property
110 def filteredrevs(self):
110 def filteredrevs(self):
111 # TODO: we should probably add a refs/hg/ namespace for hidden
111 # TODO: we should probably add a refs/hg/ namespace for hidden
112 # heads etc, but that's an idea for later.
112 # heads etc, but that's an idea for later.
113 return set()
113 return set()
114
114
115 @property
115 @property
116 def index(self):
116 def index(self):
117 return baselogindex(self)
117 return baselogindex(self)
118
118
119 @property
119 @property
120 def nodemap(self):
120 def nodemap(self):
121 r = {
121 r = {
122 nodemod.bin(v[0]): v[1]
122 nodemod.bin(v[0]): v[1]
123 for v in self._db.execute('SELECT node, rev FROM changelog')
123 for v in self._db.execute('SELECT node, rev FROM changelog')
124 }
124 }
125 r[nodemod.nullid] = nodemod.nullrev
125 r[nodemod.nullid] = nodemod.nullrev
126 return r
126 return r
127
127
128 def tip(self):
128 def tip(self):
129 t = self._db.execute(
129 t = self._db.execute(
130 'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
130 'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
131 ).fetchone()
131 ).fetchone()
132 if t:
132 if t:
133 return nodemod.bin(t[0])
133 return nodemod.bin(t[0])
134 return nodemod.nullid
134 return nodemod.nullid
135
135
136 def revs(self, start=0, stop=None):
136 def revs(self, start=0, stop=None):
137 if stop is None:
137 if stop is None:
138 stop = self.tip()
138 stop = self.tip()
139 t = self._db.execute(
139 t = self._db.execute(
140 'SELECT rev FROM changelog '
140 'SELECT rev FROM changelog '
141 'WHERE rev >= ? AND rev <= ? '
141 'WHERE rev >= ? AND rev <= ? '
142 'ORDER BY REV ASC',
142 'ORDER BY REV ASC',
143 (start, stop),
143 (start, stop),
144 )
144 )
145 return (int(r[0]) for r in t)
145 return (int(r[0]) for r in t)
146
146
147 def _partialmatch(self, id):
147 def _partialmatch(self, id):
148 if nodemod.wdirhex.startswith(id):
148 if nodemod.wdirhex.startswith(id):
149 raise error.WdirUnsupported
149 raise error.WdirUnsupported
150 candidates = [
150 candidates = [
151 nodemod.bin(x[0])
151 nodemod.bin(x[0])
152 for x in self._db.execute(
152 for x in self._db.execute(
153 'SELECT node FROM changelog WHERE node LIKE ?', (id + b'%',)
153 'SELECT node FROM changelog WHERE node LIKE ?', (id + b'%',)
154 )
154 )
155 ]
155 ]
156 if nodemod.nullhex.startswith(id):
156 if nodemod.nullhex.startswith(id):
157 candidates.append(nodemod.nullid)
157 candidates.append(nodemod.nullid)
158 if len(candidates) > 1:
158 if len(candidates) > 1:
159 raise error.AmbiguousPrefixLookupError(
159 raise error.AmbiguousPrefixLookupError(
160 id, b'00changelog.i', _(b'ambiguous identifier')
160 id, b'00changelog.i', _(b'ambiguous identifier')
161 )
161 )
162 if candidates:
162 if candidates:
163 return candidates[0]
163 return candidates[0]
164 return None
164 return None
165
165
166 def flags(self, rev):
166 def flags(self, rev):
167 return 0
167 return 0
168
168
169 def shortest(self, node, minlength=1):
169 def shortest(self, node, minlength=1):
170 nodehex = nodemod.hex(node)
170 nodehex = nodemod.hex(node)
171 for attempt in pycompat.xrange(minlength, len(nodehex) + 1):
171 for attempt in pycompat.xrange(minlength, len(nodehex) + 1):
172 candidate = nodehex[:attempt]
172 candidate = nodehex[:attempt]
173 matches = int(
173 matches = int(
174 self._db.execute(
174 self._db.execute(
175 'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
175 'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
176 (pycompat.sysstr(candidate + b'%'),),
176 (pycompat.sysstr(candidate + b'%'),),
177 ).fetchone()[0]
177 ).fetchone()[0]
178 )
178 )
179 if matches == 1:
179 if matches == 1:
180 return candidate
180 return candidate
181 return nodehex
181 return nodehex
182
182
183 def headrevs(self, revs=None):
183 def headrevs(self, revs=None):
184 realheads = [
184 realheads = [
185 int(x[0])
185 int(x[0])
186 for x in self._db.execute(
186 for x in self._db.execute(
187 'SELECT rev FROM changelog '
187 'SELECT rev FROM changelog '
188 'INNER JOIN heads ON changelog.node = heads.node'
188 'INNER JOIN heads ON changelog.node = heads.node'
189 )
189 )
190 ]
190 ]
191 if revs:
191 if revs:
192 return sorted([r for r in revs if r in realheads])
192 return sorted([r for r in revs if r in realheads])
193 return sorted(realheads)
193 return sorted(realheads)
194
194
195 def changelogrevision(self, nodeorrev):
195 def changelogrevision(self, nodeorrev):
196 # Ensure we have a node id
196 # Ensure we have a node id
197 if isinstance(nodeorrev, int):
197 if isinstance(nodeorrev, int):
198 n = self.node(nodeorrev)
198 n = self.node(nodeorrev)
199 else:
199 else:
200 n = nodeorrev
200 n = nodeorrev
201 # handle looking up nullid
201 # handle looking up nullid
202 if n == nodemod.nullid:
202 if n == nodemod.nullid:
203 return hgchangelog._changelogrevision(extra={})
203 return hgchangelog._changelogrevision(extra={})
204 hn = gitutil.togitnode(n)
204 hn = gitutil.togitnode(n)
205 # We've got a real commit!
205 # We've got a real commit!
206 files = [
206 files = [
207 r[0]
207 r[0]
208 for r in self._db.execute(
208 for r in self._db.execute(
209 'SELECT filename FROM changedfiles '
209 'SELECT filename FROM changedfiles '
210 'WHERE node = ? and filenode != ?',
210 'WHERE node = ? and filenode != ?',
211 (hn, gitutil.nullgit),
211 (hn, gitutil.nullgit),
212 )
212 )
213 ]
213 ]
214 filesremoved = [
214 filesremoved = [
215 r[0]
215 r[0]
216 for r in self._db.execute(
216 for r in self._db.execute(
217 'SELECT filename FROM changedfiles '
217 'SELECT filename FROM changedfiles '
218 'WHERE node = ? and filenode = ?',
218 'WHERE node = ? and filenode = ?',
219 (hn, nodemod.nullhex),
219 (hn, nodemod.nullhex),
220 )
220 )
221 ]
221 ]
222 c = self.gitrepo[hn]
222 c = self.gitrepo[hn]
223 return hgchangelog._changelogrevision(
223 return hgchangelog._changelogrevision(
224 manifest=n, # pretend manifest the same as the commit node
224 manifest=n, # pretend manifest the same as the commit node
225 user=b'%s <%s>'
225 user=b'%s <%s>'
226 % (c.author.name.encode('utf8'), c.author.email.encode('utf8')),
226 % (c.author.name.encode('utf8'), c.author.email.encode('utf8')),
227 date=(c.author.time, -c.author.offset * 60),
227 date=(c.author.time, -c.author.offset * 60),
228 files=files,
228 files=files,
229 # TODO filesadded in the index
229 # TODO filesadded in the index
230 filesremoved=filesremoved,
230 filesremoved=filesremoved,
231 description=c.message.encode('utf8'),
231 description=c.message.encode('utf8'),
232 # TODO do we want to handle extra? how?
232 # TODO do we want to handle extra? how?
233 extra={b'branch': b'default'},
233 extra={b'branch': b'default'},
234 )
234 )
235
235
236 def ancestors(self, revs, stoprev=0, inclusive=False):
236 def ancestors(self, revs, stoprev=0, inclusive=False):
237 revs = list(revs)
237 revs = list(revs)
238 tip = self.rev(self.tip())
238 tip = self.rev(self.tip())
239 for r in revs:
239 for r in revs:
240 if r > tip:
240 if r > tip:
241 raise IndexError(b'Invalid rev %r' % r)
241 raise IndexError(b'Invalid rev %r' % r)
242 return ancestor.lazyancestors(
242 return ancestor.lazyancestors(
243 self.parentrevs, revs, stoprev=stoprev, inclusive=inclusive
243 self.parentrevs, revs, stoprev=stoprev, inclusive=inclusive
244 )
244 )
245
245
246 # Cleanup opportunity: this is *identical* to the revlog.py version
246 # Cleanup opportunity: this is *identical* to the revlog.py version
247 def descendants(self, revs):
247 def descendants(self, revs):
248 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
248 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
249
249
250 def incrementalmissingrevs(self, common=None):
250 def incrementalmissingrevs(self, common=None):
251 """Return an object that can be used to incrementally compute the
251 """Return an object that can be used to incrementally compute the
252 revision numbers of the ancestors of arbitrary sets that are not
252 revision numbers of the ancestors of arbitrary sets that are not
253 ancestors of common. This is an ancestor.incrementalmissingancestors
253 ancestors of common. This is an ancestor.incrementalmissingancestors
254 object.
254 object.
255
255
256 'common' is a list of revision numbers. If common is not supplied, uses
256 'common' is a list of revision numbers. If common is not supplied, uses
257 nullrev.
257 nullrev.
258 """
258 """
259 if common is None:
259 if common is None:
260 common = [nodemod.nullrev]
260 common = [nodemod.nullrev]
261
261
262 return ancestor.incrementalmissingancestors(self.parentrevs, common)
262 return ancestor.incrementalmissingancestors(self.parentrevs, common)
263
263
264 def findmissing(self, common=None, heads=None):
264 def findmissing(self, common=None, heads=None):
265 """Return the ancestors of heads that are not ancestors of common.
265 """Return the ancestors of heads that are not ancestors of common.
266
266
267 More specifically, return a list of nodes N such that every N
267 More specifically, return a list of nodes N such that every N
268 satisfies the following constraints:
268 satisfies the following constraints:
269
269
270 1. N is an ancestor of some node in 'heads'
270 1. N is an ancestor of some node in 'heads'
271 2. N is not an ancestor of any node in 'common'
271 2. N is not an ancestor of any node in 'common'
272
272
273 The list is sorted by revision number, meaning it is
273 The list is sorted by revision number, meaning it is
274 topologically sorted.
274 topologically sorted.
275
275
276 'heads' and 'common' are both lists of node IDs. If heads is
276 'heads' and 'common' are both lists of node IDs. If heads is
277 not supplied, uses all of the revlog's heads. If common is not
277 not supplied, uses all of the revlog's heads. If common is not
278 supplied, uses nullid."""
278 supplied, uses nullid."""
279 if common is None:
279 if common is None:
280 common = [nodemod.nullid]
280 common = [nodemod.nullid]
281 if heads is None:
281 if heads is None:
282 heads = self.heads()
282 heads = self.heads()
283
283
284 common = [self.rev(n) for n in common]
284 common = [self.rev(n) for n in common]
285 heads = [self.rev(n) for n in heads]
285 heads = [self.rev(n) for n in heads]
286
286
287 inc = self.incrementalmissingrevs(common=common)
287 inc = self.incrementalmissingrevs(common=common)
288 return [self.node(r) for r in inc.missingancestors(heads)]
288 return [self.node(r) for r in inc.missingancestors(heads)]
289
289
290 def children(self, node):
290 def children(self, node):
291 """find the children of a given node"""
291 """find the children of a given node"""
292 c = []
292 c = []
293 p = self.rev(node)
293 p = self.rev(node)
294 for r in self.revs(start=p + 1):
294 for r in self.revs(start=p + 1):
295 prevs = [pr for pr in self.parentrevs(r) if pr != nodemod.nullrev]
295 prevs = [pr for pr in self.parentrevs(r) if pr != nodemod.nullrev]
296 if prevs:
296 if prevs:
297 for pr in prevs:
297 for pr in prevs:
298 if pr == p:
298 if pr == p:
299 c.append(self.node(r))
299 c.append(self.node(r))
300 elif p == nodemod.nullrev:
300 elif p == nodemod.nullrev:
301 c.append(self.node(r))
301 c.append(self.node(r))
302 return c
302 return c
303
303
304 def reachableroots(self, minroot, heads, roots, includepath=False):
304 def reachableroots(self, minroot, heads, roots, includepath=False):
305 return dagop._reachablerootspure(
305 return dagop._reachablerootspure(
306 self.parentrevs, minroot, roots, heads, includepath
306 self.parentrevs, minroot, roots, heads, includepath
307 )
307 )
308
308
309 # Cleanup opportunity: this is *identical* to the revlog.py version
309 # Cleanup opportunity: this is *identical* to the revlog.py version
310 def isancestor(self, a, b):
310 def isancestor(self, a, b):
311 a, b = self.rev(a), self.rev(b)
311 a, b = self.rev(a), self.rev(b)
312 return self.isancestorrev(a, b)
312 return self.isancestorrev(a, b)
313
313
314 # Cleanup opportunity: this is *identical* to the revlog.py version
314 # Cleanup opportunity: this is *identical* to the revlog.py version
315 def isancestorrev(self, a, b):
315 def isancestorrev(self, a, b):
316 if a == nodemod.nullrev:
316 if a == nodemod.nullrev:
317 return True
317 return True
318 elif a == b:
318 elif a == b:
319 return True
319 return True
320 elif a > b:
320 elif a > b:
321 return False
321 return False
322 return bool(self.reachableroots(a, [b], [a], includepath=False))
322 return bool(self.reachableroots(a, [b], [a], includepath=False))
323
323
324 def parentrevs(self, rev):
324 def parentrevs(self, rev):
325 n = self.node(rev)
325 n = self.node(rev)
326 hn = gitutil.togitnode(n)
326 hn = gitutil.togitnode(n)
327 if hn != gitutil.nullgit:
327 if hn != gitutil.nullgit:
328 c = self.gitrepo[hn]
328 c = self.gitrepo[hn]
329 else:
329 else:
330 return nodemod.nullrev, nodemod.nullrev
330 return nodemod.nullrev, nodemod.nullrev
331 p1 = p2 = nodemod.nullrev
331 p1 = p2 = nodemod.nullrev
332 if c.parents:
332 if c.parents:
333 p1 = self.rev(c.parents[0].id.raw)
333 p1 = self.rev(c.parents[0].id.raw)
334 if len(c.parents) > 2:
334 if len(c.parents) > 2:
335 raise error.Abort(b'TODO octopus merge handling')
335 raise error.Abort(b'TODO octopus merge handling')
336 if len(c.parents) == 2:
336 if len(c.parents) == 2:
337 p2 = self.rev(c.parents[1].id.raw)
337 p2 = self.rev(c.parents[1].id.raw)
338 return p1, p2
338 return p1, p2
339
339
340 # Private method is used at least by the tags code.
340 # Private method is used at least by the tags code.
341 _uncheckedparentrevs = parentrevs
341 _uncheckedparentrevs = parentrevs
342
342
343 def commonancestorsheads(self, a, b):
343 def commonancestorsheads(self, a, b):
344 # TODO the revlog verson of this has a C path, so we probably
344 # TODO the revlog verson of this has a C path, so we probably
345 # need to optimize this...
345 # need to optimize this...
346 a, b = self.rev(a), self.rev(b)
346 a, b = self.rev(a), self.rev(b)
347 return [
347 return [
348 self.node(n)
348 self.node(n)
349 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
349 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
350 ]
350 ]
351
351
352 def branchinfo(self, rev):
352 def branchinfo(self, rev):
353 """Git doesn't do named branches, so just put everything on default."""
353 """Git doesn't do named branches, so just put everything on default."""
354 return b'default', False
354 return b'default', False
355
355
356 def delayupdate(self, tr):
356 def delayupdate(self, tr):
357 # TODO: I think we can elide this because we're just dropping
357 # TODO: I think we can elide this because we're just dropping
358 # an object in the git repo?
358 # an object in the git repo?
359 pass
359 pass
360
360
361 def add(
361 def add(
362 self,
362 self,
363 manifest,
363 manifest,
364 files,
364 files,
365 desc,
365 desc,
366 transaction,
366 transaction,
367 p1,
367 p1,
368 p2,
368 p2,
369 user,
369 user,
370 date=None,
370 date=None,
371 extra=None,
371 extra=None,
372 p1copies=None,
372 p1copies=None,
373 p2copies=None,
373 p2copies=None,
374 filesadded=None,
374 filesadded=None,
375 filesremoved=None,
375 filesremoved=None,
376 ):
376 ):
377 parents = []
377 parents = []
378 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
378 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
379 if p1 != nodemod.nullid:
379 if p1 != nodemod.nullid:
380 parents.append(hp1)
380 parents.append(hp1)
381 if p2 and p2 != nodemod.nullid:
381 if p2 and p2 != nodemod.nullid:
382 parents.append(hp2)
382 parents.append(hp2)
383 assert date is not None
383 assert date is not None
384 timestamp, tz = date
384 timestamp, tz = date
385 sig = pygit2.Signature(
385 sig = pygit2.Signature(
386 encoding.unifromlocal(stringutil.person(user)),
386 encoding.unifromlocal(stringutil.person(user)),
387 encoding.unifromlocal(stringutil.email(user)),
387 encoding.unifromlocal(stringutil.email(user)),
388 timestamp,
388 timestamp,
389 -(tz // 60),
389 -int(tz // 60),
390 )
390 )
391 oid = self.gitrepo.create_commit(
391 oid = self.gitrepo.create_commit(
392 None, sig, sig, desc, gitutil.togitnode(manifest), parents
392 None, sig, sig, desc, gitutil.togitnode(manifest), parents
393 )
393 )
394 # Set up an internal reference to force the commit into the
394 # Set up an internal reference to force the commit into the
395 # changelog. Hypothetically, we could even use this refs/hg/
395 # changelog. Hypothetically, we could even use this refs/hg/
396 # namespace to allow for anonymous heads on git repos, which
396 # namespace to allow for anonymous heads on git repos, which
397 # would be neat.
397 # would be neat.
398 self.gitrepo.references.create(
398 self.gitrepo.references.create(
399 'refs/hg/internal/latest-commit', oid, force=True
399 'refs/hg/internal/latest-commit', oid, force=True
400 )
400 )
401 # Reindex now to pick up changes. We omit the progress
401 # Reindex now to pick up changes. We omit the progress
402 # and log callbacks because this will be very quick.
402 # and log callbacks because this will be very quick.
403 index._index_repo(self.gitrepo, self._db)
403 index._index_repo(self.gitrepo, self._db)
404 return oid.raw
404 return oid.raw
405
405
406
406
407 class manifestlog(baselog):
407 class manifestlog(baselog):
408 def __getitem__(self, node):
408 def __getitem__(self, node):
409 return self.get(b'', node)
409 return self.get(b'', node)
410
410
411 def get(self, relpath, node):
411 def get(self, relpath, node):
412 if node == nodemod.nullid:
412 if node == nodemod.nullid:
413 # TODO: this should almost certainly be a memgittreemanifestctx
413 # TODO: this should almost certainly be a memgittreemanifestctx
414 return manifest.memtreemanifestctx(self, relpath)
414 return manifest.memtreemanifestctx(self, relpath)
415 commit = self.gitrepo[gitutil.togitnode(node)]
415 commit = self.gitrepo[gitutil.togitnode(node)]
416 t = commit.tree
416 t = commit.tree
417 if relpath:
417 if relpath:
418 parts = relpath.split(b'/')
418 parts = relpath.split(b'/')
419 for p in parts:
419 for p in parts:
420 te = t[p]
420 te = t[p]
421 t = self.gitrepo[te.id]
421 t = self.gitrepo[te.id]
422 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
422 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
423
423
424
424
425 @interfaceutil.implementer(repository.ifilestorage)
425 @interfaceutil.implementer(repository.ifilestorage)
426 class filelog(baselog):
426 class filelog(baselog):
427 def __init__(self, gr, db, path):
427 def __init__(self, gr, db, path):
428 super(filelog, self).__init__(gr, db)
428 super(filelog, self).__init__(gr, db)
429 assert isinstance(path, bytes)
429 assert isinstance(path, bytes)
430 self.path = path
430 self.path = path
431
431
432 def read(self, node):
432 def read(self, node):
433 if node == nodemod.nullid:
433 if node == nodemod.nullid:
434 return b''
434 return b''
435 return self.gitrepo[gitutil.togitnode(node)].data
435 return self.gitrepo[gitutil.togitnode(node)].data
436
436
437 def lookup(self, node):
437 def lookup(self, node):
438 if len(node) not in (20, 40):
438 if len(node) not in (20, 40):
439 node = int(node)
439 node = int(node)
440 if isinstance(node, int):
440 if isinstance(node, int):
441 assert False, b'todo revnums for nodes'
441 assert False, b'todo revnums for nodes'
442 if len(node) == 40:
442 if len(node) == 40:
443 node = nodemod.bin(node)
443 node = nodemod.bin(node)
444 hnode = gitutil.togitnode(node)
444 hnode = gitutil.togitnode(node)
445 if hnode in self.gitrepo:
445 if hnode in self.gitrepo:
446 return node
446 return node
447 raise error.LookupError(self.path, node, _(b'no match found'))
447 raise error.LookupError(self.path, node, _(b'no match found'))
448
448
449 def cmp(self, node, text):
449 def cmp(self, node, text):
450 """Returns True if text is different than content at `node`."""
450 """Returns True if text is different than content at `node`."""
451 return self.read(node) != text
451 return self.read(node) != text
452
452
453 def add(self, text, meta, transaction, link, p1=None, p2=None):
453 def add(self, text, meta, transaction, link, p1=None, p2=None):
454 assert not meta # Should we even try to handle this?
454 assert not meta # Should we even try to handle this?
455 return self.gitrepo.create_blob(text).raw
455 return self.gitrepo.create_blob(text).raw
456
456
457 def __iter__(self):
457 def __iter__(self):
458 for clrev in self._db.execute(
458 for clrev in self._db.execute(
459 '''
459 '''
460 SELECT rev FROM changelog
460 SELECT rev FROM changelog
461 INNER JOIN changedfiles ON changelog.node = changedfiles.node
461 INNER JOIN changedfiles ON changelog.node = changedfiles.node
462 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
462 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
463 ''',
463 ''',
464 (pycompat.fsdecode(self.path), gitutil.nullgit),
464 (pycompat.fsdecode(self.path), gitutil.nullgit),
465 ):
465 ):
466 yield clrev[0]
466 yield clrev[0]
467
467
468 def linkrev(self, fr):
468 def linkrev(self, fr):
469 return fr
469 return fr
470
470
471 def rev(self, node):
471 def rev(self, node):
472 row = self._db.execute(
472 row = self._db.execute(
473 '''
473 '''
474 SELECT rev FROM changelog
474 SELECT rev FROM changelog
475 INNER JOIN changedfiles ON changelog.node = changedfiles.node
475 INNER JOIN changedfiles ON changelog.node = changedfiles.node
476 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
476 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
477 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
477 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
478 ).fetchone()
478 ).fetchone()
479 if row is None:
479 if row is None:
480 raise error.LookupError(self.path, node, _(b'no such node'))
480 raise error.LookupError(self.path, node, _(b'no such node'))
481 return int(row[0])
481 return int(row[0])
482
482
483 def node(self, rev):
483 def node(self, rev):
484 maybe = self._db.execute(
484 maybe = self._db.execute(
485 '''SELECT filenode FROM changedfiles
485 '''SELECT filenode FROM changedfiles
486 INNER JOIN changelog ON changelog.node = changedfiles.node
486 INNER JOIN changelog ON changelog.node = changedfiles.node
487 WHERE changelog.rev = ? AND filename = ?
487 WHERE changelog.rev = ? AND filename = ?
488 ''',
488 ''',
489 (rev, pycompat.fsdecode(self.path)),
489 (rev, pycompat.fsdecode(self.path)),
490 ).fetchone()
490 ).fetchone()
491 if maybe is None:
491 if maybe is None:
492 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
492 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
493 return nodemod.bin(maybe[0])
493 return nodemod.bin(maybe[0])
494
494
495 def parents(self, node):
495 def parents(self, node):
496 gn = gitutil.togitnode(node)
496 gn = gitutil.togitnode(node)
497 gp = pycompat.fsdecode(self.path)
497 gp = pycompat.fsdecode(self.path)
498 ps = []
498 ps = []
499 for p in self._db.execute(
499 for p in self._db.execute(
500 '''SELECT p1filenode, p2filenode FROM changedfiles
500 '''SELECT p1filenode, p2filenode FROM changedfiles
501 WHERE filenode = ? AND filename = ?
501 WHERE filenode = ? AND filename = ?
502 ''',
502 ''',
503 (gn, gp),
503 (gn, gp),
504 ).fetchone():
504 ).fetchone():
505 if p is None:
505 if p is None:
506 commit = self._db.execute(
506 commit = self._db.execute(
507 "SELECT node FROM changedfiles "
507 "SELECT node FROM changedfiles "
508 "WHERE filenode = ? AND filename = ?",
508 "WHERE filenode = ? AND filename = ?",
509 (gn, gp),
509 (gn, gp),
510 ).fetchone()[0]
510 ).fetchone()[0]
511 # This filelog is missing some data. Build the
511 # This filelog is missing some data. Build the
512 # filelog, then recurse (which will always find data).
512 # filelog, then recurse (which will always find data).
513 if pycompat.ispy3:
513 if pycompat.ispy3:
514 commit = commit.decode('ascii')
514 commit = commit.decode('ascii')
515 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
515 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
516 return self.parents(node)
516 return self.parents(node)
517 else:
517 else:
518 ps.append(nodemod.bin(p))
518 ps.append(nodemod.bin(p))
519 return ps
519 return ps
520
520
521 def renamed(self, node):
521 def renamed(self, node):
522 # TODO: renames/copies
522 # TODO: renames/copies
523 return False
523 return False
General Comments 0
You need to be logged in to leave comments. Login now