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