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