##// END OF EJS Templates
gitlog: implement __iter__ on changelog...
Augie Fackler -
r44965:8b5f5d8a default
parent child Browse files
Show More
@@ -1,463 +1,466 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import pygit2
3 import pygit2
4
4
5 from mercurial.i18n import _
5 from mercurial.i18n import _
6
6
7 from mercurial import (
7 from mercurial import (
8 ancestor,
8 ancestor,
9 changelog as hgchangelog,
9 changelog as hgchangelog,
10 dagop,
10 dagop,
11 encoding,
11 encoding,
12 error,
12 error,
13 manifest,
13 manifest,
14 node as nodemod,
14 node as nodemod,
15 pycompat,
15 pycompat,
16 )
16 )
17 from mercurial.interfaces import (
17 from mercurial.interfaces import (
18 repository,
18 repository,
19 util as interfaceutil,
19 util as interfaceutil,
20 )
20 )
21 from mercurial.utils import stringutil
21 from mercurial.utils import stringutil
22 from . import (
22 from . import (
23 gitutil,
23 gitutil,
24 index,
24 index,
25 manifest as gitmanifest,
25 manifest as gitmanifest,
26 )
26 )
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):
107 return iter(pycompat.xrange(len(self)))
108
106 @property
109 @property
107 def filteredrevs(self):
110 def filteredrevs(self):
108 # TODO: we should probably add a refs/hg/ namespace for hidden
111 # TODO: we should probably add a refs/hg/ namespace for hidden
109 # heads etc, but that's an idea for later.
112 # heads etc, but that's an idea for later.
110 return set()
113 return set()
111
114
112 @property
115 @property
113 def index(self):
116 def index(self):
114 return baselogindex(self)
117 return baselogindex(self)
115
118
116 @property
119 @property
117 def nodemap(self):
120 def nodemap(self):
118 r = {
121 r = {
119 nodemod.bin(v[0]): v[1]
122 nodemod.bin(v[0]): v[1]
120 for v in self._db.execute('SELECT node, rev FROM changelog')
123 for v in self._db.execute('SELECT node, rev FROM changelog')
121 }
124 }
122 r[nodemod.nullid] = nodemod.nullrev
125 r[nodemod.nullid] = nodemod.nullrev
123 return r
126 return r
124
127
125 def tip(self):
128 def tip(self):
126 t = self._db.execute(
129 t = self._db.execute(
127 'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
130 'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
128 ).fetchone()
131 ).fetchone()
129 if t:
132 if t:
130 return nodemod.bin(t[0])
133 return nodemod.bin(t[0])
131 return nodemod.nullid
134 return nodemod.nullid
132
135
133 def revs(self, start=0, stop=None):
136 def revs(self, start=0, stop=None):
134 if stop is None:
137 if stop is None:
135 stop = self.tip()
138 stop = self.tip()
136 t = self._db.execute(
139 t = self._db.execute(
137 'SELECT rev FROM changelog '
140 'SELECT rev FROM changelog '
138 'WHERE rev >= ? AND rev <= ? '
141 'WHERE rev >= ? AND rev <= ? '
139 'ORDER BY REV ASC',
142 'ORDER BY REV ASC',
140 (start, stop),
143 (start, stop),
141 )
144 )
142 return (int(r[0]) for r in t)
145 return (int(r[0]) for r in t)
143
146
144 def _partialmatch(self, id):
147 def _partialmatch(self, id):
145 if nodemod.wdirhex.startswith(id):
148 if nodemod.wdirhex.startswith(id):
146 raise error.WdirUnsupported
149 raise error.WdirUnsupported
147 candidates = [
150 candidates = [
148 nodemod.bin(x[0])
151 nodemod.bin(x[0])
149 for x in self._db.execute(
152 for x in self._db.execute(
150 'SELECT node FROM changelog WHERE node LIKE ?', (id + b'%',)
153 'SELECT node FROM changelog WHERE node LIKE ?', (id + b'%',)
151 )
154 )
152 ]
155 ]
153 if nodemod.nullhex.startswith(id):
156 if nodemod.nullhex.startswith(id):
154 candidates.append(nodemod.nullid)
157 candidates.append(nodemod.nullid)
155 if len(candidates) > 1:
158 if len(candidates) > 1:
156 raise error.AmbiguousPrefixLookupError(
159 raise error.AmbiguousPrefixLookupError(
157 id, b'00changelog.i', _(b'ambiguous identifier')
160 id, b'00changelog.i', _(b'ambiguous identifier')
158 )
161 )
159 if candidates:
162 if candidates:
160 return candidates[0]
163 return candidates[0]
161 return None
164 return None
162
165
163 def flags(self, rev):
166 def flags(self, rev):
164 return 0
167 return 0
165
168
166 def shortest(self, node, minlength=1):
169 def shortest(self, node, minlength=1):
167 nodehex = nodemod.hex(node)
170 nodehex = nodemod.hex(node)
168 for attempt in pycompat.xrange(minlength, len(nodehex) + 1):
171 for attempt in pycompat.xrange(minlength, len(nodehex) + 1):
169 candidate = nodehex[:attempt]
172 candidate = nodehex[:attempt]
170 matches = int(
173 matches = int(
171 self._db.execute(
174 self._db.execute(
172 'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
175 'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
173 (pycompat.sysstr(candidate + b'%'),),
176 (pycompat.sysstr(candidate + b'%'),),
174 ).fetchone()[0]
177 ).fetchone()[0]
175 )
178 )
176 if matches == 1:
179 if matches == 1:
177 return candidate
180 return candidate
178 return nodehex
181 return nodehex
179
182
180 def headrevs(self, revs=None):
183 def headrevs(self, revs=None):
181 realheads = [
184 realheads = [
182 int(x[0])
185 int(x[0])
183 for x in self._db.execute(
186 for x in self._db.execute(
184 'SELECT rev FROM changelog '
187 'SELECT rev FROM changelog '
185 'INNER JOIN heads ON changelog.node = heads.node'
188 'INNER JOIN heads ON changelog.node = heads.node'
186 )
189 )
187 ]
190 ]
188 if revs:
191 if revs:
189 return sorted([r for r in revs if r in realheads])
192 return sorted([r for r in revs if r in realheads])
190 return sorted(realheads)
193 return sorted(realheads)
191
194
192 def changelogrevision(self, nodeorrev):
195 def changelogrevision(self, nodeorrev):
193 # Ensure we have a node id
196 # Ensure we have a node id
194 if isinstance(nodeorrev, int):
197 if isinstance(nodeorrev, int):
195 n = self.node(nodeorrev)
198 n = self.node(nodeorrev)
196 else:
199 else:
197 n = nodeorrev
200 n = nodeorrev
198 # handle looking up nullid
201 # handle looking up nullid
199 if n == nodemod.nullid:
202 if n == nodemod.nullid:
200 return hgchangelog._changelogrevision(extra={})
203 return hgchangelog._changelogrevision(extra={})
201 hn = gitutil.togitnode(n)
204 hn = gitutil.togitnode(n)
202 # We've got a real commit!
205 # We've got a real commit!
203 files = [
206 files = [
204 r[0]
207 r[0]
205 for r in self._db.execute(
208 for r in self._db.execute(
206 'SELECT filename FROM changedfiles '
209 'SELECT filename FROM changedfiles '
207 'WHERE node = ? and filenode != ?',
210 'WHERE node = ? and filenode != ?',
208 (hn, gitutil.nullgit),
211 (hn, gitutil.nullgit),
209 )
212 )
210 ]
213 ]
211 filesremoved = [
214 filesremoved = [
212 r[0]
215 r[0]
213 for r in self._db.execute(
216 for r in self._db.execute(
214 'SELECT filename FROM changedfiles '
217 'SELECT filename FROM changedfiles '
215 'WHERE node = ? and filenode = ?',
218 'WHERE node = ? and filenode = ?',
216 (hn, nodemod.nullhex),
219 (hn, nodemod.nullhex),
217 )
220 )
218 ]
221 ]
219 c = self.gitrepo[hn]
222 c = self.gitrepo[hn]
220 return hgchangelog._changelogrevision(
223 return hgchangelog._changelogrevision(
221 manifest=n, # pretend manifest the same as the commit node
224 manifest=n, # pretend manifest the same as the commit node
222 user=b'%s <%s>'
225 user=b'%s <%s>'
223 % (c.author.name.encode('utf8'), c.author.email.encode('utf8')),
226 % (c.author.name.encode('utf8'), c.author.email.encode('utf8')),
224 date=(c.author.time, -c.author.offset * 60),
227 date=(c.author.time, -c.author.offset * 60),
225 files=files,
228 files=files,
226 # TODO filesadded in the index
229 # TODO filesadded in the index
227 filesremoved=filesremoved,
230 filesremoved=filesremoved,
228 description=c.message.encode('utf8'),
231 description=c.message.encode('utf8'),
229 # TODO do we want to handle extra? how?
232 # TODO do we want to handle extra? how?
230 extra={b'branch': b'default'},
233 extra={b'branch': b'default'},
231 )
234 )
232
235
233 def ancestors(self, revs, stoprev=0, inclusive=False):
236 def ancestors(self, revs, stoprev=0, inclusive=False):
234 revs = list(revs)
237 revs = list(revs)
235 tip = self.rev(self.tip())
238 tip = self.rev(self.tip())
236 for r in revs:
239 for r in revs:
237 if r > tip:
240 if r > tip:
238 raise IndexError(b'Invalid rev %r' % r)
241 raise IndexError(b'Invalid rev %r' % r)
239 return ancestor.lazyancestors(
242 return ancestor.lazyancestors(
240 self.parentrevs, revs, stoprev=stoprev, inclusive=inclusive
243 self.parentrevs, revs, stoprev=stoprev, inclusive=inclusive
241 )
244 )
242
245
243 # Cleanup opportunity: this is *identical* to the revlog.py version
246 # Cleanup opportunity: this is *identical* to the revlog.py version
244 def descendants(self, revs):
247 def descendants(self, revs):
245 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
248 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
246
249
247 def reachableroots(self, minroot, heads, roots, includepath=False):
250 def reachableroots(self, minroot, heads, roots, includepath=False):
248 return dagop._reachablerootspure(
251 return dagop._reachablerootspure(
249 self.parentrevs, minroot, roots, heads, includepath
252 self.parentrevs, minroot, roots, heads, includepath
250 )
253 )
251
254
252 # Cleanup opportunity: this is *identical* to the revlog.py version
255 # Cleanup opportunity: this is *identical* to the revlog.py version
253 def isancestor(self, a, b):
256 def isancestor(self, a, b):
254 a, b = self.rev(a), self.rev(b)
257 a, b = self.rev(a), self.rev(b)
255 return self.isancestorrev(a, b)
258 return self.isancestorrev(a, b)
256
259
257 # Cleanup opportunity: this is *identical* to the revlog.py version
260 # Cleanup opportunity: this is *identical* to the revlog.py version
258 def isancestorrev(self, a, b):
261 def isancestorrev(self, a, b):
259 if a == nodemod.nullrev:
262 if a == nodemod.nullrev:
260 return True
263 return True
261 elif a == b:
264 elif a == b:
262 return True
265 return True
263 elif a > b:
266 elif a > b:
264 return False
267 return False
265 return bool(self.reachableroots(a, [b], [a], includepath=False))
268 return bool(self.reachableroots(a, [b], [a], includepath=False))
266
269
267 def parentrevs(self, rev):
270 def parentrevs(self, rev):
268 n = self.node(rev)
271 n = self.node(rev)
269 hn = gitutil.togitnode(n)
272 hn = gitutil.togitnode(n)
270 c = self.gitrepo[hn]
273 c = self.gitrepo[hn]
271 p1 = p2 = nodemod.nullrev
274 p1 = p2 = nodemod.nullrev
272 if c.parents:
275 if c.parents:
273 p1 = self.rev(c.parents[0].id.raw)
276 p1 = self.rev(c.parents[0].id.raw)
274 if len(c.parents) > 2:
277 if len(c.parents) > 2:
275 raise error.Abort(b'TODO octopus merge handling')
278 raise error.Abort(b'TODO octopus merge handling')
276 if len(c.parents) == 2:
279 if len(c.parents) == 2:
277 p2 = self.rev(c.parents[1].id.raw)
280 p2 = self.rev(c.parents[1].id.raw)
278 return p1, p2
281 return p1, p2
279
282
280 # Private method is used at least by the tags code.
283 # Private method is used at least by the tags code.
281 _uncheckedparentrevs = parentrevs
284 _uncheckedparentrevs = parentrevs
282
285
283 def commonancestorsheads(self, a, b):
286 def commonancestorsheads(self, a, b):
284 # TODO the revlog verson of this has a C path, so we probably
287 # TODO the revlog verson of this has a C path, so we probably
285 # need to optimize this...
288 # need to optimize this...
286 a, b = self.rev(a), self.rev(b)
289 a, b = self.rev(a), self.rev(b)
287 return [
290 return [
288 self.node(n)
291 self.node(n)
289 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
292 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
290 ]
293 ]
291
294
292 def branchinfo(self, rev):
295 def branchinfo(self, rev):
293 """Git doesn't do named branches, so just put everything on default."""
296 """Git doesn't do named branches, so just put everything on default."""
294 return b'default', False
297 return b'default', False
295
298
296 def delayupdate(self, tr):
299 def delayupdate(self, tr):
297 # TODO: I think we can elide this because we're just dropping
300 # TODO: I think we can elide this because we're just dropping
298 # an object in the git repo?
301 # an object in the git repo?
299 pass
302 pass
300
303
301 def add(
304 def add(
302 self,
305 self,
303 manifest,
306 manifest,
304 files,
307 files,
305 desc,
308 desc,
306 transaction,
309 transaction,
307 p1,
310 p1,
308 p2,
311 p2,
309 user,
312 user,
310 date=None,
313 date=None,
311 extra=None,
314 extra=None,
312 p1copies=None,
315 p1copies=None,
313 p2copies=None,
316 p2copies=None,
314 filesadded=None,
317 filesadded=None,
315 filesremoved=None,
318 filesremoved=None,
316 ):
319 ):
317 parents = []
320 parents = []
318 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
321 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
319 if p1 != nodemod.nullid:
322 if p1 != nodemod.nullid:
320 parents.append(hp1)
323 parents.append(hp1)
321 if p2 and p2 != nodemod.nullid:
324 if p2 and p2 != nodemod.nullid:
322 parents.append(hp2)
325 parents.append(hp2)
323 assert date is not None
326 assert date is not None
324 timestamp, tz = date
327 timestamp, tz = date
325 sig = pygit2.Signature(
328 sig = pygit2.Signature(
326 encoding.unifromlocal(stringutil.person(user)),
329 encoding.unifromlocal(stringutil.person(user)),
327 encoding.unifromlocal(stringutil.email(user)),
330 encoding.unifromlocal(stringutil.email(user)),
328 timestamp,
331 timestamp,
329 -(tz // 60),
332 -(tz // 60),
330 )
333 )
331 oid = self.gitrepo.create_commit(
334 oid = self.gitrepo.create_commit(
332 None, sig, sig, desc, gitutil.togitnode(manifest), parents
335 None, sig, sig, desc, gitutil.togitnode(manifest), parents
333 )
336 )
334 # Set up an internal reference to force the commit into the
337 # Set up an internal reference to force the commit into the
335 # changelog. Hypothetically, we could even use this refs/hg/
338 # changelog. Hypothetically, we could even use this refs/hg/
336 # namespace to allow for anonymous heads on git repos, which
339 # namespace to allow for anonymous heads on git repos, which
337 # would be neat.
340 # would be neat.
338 self.gitrepo.references.create(
341 self.gitrepo.references.create(
339 'refs/hg/internal/latest-commit', oid, force=True
342 'refs/hg/internal/latest-commit', oid, force=True
340 )
343 )
341 # Reindex now to pick up changes. We omit the progress
344 # Reindex now to pick up changes. We omit the progress
342 # callback because this will be very quick.
345 # callback because this will be very quick.
343 index._index_repo(self.gitrepo, self._db)
346 index._index_repo(self.gitrepo, self._db)
344 return oid.raw
347 return oid.raw
345
348
346
349
347 class manifestlog(baselog):
350 class manifestlog(baselog):
348 def __getitem__(self, node):
351 def __getitem__(self, node):
349 return self.get(b'', node)
352 return self.get(b'', node)
350
353
351 def get(self, relpath, node):
354 def get(self, relpath, node):
352 if node == nodemod.nullid:
355 if node == nodemod.nullid:
353 # TODO: this should almost certainly be a memgittreemanifestctx
356 # TODO: this should almost certainly be a memgittreemanifestctx
354 return manifest.memtreemanifestctx(self, relpath)
357 return manifest.memtreemanifestctx(self, relpath)
355 commit = self.gitrepo[gitutil.togitnode(node)]
358 commit = self.gitrepo[gitutil.togitnode(node)]
356 t = commit.tree
359 t = commit.tree
357 if relpath:
360 if relpath:
358 parts = relpath.split(b'/')
361 parts = relpath.split(b'/')
359 for p in parts:
362 for p in parts:
360 te = t[p]
363 te = t[p]
361 t = self.gitrepo[te.id]
364 t = self.gitrepo[te.id]
362 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
365 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
363
366
364
367
365 @interfaceutil.implementer(repository.ifilestorage)
368 @interfaceutil.implementer(repository.ifilestorage)
366 class filelog(baselog):
369 class filelog(baselog):
367 def __init__(self, gr, db, path):
370 def __init__(self, gr, db, path):
368 super(filelog, self).__init__(gr, db)
371 super(filelog, self).__init__(gr, db)
369 assert isinstance(path, bytes)
372 assert isinstance(path, bytes)
370 self.path = path
373 self.path = path
371
374
372 def read(self, node):
375 def read(self, node):
373 if node == nodemod.nullid:
376 if node == nodemod.nullid:
374 return b''
377 return b''
375 return self.gitrepo[gitutil.togitnode(node)].data
378 return self.gitrepo[gitutil.togitnode(node)].data
376
379
377 def lookup(self, node):
380 def lookup(self, node):
378 if len(node) not in (20, 40):
381 if len(node) not in (20, 40):
379 node = int(node)
382 node = int(node)
380 if isinstance(node, int):
383 if isinstance(node, int):
381 assert False, b'todo revnums for nodes'
384 assert False, b'todo revnums for nodes'
382 if len(node) == 40:
385 if len(node) == 40:
383 node = nodemod.bin(node)
386 node = nodemod.bin(node)
384 hnode = gitutil.togitnode(node)
387 hnode = gitutil.togitnode(node)
385 if hnode in self.gitrepo:
388 if hnode in self.gitrepo:
386 return node
389 return node
387 raise error.LookupError(self.path, node, _(b'no match found'))
390 raise error.LookupError(self.path, node, _(b'no match found'))
388
391
389 def cmp(self, node, text):
392 def cmp(self, node, text):
390 """Returns True if text is different than content at `node`."""
393 """Returns True if text is different than content at `node`."""
391 return self.read(node) != text
394 return self.read(node) != text
392
395
393 def add(self, text, meta, transaction, link, p1=None, p2=None):
396 def add(self, text, meta, transaction, link, p1=None, p2=None):
394 assert not meta # Should we even try to handle this?
397 assert not meta # Should we even try to handle this?
395 return self.gitrepo.create_blob(text).raw
398 return self.gitrepo.create_blob(text).raw
396
399
397 def __iter__(self):
400 def __iter__(self):
398 for clrev in self._db.execute(
401 for clrev in self._db.execute(
399 '''
402 '''
400 SELECT rev FROM changelog
403 SELECT rev FROM changelog
401 INNER JOIN changedfiles ON changelog.node = changedfiles.node
404 INNER JOIN changedfiles ON changelog.node = changedfiles.node
402 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
405 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
403 ''',
406 ''',
404 (pycompat.fsdecode(self.path), gitutil.nullgit),
407 (pycompat.fsdecode(self.path), gitutil.nullgit),
405 ):
408 ):
406 yield clrev[0]
409 yield clrev[0]
407
410
408 def linkrev(self, fr):
411 def linkrev(self, fr):
409 return fr
412 return fr
410
413
411 def rev(self, node):
414 def rev(self, node):
412 row = self._db.execute(
415 row = self._db.execute(
413 '''
416 '''
414 SELECT rev FROM changelog
417 SELECT rev FROM changelog
415 INNER JOIN changedfiles ON changelog.node = changedfiles.node
418 INNER JOIN changedfiles ON changelog.node = changedfiles.node
416 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
419 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
417 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
420 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
418 ).fetchone()
421 ).fetchone()
419 if row is None:
422 if row is None:
420 raise error.LookupError(self.path, node, _(b'no such node'))
423 raise error.LookupError(self.path, node, _(b'no such node'))
421 return int(row[0])
424 return int(row[0])
422
425
423 def node(self, rev):
426 def node(self, rev):
424 maybe = self._db.execute(
427 maybe = self._db.execute(
425 '''SELECT filenode FROM changedfiles
428 '''SELECT filenode FROM changedfiles
426 INNER JOIN changelog ON changelog.node = changedfiles.node
429 INNER JOIN changelog ON changelog.node = changedfiles.node
427 WHERE changelog.rev = ? AND filename = ?
430 WHERE changelog.rev = ? AND filename = ?
428 ''',
431 ''',
429 (rev, pycompat.fsdecode(self.path)),
432 (rev, pycompat.fsdecode(self.path)),
430 ).fetchone()
433 ).fetchone()
431 if maybe is None:
434 if maybe is None:
432 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
435 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
433 return nodemod.bin(maybe[0])
436 return nodemod.bin(maybe[0])
434
437
435 def parents(self, node):
438 def parents(self, node):
436 gn = gitutil.togitnode(node)
439 gn = gitutil.togitnode(node)
437 gp = pycompat.fsdecode(self.path)
440 gp = pycompat.fsdecode(self.path)
438 ps = []
441 ps = []
439 for p in self._db.execute(
442 for p in self._db.execute(
440 '''SELECT p1filenode, p2filenode FROM changedfiles
443 '''SELECT p1filenode, p2filenode FROM changedfiles
441 WHERE filenode = ? AND filename = ?
444 WHERE filenode = ? AND filename = ?
442 ''',
445 ''',
443 (gn, gp),
446 (gn, gp),
444 ).fetchone():
447 ).fetchone():
445 if p is None:
448 if p is None:
446 commit = self._db.execute(
449 commit = self._db.execute(
447 "SELECT node FROM changedfiles "
450 "SELECT node FROM changedfiles "
448 "WHERE filenode = ? AND filename = ?",
451 "WHERE filenode = ? AND filename = ?",
449 (gn, gp),
452 (gn, gp),
450 ).fetchone()[0]
453 ).fetchone()[0]
451 # This filelog is missing some data. Build the
454 # This filelog is missing some data. Build the
452 # filelog, then recurse (which will always find data).
455 # filelog, then recurse (which will always find data).
453 if pycompat.ispy3:
456 if pycompat.ispy3:
454 commit = commit.decode('ascii')
457 commit = commit.decode('ascii')
455 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
458 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
456 return self.parents(node)
459 return self.parents(node)
457 else:
460 else:
458 ps.append(nodemod.bin(p))
461 ps.append(nodemod.bin(p))
459 return ps
462 return ps
460
463
461 def renamed(self, node):
464 def renamed(self, node):
462 # TODO: renames/copies
465 # TODO: renames/copies
463 return False
466 return False
General Comments 0
You need to be logged in to leave comments. Login now