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