##// END OF EJS Templates
git: implement some changelog methods...
Romain DEP. -
r45364:3e09d22a default
parent child Browse files
Show More
@@ -1,469 +1,523 b''
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):
251 """Return an object that can be used to incrementally compute the
252 revision numbers of the ancestors of arbitrary sets that are not
253 ancestors of common. This is an ancestor.incrementalmissingancestors
254 object.
255
256 'common' is a list of revision numbers. If common is not supplied, uses
257 nullrev.
258 """
259 if common is None:
260 common = [nodemod.nullrev]
261
262 return ancestor.incrementalmissingancestors(self.parentrevs, common)
263
264 def findmissing(self, common=None, heads=None):
265 """Return the ancestors of heads that are not ancestors of common.
266
267 More specifically, return a list of nodes N such that every N
268 satisfies the following constraints:
269
270 1. N is an ancestor of some node in 'heads'
271 2. N is not an ancestor of any node in 'common'
272
273 The list is sorted by revision number, meaning it is
274 topologically sorted.
275
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
278 supplied, uses nullid."""
279 if common is None:
280 common = [nodemod.nullid]
281 if heads is None:
282 heads = self.heads()
283
284 common = [self.rev(n) for n in common]
285 heads = [self.rev(n) for n in heads]
286
287 inc = self.incrementalmissingrevs(common=common)
288 return [self.node(r) for r in inc.missingancestors(heads)]
289
290 def children(self, node):
291 """find the children of a given node"""
292 c = []
293 p = self.rev(node)
294 for r in self.revs(start=p + 1):
295 prevs = [pr for pr in self.parentrevs(r) if pr != nodemod.nullrev]
296 if prevs:
297 for pr in prevs:
298 if pr == p:
299 c.append(self.node(r))
300 elif p == nodemod.nullrev:
301 c.append(self.node(r))
302 return c
303
250 def reachableroots(self, minroot, heads, roots, includepath=False):
304 def reachableroots(self, minroot, heads, roots, includepath=False):
251 return dagop._reachablerootspure(
305 return dagop._reachablerootspure(
252 self.parentrevs, minroot, roots, heads, includepath
306 self.parentrevs, minroot, roots, heads, includepath
253 )
307 )
254
308
255 # Cleanup opportunity: this is *identical* to the revlog.py version
309 # Cleanup opportunity: this is *identical* to the revlog.py version
256 def isancestor(self, a, b):
310 def isancestor(self, a, b):
257 a, b = self.rev(a), self.rev(b)
311 a, b = self.rev(a), self.rev(b)
258 return self.isancestorrev(a, b)
312 return self.isancestorrev(a, b)
259
313
260 # Cleanup opportunity: this is *identical* to the revlog.py version
314 # Cleanup opportunity: this is *identical* to the revlog.py version
261 def isancestorrev(self, a, b):
315 def isancestorrev(self, a, b):
262 if a == nodemod.nullrev:
316 if a == nodemod.nullrev:
263 return True
317 return True
264 elif a == b:
318 elif a == b:
265 return True
319 return True
266 elif a > b:
320 elif a > b:
267 return False
321 return False
268 return bool(self.reachableroots(a, [b], [a], includepath=False))
322 return bool(self.reachableroots(a, [b], [a], includepath=False))
269
323
270 def parentrevs(self, rev):
324 def parentrevs(self, rev):
271 n = self.node(rev)
325 n = self.node(rev)
272 hn = gitutil.togitnode(n)
326 hn = gitutil.togitnode(n)
273 if hn != gitutil.nullgit:
327 if hn != gitutil.nullgit:
274 c = self.gitrepo[hn]
328 c = self.gitrepo[hn]
275 else:
329 else:
276 return nodemod.nullrev, nodemod.nullrev
330 return nodemod.nullrev, nodemod.nullrev
277 p1 = p2 = nodemod.nullrev
331 p1 = p2 = nodemod.nullrev
278 if c.parents:
332 if c.parents:
279 p1 = self.rev(c.parents[0].id.raw)
333 p1 = self.rev(c.parents[0].id.raw)
280 if len(c.parents) > 2:
334 if len(c.parents) > 2:
281 raise error.Abort(b'TODO octopus merge handling')
335 raise error.Abort(b'TODO octopus merge handling')
282 if len(c.parents) == 2:
336 if len(c.parents) == 2:
283 p2 = self.rev(c.parents[1].id.raw)
337 p2 = self.rev(c.parents[1].id.raw)
284 return p1, p2
338 return p1, p2
285
339
286 # Private method is used at least by the tags code.
340 # Private method is used at least by the tags code.
287 _uncheckedparentrevs = parentrevs
341 _uncheckedparentrevs = parentrevs
288
342
289 def commonancestorsheads(self, a, b):
343 def commonancestorsheads(self, a, b):
290 # 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
291 # need to optimize this...
345 # need to optimize this...
292 a, b = self.rev(a), self.rev(b)
346 a, b = self.rev(a), self.rev(b)
293 return [
347 return [
294 self.node(n)
348 self.node(n)
295 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
349 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
296 ]
350 ]
297
351
298 def branchinfo(self, rev):
352 def branchinfo(self, rev):
299 """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."""
300 return b'default', False
354 return b'default', False
301
355
302 def delayupdate(self, tr):
356 def delayupdate(self, tr):
303 # 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
304 # an object in the git repo?
358 # an object in the git repo?
305 pass
359 pass
306
360
307 def add(
361 def add(
308 self,
362 self,
309 manifest,
363 manifest,
310 files,
364 files,
311 desc,
365 desc,
312 transaction,
366 transaction,
313 p1,
367 p1,
314 p2,
368 p2,
315 user,
369 user,
316 date=None,
370 date=None,
317 extra=None,
371 extra=None,
318 p1copies=None,
372 p1copies=None,
319 p2copies=None,
373 p2copies=None,
320 filesadded=None,
374 filesadded=None,
321 filesremoved=None,
375 filesremoved=None,
322 ):
376 ):
323 parents = []
377 parents = []
324 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
378 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
325 if p1 != nodemod.nullid:
379 if p1 != nodemod.nullid:
326 parents.append(hp1)
380 parents.append(hp1)
327 if p2 and p2 != nodemod.nullid:
381 if p2 and p2 != nodemod.nullid:
328 parents.append(hp2)
382 parents.append(hp2)
329 assert date is not None
383 assert date is not None
330 timestamp, tz = date
384 timestamp, tz = date
331 sig = pygit2.Signature(
385 sig = pygit2.Signature(
332 encoding.unifromlocal(stringutil.person(user)),
386 encoding.unifromlocal(stringutil.person(user)),
333 encoding.unifromlocal(stringutil.email(user)),
387 encoding.unifromlocal(stringutil.email(user)),
334 timestamp,
388 timestamp,
335 -(tz // 60),
389 -(tz // 60),
336 )
390 )
337 oid = self.gitrepo.create_commit(
391 oid = self.gitrepo.create_commit(
338 None, sig, sig, desc, gitutil.togitnode(manifest), parents
392 None, sig, sig, desc, gitutil.togitnode(manifest), parents
339 )
393 )
340 # Set up an internal reference to force the commit into the
394 # Set up an internal reference to force the commit into the
341 # changelog. Hypothetically, we could even use this refs/hg/
395 # changelog. Hypothetically, we could even use this refs/hg/
342 # namespace to allow for anonymous heads on git repos, which
396 # namespace to allow for anonymous heads on git repos, which
343 # would be neat.
397 # would be neat.
344 self.gitrepo.references.create(
398 self.gitrepo.references.create(
345 'refs/hg/internal/latest-commit', oid, force=True
399 'refs/hg/internal/latest-commit', oid, force=True
346 )
400 )
347 # Reindex now to pick up changes. We omit the progress
401 # Reindex now to pick up changes. We omit the progress
348 # callback because this will be very quick.
402 # callback because this will be very quick.
349 index._index_repo(self.gitrepo, self._db)
403 index._index_repo(self.gitrepo, self._db)
350 return oid.raw
404 return oid.raw
351
405
352
406
353 class manifestlog(baselog):
407 class manifestlog(baselog):
354 def __getitem__(self, node):
408 def __getitem__(self, node):
355 return self.get(b'', node)
409 return self.get(b'', node)
356
410
357 def get(self, relpath, node):
411 def get(self, relpath, node):
358 if node == nodemod.nullid:
412 if node == nodemod.nullid:
359 # TODO: this should almost certainly be a memgittreemanifestctx
413 # TODO: this should almost certainly be a memgittreemanifestctx
360 return manifest.memtreemanifestctx(self, relpath)
414 return manifest.memtreemanifestctx(self, relpath)
361 commit = self.gitrepo[gitutil.togitnode(node)]
415 commit = self.gitrepo[gitutil.togitnode(node)]
362 t = commit.tree
416 t = commit.tree
363 if relpath:
417 if relpath:
364 parts = relpath.split(b'/')
418 parts = relpath.split(b'/')
365 for p in parts:
419 for p in parts:
366 te = t[p]
420 te = t[p]
367 t = self.gitrepo[te.id]
421 t = self.gitrepo[te.id]
368 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
422 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
369
423
370
424
371 @interfaceutil.implementer(repository.ifilestorage)
425 @interfaceutil.implementer(repository.ifilestorage)
372 class filelog(baselog):
426 class filelog(baselog):
373 def __init__(self, gr, db, path):
427 def __init__(self, gr, db, path):
374 super(filelog, self).__init__(gr, db)
428 super(filelog, self).__init__(gr, db)
375 assert isinstance(path, bytes)
429 assert isinstance(path, bytes)
376 self.path = path
430 self.path = path
377
431
378 def read(self, node):
432 def read(self, node):
379 if node == nodemod.nullid:
433 if node == nodemod.nullid:
380 return b''
434 return b''
381 return self.gitrepo[gitutil.togitnode(node)].data
435 return self.gitrepo[gitutil.togitnode(node)].data
382
436
383 def lookup(self, node):
437 def lookup(self, node):
384 if len(node) not in (20, 40):
438 if len(node) not in (20, 40):
385 node = int(node)
439 node = int(node)
386 if isinstance(node, int):
440 if isinstance(node, int):
387 assert False, b'todo revnums for nodes'
441 assert False, b'todo revnums for nodes'
388 if len(node) == 40:
442 if len(node) == 40:
389 node = nodemod.bin(node)
443 node = nodemod.bin(node)
390 hnode = gitutil.togitnode(node)
444 hnode = gitutil.togitnode(node)
391 if hnode in self.gitrepo:
445 if hnode in self.gitrepo:
392 return node
446 return node
393 raise error.LookupError(self.path, node, _(b'no match found'))
447 raise error.LookupError(self.path, node, _(b'no match found'))
394
448
395 def cmp(self, node, text):
449 def cmp(self, node, text):
396 """Returns True if text is different than content at `node`."""
450 """Returns True if text is different than content at `node`."""
397 return self.read(node) != text
451 return self.read(node) != text
398
452
399 def add(self, text, meta, transaction, link, p1=None, p2=None):
453 def add(self, text, meta, transaction, link, p1=None, p2=None):
400 assert not meta # Should we even try to handle this?
454 assert not meta # Should we even try to handle this?
401 return self.gitrepo.create_blob(text).raw
455 return self.gitrepo.create_blob(text).raw
402
456
403 def __iter__(self):
457 def __iter__(self):
404 for clrev in self._db.execute(
458 for clrev in self._db.execute(
405 '''
459 '''
406 SELECT rev FROM changelog
460 SELECT rev FROM changelog
407 INNER JOIN changedfiles ON changelog.node = changedfiles.node
461 INNER JOIN changedfiles ON changelog.node = changedfiles.node
408 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
462 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
409 ''',
463 ''',
410 (pycompat.fsdecode(self.path), gitutil.nullgit),
464 (pycompat.fsdecode(self.path), gitutil.nullgit),
411 ):
465 ):
412 yield clrev[0]
466 yield clrev[0]
413
467
414 def linkrev(self, fr):
468 def linkrev(self, fr):
415 return fr
469 return fr
416
470
417 def rev(self, node):
471 def rev(self, node):
418 row = self._db.execute(
472 row = self._db.execute(
419 '''
473 '''
420 SELECT rev FROM changelog
474 SELECT rev FROM changelog
421 INNER JOIN changedfiles ON changelog.node = changedfiles.node
475 INNER JOIN changedfiles ON changelog.node = changedfiles.node
422 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
476 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
423 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
477 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
424 ).fetchone()
478 ).fetchone()
425 if row is None:
479 if row is None:
426 raise error.LookupError(self.path, node, _(b'no such node'))
480 raise error.LookupError(self.path, node, _(b'no such node'))
427 return int(row[0])
481 return int(row[0])
428
482
429 def node(self, rev):
483 def node(self, rev):
430 maybe = self._db.execute(
484 maybe = self._db.execute(
431 '''SELECT filenode FROM changedfiles
485 '''SELECT filenode FROM changedfiles
432 INNER JOIN changelog ON changelog.node = changedfiles.node
486 INNER JOIN changelog ON changelog.node = changedfiles.node
433 WHERE changelog.rev = ? AND filename = ?
487 WHERE changelog.rev = ? AND filename = ?
434 ''',
488 ''',
435 (rev, pycompat.fsdecode(self.path)),
489 (rev, pycompat.fsdecode(self.path)),
436 ).fetchone()
490 ).fetchone()
437 if maybe is None:
491 if maybe is None:
438 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
492 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
439 return nodemod.bin(maybe[0])
493 return nodemod.bin(maybe[0])
440
494
441 def parents(self, node):
495 def parents(self, node):
442 gn = gitutil.togitnode(node)
496 gn = gitutil.togitnode(node)
443 gp = pycompat.fsdecode(self.path)
497 gp = pycompat.fsdecode(self.path)
444 ps = []
498 ps = []
445 for p in self._db.execute(
499 for p in self._db.execute(
446 '''SELECT p1filenode, p2filenode FROM changedfiles
500 '''SELECT p1filenode, p2filenode FROM changedfiles
447 WHERE filenode = ? AND filename = ?
501 WHERE filenode = ? AND filename = ?
448 ''',
502 ''',
449 (gn, gp),
503 (gn, gp),
450 ).fetchone():
504 ).fetchone():
451 if p is None:
505 if p is None:
452 commit = self._db.execute(
506 commit = self._db.execute(
453 "SELECT node FROM changedfiles "
507 "SELECT node FROM changedfiles "
454 "WHERE filenode = ? AND filename = ?",
508 "WHERE filenode = ? AND filename = ?",
455 (gn, gp),
509 (gn, gp),
456 ).fetchone()[0]
510 ).fetchone()[0]
457 # This filelog is missing some data. Build the
511 # This filelog is missing some data. Build the
458 # filelog, then recurse (which will always find data).
512 # filelog, then recurse (which will always find data).
459 if pycompat.ispy3:
513 if pycompat.ispy3:
460 commit = commit.decode('ascii')
514 commit = commit.decode('ascii')
461 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
515 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
462 return self.parents(node)
516 return self.parents(node)
463 else:
517 else:
464 ps.append(nodemod.bin(p))
518 ps.append(nodemod.bin(p))
465 return ps
519 return ps
466
520
467 def renamed(self, node):
521 def renamed(self, node):
468 # TODO: renames/copies
522 # TODO: renames/copies
469 return False
523 return False
General Comments 0
You need to be logged in to leave comments. Login now