##// END OF EJS Templates
git: initialize `extra` to have at least the branch name for nullid...
Matt Harbison -
r47826:f8fa7ec5 stable
parent child Browse files
Show More
@@ -1,548 +1,549 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 = ?',
71 'SELECT node FROM changelog WHERE node = ?',
72 (pycompat.sysstr(n),),
72 (pycompat.sysstr(n),),
73 ).fetchone()
73 ).fetchone()
74 return t is not None
74 return t is not None
75
75
76
76
77 class baselogindex(object):
77 class baselogindex(object):
78 def __init__(self, log):
78 def __init__(self, log):
79 self._log = log
79 self._log = log
80
80
81 def has_node(self, n):
81 def has_node(self, n):
82 return self._log.rev(n) != -1
82 return self._log.rev(n) != -1
83
83
84 def __len__(self):
84 def __len__(self):
85 return len(self._log)
85 return len(self._log)
86
86
87 def __getitem__(self, idx):
87 def __getitem__(self, idx):
88 p1rev, p2rev = self._log.parentrevs(idx)
88 p1rev, p2rev = self._log.parentrevs(idx)
89 # TODO: it's messy that the index leaks so far out of the
89 # TODO: it's messy that the index leaks so far out of the
90 # storage layer that we have to implement things like reading
90 # storage layer that we have to implement things like reading
91 # this raw tuple, which exposes revlog internals.
91 # this raw tuple, which exposes revlog internals.
92 return (
92 return (
93 # Pretend offset is just the index, since we don't really care.
93 # Pretend offset is just the index, since we don't really care.
94 idx,
94 idx,
95 # Same with lengths
95 # Same with lengths
96 idx, # length
96 idx, # length
97 idx, # rawsize
97 idx, # rawsize
98 -1, # delta base
98 -1, # delta base
99 idx, # linkrev TODO is this right?
99 idx, # linkrev TODO is this right?
100 p1rev,
100 p1rev,
101 p2rev,
101 p2rev,
102 self._log.node(idx),
102 self._log.node(idx),
103 )
103 )
104
104
105
105
106 # TODO: an interface for the changelog type?
106 # TODO: an interface for the changelog type?
107 class changelog(baselog):
107 class changelog(baselog):
108 # TODO: this appears to be an enumerated type, and should probably
108 # TODO: this appears to be an enumerated type, and should probably
109 # be part of the public changelog interface
109 # be part of the public changelog interface
110 _copiesstorage = b'extra'
110 _copiesstorage = b'extra'
111
111
112 def __contains__(self, rev):
112 def __contains__(self, rev):
113 try:
113 try:
114 self.node(rev)
114 self.node(rev)
115 return True
115 return True
116 except error.LookupError:
116 except error.LookupError:
117 return False
117 return False
118
118
119 def __iter__(self):
119 def __iter__(self):
120 return iter(pycompat.xrange(len(self)))
120 return iter(pycompat.xrange(len(self)))
121
121
122 @property
122 @property
123 def filteredrevs(self):
123 def filteredrevs(self):
124 # TODO: we should probably add a refs/hg/ namespace for hidden
124 # TODO: we should probably add a refs/hg/ namespace for hidden
125 # heads etc, but that's an idea for later.
125 # heads etc, but that's an idea for later.
126 return set()
126 return set()
127
127
128 @property
128 @property
129 def index(self):
129 def index(self):
130 return baselogindex(self)
130 return baselogindex(self)
131
131
132 @property
132 @property
133 def nodemap(self):
133 def nodemap(self):
134 r = {
134 r = {
135 bin(v[0]): v[1]
135 bin(v[0]): v[1]
136 for v in self._db.execute('SELECT node, rev FROM changelog')
136 for v in self._db.execute('SELECT node, rev FROM changelog')
137 }
137 }
138 r[nullid] = nullrev
138 r[nullid] = nullrev
139 return r
139 return r
140
140
141 def tip(self):
141 def tip(self):
142 t = self._db.execute(
142 t = self._db.execute(
143 'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
143 'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
144 ).fetchone()
144 ).fetchone()
145 if t:
145 if t:
146 return bin(t[0])
146 return bin(t[0])
147 return nullid
147 return nullid
148
148
149 def revs(self, start=0, stop=None):
149 def revs(self, start=0, stop=None):
150 if stop is None:
150 if stop is None:
151 stop = self.tiprev()
151 stop = self.tiprev()
152 t = self._db.execute(
152 t = self._db.execute(
153 'SELECT rev FROM changelog '
153 'SELECT rev FROM changelog '
154 'WHERE rev >= ? AND rev <= ? '
154 'WHERE rev >= ? AND rev <= ? '
155 'ORDER BY REV ASC',
155 'ORDER BY REV ASC',
156 (start, stop),
156 (start, stop),
157 )
157 )
158 return (int(r[0]) for r in t)
158 return (int(r[0]) for r in t)
159
159
160 def tiprev(self):
160 def tiprev(self):
161 t = self._db.execute(
161 t = self._db.execute(
162 'SELECT rev FROM changelog ' 'ORDER BY REV DESC ' 'LIMIT 1'
162 'SELECT rev FROM changelog ' 'ORDER BY REV DESC ' 'LIMIT 1'
163 ).fetchone()
163 ).fetchone()
164
164
165 if t is not None:
165 if t is not None:
166 return t[0]
166 return t[0]
167 return -1
167 return -1
168
168
169 def _partialmatch(self, id):
169 def _partialmatch(self, id):
170 if wdirhex.startswith(id):
170 if wdirhex.startswith(id):
171 raise error.WdirUnsupported
171 raise error.WdirUnsupported
172 candidates = [
172 candidates = [
173 bin(x[0])
173 bin(x[0])
174 for x in self._db.execute(
174 for x in self._db.execute(
175 'SELECT node FROM changelog WHERE node LIKE ?',
175 'SELECT node FROM changelog WHERE node LIKE ?',
176 (pycompat.sysstr(id + b'%'),),
176 (pycompat.sysstr(id + b'%'),),
177 )
177 )
178 ]
178 ]
179 if nullhex.startswith(id):
179 if nullhex.startswith(id):
180 candidates.append(nullid)
180 candidates.append(nullid)
181 if len(candidates) > 1:
181 if len(candidates) > 1:
182 raise error.AmbiguousPrefixLookupError(
182 raise error.AmbiguousPrefixLookupError(
183 id, b'00changelog.i', _(b'ambiguous identifier')
183 id, b'00changelog.i', _(b'ambiguous identifier')
184 )
184 )
185 if candidates:
185 if candidates:
186 return candidates[0]
186 return candidates[0]
187 return None
187 return None
188
188
189 def flags(self, rev):
189 def flags(self, rev):
190 return 0
190 return 0
191
191
192 def shortest(self, node, minlength=1):
192 def shortest(self, node, minlength=1):
193 nodehex = hex(node)
193 nodehex = hex(node)
194 for attempt in pycompat.xrange(minlength, len(nodehex) + 1):
194 for attempt in pycompat.xrange(minlength, len(nodehex) + 1):
195 candidate = nodehex[:attempt]
195 candidate = nodehex[:attempt]
196 matches = int(
196 matches = int(
197 self._db.execute(
197 self._db.execute(
198 'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
198 'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
199 (pycompat.sysstr(candidate + b'%'),),
199 (pycompat.sysstr(candidate + b'%'),),
200 ).fetchone()[0]
200 ).fetchone()[0]
201 )
201 )
202 if matches == 1:
202 if matches == 1:
203 return candidate
203 return candidate
204 return nodehex
204 return nodehex
205
205
206 def headrevs(self, revs=None):
206 def headrevs(self, revs=None):
207 realheads = [
207 realheads = [
208 int(x[0])
208 int(x[0])
209 for x in self._db.execute(
209 for x in self._db.execute(
210 'SELECT rev FROM changelog '
210 'SELECT rev FROM changelog '
211 'INNER JOIN heads ON changelog.node = heads.node'
211 'INNER JOIN heads ON changelog.node = heads.node'
212 )
212 )
213 ]
213 ]
214 if revs:
214 if revs:
215 return sorted([r for r in revs if r in realheads])
215 return sorted([r for r in revs if r in realheads])
216 return sorted(realheads)
216 return sorted(realheads)
217
217
218 def changelogrevision(self, nodeorrev):
218 def changelogrevision(self, nodeorrev):
219 # Ensure we have a node id
219 # Ensure we have a node id
220 if isinstance(nodeorrev, int):
220 if isinstance(nodeorrev, int):
221 n = self.node(nodeorrev)
221 n = self.node(nodeorrev)
222 else:
222 else:
223 n = nodeorrev
223 n = nodeorrev
224 extra = {b'branch': b'default'}
224 # handle looking up nullid
225 # handle looking up nullid
225 if n == nullid:
226 if n == nullid:
226 return hgchangelog._changelogrevision(extra={}, manifest=nullid)
227 return hgchangelog._changelogrevision(extra=extra, manifest=nullid)
227 hn = gitutil.togitnode(n)
228 hn = gitutil.togitnode(n)
228 # We've got a real commit!
229 # We've got a real commit!
229 files = [
230 files = [
230 r[0]
231 r[0]
231 for r in self._db.execute(
232 for r in self._db.execute(
232 'SELECT filename FROM changedfiles '
233 'SELECT filename FROM changedfiles '
233 'WHERE node = ? and filenode != ?',
234 'WHERE node = ? and filenode != ?',
234 (hn, gitutil.nullgit),
235 (hn, gitutil.nullgit),
235 )
236 )
236 ]
237 ]
237 filesremoved = [
238 filesremoved = [
238 r[0]
239 r[0]
239 for r in self._db.execute(
240 for r in self._db.execute(
240 'SELECT filename FROM changedfiles '
241 'SELECT filename FROM changedfiles '
241 'WHERE node = ? and filenode = ?',
242 'WHERE node = ? and filenode = ?',
242 (hn, gitutil.nullgit),
243 (hn, gitutil.nullgit),
243 )
244 )
244 ]
245 ]
245 c = self.gitrepo[hn]
246 c = self.gitrepo[hn]
246 return hgchangelog._changelogrevision(
247 return hgchangelog._changelogrevision(
247 manifest=n, # pretend manifest the same as the commit node
248 manifest=n, # pretend manifest the same as the commit node
248 user=b'%s <%s>'
249 user=b'%s <%s>'
249 % (c.author.name.encode('utf8'), c.author.email.encode('utf8')),
250 % (c.author.name.encode('utf8'), c.author.email.encode('utf8')),
250 date=(c.author.time, -c.author.offset * 60),
251 date=(c.author.time, -c.author.offset * 60),
251 files=files,
252 files=files,
252 # TODO filesadded in the index
253 # TODO filesadded in the index
253 filesremoved=filesremoved,
254 filesremoved=filesremoved,
254 description=c.message.encode('utf8'),
255 description=c.message.encode('utf8'),
255 # TODO do we want to handle extra? how?
256 # TODO do we want to handle extra? how?
256 extra={b'branch': b'default'},
257 extra=extra,
257 )
258 )
258
259
259 def ancestors(self, revs, stoprev=0, inclusive=False):
260 def ancestors(self, revs, stoprev=0, inclusive=False):
260 revs = list(revs)
261 revs = list(revs)
261 tip = self.rev(self.tip())
262 tip = self.rev(self.tip())
262 for r in revs:
263 for r in revs:
263 if r > tip:
264 if r > tip:
264 raise IndexError(b'Invalid rev %r' % r)
265 raise IndexError(b'Invalid rev %r' % r)
265 return ancestor.lazyancestors(
266 return ancestor.lazyancestors(
266 self.parentrevs, revs, stoprev=stoprev, inclusive=inclusive
267 self.parentrevs, revs, stoprev=stoprev, inclusive=inclusive
267 )
268 )
268
269
269 # Cleanup opportunity: this is *identical* to the revlog.py version
270 # Cleanup opportunity: this is *identical* to the revlog.py version
270 def descendants(self, revs):
271 def descendants(self, revs):
271 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
272 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
272
273
273 def incrementalmissingrevs(self, common=None):
274 def incrementalmissingrevs(self, common=None):
274 """Return an object that can be used to incrementally compute the
275 """Return an object that can be used to incrementally compute the
275 revision numbers of the ancestors of arbitrary sets that are not
276 revision numbers of the ancestors of arbitrary sets that are not
276 ancestors of common. This is an ancestor.incrementalmissingancestors
277 ancestors of common. This is an ancestor.incrementalmissingancestors
277 object.
278 object.
278
279
279 'common' is a list of revision numbers. If common is not supplied, uses
280 'common' is a list of revision numbers. If common is not supplied, uses
280 nullrev.
281 nullrev.
281 """
282 """
282 if common is None:
283 if common is None:
283 common = [nullrev]
284 common = [nullrev]
284
285
285 return ancestor.incrementalmissingancestors(self.parentrevs, common)
286 return ancestor.incrementalmissingancestors(self.parentrevs, common)
286
287
287 def findmissing(self, common=None, heads=None):
288 def findmissing(self, common=None, heads=None):
288 """Return the ancestors of heads that are not ancestors of common.
289 """Return the ancestors of heads that are not ancestors of common.
289
290
290 More specifically, return a list of nodes N such that every N
291 More specifically, return a list of nodes N such that every N
291 satisfies the following constraints:
292 satisfies the following constraints:
292
293
293 1. N is an ancestor of some node in 'heads'
294 1. N is an ancestor of some node in 'heads'
294 2. N is not an ancestor of any node in 'common'
295 2. N is not an ancestor of any node in 'common'
295
296
296 The list is sorted by revision number, meaning it is
297 The list is sorted by revision number, meaning it is
297 topologically sorted.
298 topologically sorted.
298
299
299 'heads' and 'common' are both lists of node IDs. If heads is
300 'heads' and 'common' are both lists of node IDs. If heads is
300 not supplied, uses all of the revlog's heads. If common is not
301 not supplied, uses all of the revlog's heads. If common is not
301 supplied, uses nullid."""
302 supplied, uses nullid."""
302 if common is None:
303 if common is None:
303 common = [nullid]
304 common = [nullid]
304 if heads is None:
305 if heads is None:
305 heads = self.heads()
306 heads = self.heads()
306
307
307 common = [self.rev(n) for n in common]
308 common = [self.rev(n) for n in common]
308 heads = [self.rev(n) for n in heads]
309 heads = [self.rev(n) for n in heads]
309
310
310 inc = self.incrementalmissingrevs(common=common)
311 inc = self.incrementalmissingrevs(common=common)
311 return [self.node(r) for r in inc.missingancestors(heads)]
312 return [self.node(r) for r in inc.missingancestors(heads)]
312
313
313 def children(self, node):
314 def children(self, node):
314 """find the children of a given node"""
315 """find the children of a given node"""
315 c = []
316 c = []
316 p = self.rev(node)
317 p = self.rev(node)
317 for r in self.revs(start=p + 1):
318 for r in self.revs(start=p + 1):
318 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
319 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
319 if prevs:
320 if prevs:
320 for pr in prevs:
321 for pr in prevs:
321 if pr == p:
322 if pr == p:
322 c.append(self.node(r))
323 c.append(self.node(r))
323 elif p == nullrev:
324 elif p == nullrev:
324 c.append(self.node(r))
325 c.append(self.node(r))
325 return c
326 return c
326
327
327 def reachableroots(self, minroot, heads, roots, includepath=False):
328 def reachableroots(self, minroot, heads, roots, includepath=False):
328 return dagop._reachablerootspure(
329 return dagop._reachablerootspure(
329 self.parentrevs, minroot, roots, heads, includepath
330 self.parentrevs, minroot, roots, heads, includepath
330 )
331 )
331
332
332 # Cleanup opportunity: this is *identical* to the revlog.py version
333 # Cleanup opportunity: this is *identical* to the revlog.py version
333 def isancestor(self, a, b):
334 def isancestor(self, a, b):
334 a, b = self.rev(a), self.rev(b)
335 a, b = self.rev(a), self.rev(b)
335 return self.isancestorrev(a, b)
336 return self.isancestorrev(a, b)
336
337
337 # Cleanup opportunity: this is *identical* to the revlog.py version
338 # Cleanup opportunity: this is *identical* to the revlog.py version
338 def isancestorrev(self, a, b):
339 def isancestorrev(self, a, b):
339 if a == nullrev:
340 if a == nullrev:
340 return True
341 return True
341 elif a == b:
342 elif a == b:
342 return True
343 return True
343 elif a > b:
344 elif a > b:
344 return False
345 return False
345 return bool(self.reachableroots(a, [b], [a], includepath=False))
346 return bool(self.reachableroots(a, [b], [a], includepath=False))
346
347
347 def parentrevs(self, rev):
348 def parentrevs(self, rev):
348 n = self.node(rev)
349 n = self.node(rev)
349 hn = gitutil.togitnode(n)
350 hn = gitutil.togitnode(n)
350 if hn != gitutil.nullgit:
351 if hn != gitutil.nullgit:
351 c = self.gitrepo[hn]
352 c = self.gitrepo[hn]
352 else:
353 else:
353 return nullrev, nullrev
354 return nullrev, nullrev
354 p1 = p2 = nullrev
355 p1 = p2 = nullrev
355 if c.parents:
356 if c.parents:
356 p1 = self.rev(c.parents[0].id.raw)
357 p1 = self.rev(c.parents[0].id.raw)
357 if len(c.parents) > 2:
358 if len(c.parents) > 2:
358 raise error.Abort(b'TODO octopus merge handling')
359 raise error.Abort(b'TODO octopus merge handling')
359 if len(c.parents) == 2:
360 if len(c.parents) == 2:
360 p2 = self.rev(c.parents[1].id.raw)
361 p2 = self.rev(c.parents[1].id.raw)
361 return p1, p2
362 return p1, p2
362
363
363 # Private method is used at least by the tags code.
364 # Private method is used at least by the tags code.
364 _uncheckedparentrevs = parentrevs
365 _uncheckedparentrevs = parentrevs
365
366
366 def commonancestorsheads(self, a, b):
367 def commonancestorsheads(self, a, b):
367 # TODO the revlog verson of this has a C path, so we probably
368 # TODO the revlog verson of this has a C path, so we probably
368 # need to optimize this...
369 # need to optimize this...
369 a, b = self.rev(a), self.rev(b)
370 a, b = self.rev(a), self.rev(b)
370 return [
371 return [
371 self.node(n)
372 self.node(n)
372 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
373 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
373 ]
374 ]
374
375
375 def branchinfo(self, rev):
376 def branchinfo(self, rev):
376 """Git doesn't do named branches, so just put everything on default."""
377 """Git doesn't do named branches, so just put everything on default."""
377 return b'default', False
378 return b'default', False
378
379
379 def delayupdate(self, tr):
380 def delayupdate(self, tr):
380 # TODO: I think we can elide this because we're just dropping
381 # TODO: I think we can elide this because we're just dropping
381 # an object in the git repo?
382 # an object in the git repo?
382 pass
383 pass
383
384
384 def add(
385 def add(
385 self,
386 self,
386 manifest,
387 manifest,
387 files,
388 files,
388 desc,
389 desc,
389 transaction,
390 transaction,
390 p1,
391 p1,
391 p2,
392 p2,
392 user,
393 user,
393 date=None,
394 date=None,
394 extra=None,
395 extra=None,
395 p1copies=None,
396 p1copies=None,
396 p2copies=None,
397 p2copies=None,
397 filesadded=None,
398 filesadded=None,
398 filesremoved=None,
399 filesremoved=None,
399 ):
400 ):
400 parents = []
401 parents = []
401 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
402 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
402 if p1 != nullid:
403 if p1 != nullid:
403 parents.append(hp1)
404 parents.append(hp1)
404 if p2 and p2 != nullid:
405 if p2 and p2 != nullid:
405 parents.append(hp2)
406 parents.append(hp2)
406 assert date is not None
407 assert date is not None
407 timestamp, tz = date
408 timestamp, tz = date
408 sig = pygit2.Signature(
409 sig = pygit2.Signature(
409 encoding.unifromlocal(stringutil.person(user)),
410 encoding.unifromlocal(stringutil.person(user)),
410 encoding.unifromlocal(stringutil.email(user)),
411 encoding.unifromlocal(stringutil.email(user)),
411 int(timestamp),
412 int(timestamp),
412 -int(tz // 60),
413 -int(tz // 60),
413 )
414 )
414 oid = self.gitrepo.create_commit(
415 oid = self.gitrepo.create_commit(
415 None, sig, sig, desc, gitutil.togitnode(manifest), parents
416 None, sig, sig, desc, gitutil.togitnode(manifest), parents
416 )
417 )
417 # Set up an internal reference to force the commit into the
418 # Set up an internal reference to force the commit into the
418 # changelog. Hypothetically, we could even use this refs/hg/
419 # changelog. Hypothetically, we could even use this refs/hg/
419 # namespace to allow for anonymous heads on git repos, which
420 # namespace to allow for anonymous heads on git repos, which
420 # would be neat.
421 # would be neat.
421 self.gitrepo.references.create(
422 self.gitrepo.references.create(
422 'refs/hg/internal/latest-commit', oid, force=True
423 'refs/hg/internal/latest-commit', oid, force=True
423 )
424 )
424 # Reindex now to pick up changes. We omit the progress
425 # Reindex now to pick up changes. We omit the progress
425 # and log callbacks because this will be very quick.
426 # and log callbacks because this will be very quick.
426 index._index_repo(self.gitrepo, self._db)
427 index._index_repo(self.gitrepo, self._db)
427 return oid.raw
428 return oid.raw
428
429
429
430
430 class manifestlog(baselog):
431 class manifestlog(baselog):
431 nodeconstants = sha1nodeconstants
432 nodeconstants = sha1nodeconstants
432
433
433 def __getitem__(self, node):
434 def __getitem__(self, node):
434 return self.get(b'', node)
435 return self.get(b'', node)
435
436
436 def get(self, relpath, node):
437 def get(self, relpath, node):
437 if node == nullid:
438 if node == nullid:
438 # TODO: this should almost certainly be a memgittreemanifestctx
439 # TODO: this should almost certainly be a memgittreemanifestctx
439 return manifest.memtreemanifestctx(self, relpath)
440 return manifest.memtreemanifestctx(self, relpath)
440 commit = self.gitrepo[gitutil.togitnode(node)]
441 commit = self.gitrepo[gitutil.togitnode(node)]
441 t = commit.tree
442 t = commit.tree
442 if relpath:
443 if relpath:
443 parts = relpath.split(b'/')
444 parts = relpath.split(b'/')
444 for p in parts:
445 for p in parts:
445 te = t[p]
446 te = t[p]
446 t = self.gitrepo[te.id]
447 t = self.gitrepo[te.id]
447 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
448 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
448
449
449
450
450 @interfaceutil.implementer(repository.ifilestorage)
451 @interfaceutil.implementer(repository.ifilestorage)
451 class filelog(baselog):
452 class filelog(baselog):
452 def __init__(self, gr, db, path):
453 def __init__(self, gr, db, path):
453 super(filelog, self).__init__(gr, db)
454 super(filelog, self).__init__(gr, db)
454 assert isinstance(path, bytes)
455 assert isinstance(path, bytes)
455 self.path = path
456 self.path = path
456
457
457 def read(self, node):
458 def read(self, node):
458 if node == nullid:
459 if node == nullid:
459 return b''
460 return b''
460 return self.gitrepo[gitutil.togitnode(node)].data
461 return self.gitrepo[gitutil.togitnode(node)].data
461
462
462 def lookup(self, node):
463 def lookup(self, node):
463 if len(node) not in (20, 40):
464 if len(node) not in (20, 40):
464 node = int(node)
465 node = int(node)
465 if isinstance(node, int):
466 if isinstance(node, int):
466 assert False, b'todo revnums for nodes'
467 assert False, b'todo revnums for nodes'
467 if len(node) == 40:
468 if len(node) == 40:
468 node = bin(node)
469 node = bin(node)
469 hnode = gitutil.togitnode(node)
470 hnode = gitutil.togitnode(node)
470 if hnode in self.gitrepo:
471 if hnode in self.gitrepo:
471 return node
472 return node
472 raise error.LookupError(self.path, node, _(b'no match found'))
473 raise error.LookupError(self.path, node, _(b'no match found'))
473
474
474 def cmp(self, node, text):
475 def cmp(self, node, text):
475 """Returns True if text is different than content at `node`."""
476 """Returns True if text is different than content at `node`."""
476 return self.read(node) != text
477 return self.read(node) != text
477
478
478 def add(self, text, meta, transaction, link, p1=None, p2=None):
479 def add(self, text, meta, transaction, link, p1=None, p2=None):
479 assert not meta # Should we even try to handle this?
480 assert not meta # Should we even try to handle this?
480 return self.gitrepo.create_blob(text).raw
481 return self.gitrepo.create_blob(text).raw
481
482
482 def __iter__(self):
483 def __iter__(self):
483 for clrev in self._db.execute(
484 for clrev in self._db.execute(
484 '''
485 '''
485 SELECT rev FROM changelog
486 SELECT rev FROM changelog
486 INNER JOIN changedfiles ON changelog.node = changedfiles.node
487 INNER JOIN changedfiles ON changelog.node = changedfiles.node
487 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
488 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
488 ''',
489 ''',
489 (pycompat.fsdecode(self.path), gitutil.nullgit),
490 (pycompat.fsdecode(self.path), gitutil.nullgit),
490 ):
491 ):
491 yield clrev[0]
492 yield clrev[0]
492
493
493 def linkrev(self, fr):
494 def linkrev(self, fr):
494 return fr
495 return fr
495
496
496 def rev(self, node):
497 def rev(self, node):
497 row = self._db.execute(
498 row = self._db.execute(
498 '''
499 '''
499 SELECT rev FROM changelog
500 SELECT rev FROM changelog
500 INNER JOIN changedfiles ON changelog.node = changedfiles.node
501 INNER JOIN changedfiles ON changelog.node = changedfiles.node
501 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
502 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
502 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
503 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
503 ).fetchone()
504 ).fetchone()
504 if row is None:
505 if row is None:
505 raise error.LookupError(self.path, node, _(b'no such node'))
506 raise error.LookupError(self.path, node, _(b'no such node'))
506 return int(row[0])
507 return int(row[0])
507
508
508 def node(self, rev):
509 def node(self, rev):
509 maybe = self._db.execute(
510 maybe = self._db.execute(
510 '''SELECT filenode FROM changedfiles
511 '''SELECT filenode FROM changedfiles
511 INNER JOIN changelog ON changelog.node = changedfiles.node
512 INNER JOIN changelog ON changelog.node = changedfiles.node
512 WHERE changelog.rev = ? AND filename = ?
513 WHERE changelog.rev = ? AND filename = ?
513 ''',
514 ''',
514 (rev, pycompat.fsdecode(self.path)),
515 (rev, pycompat.fsdecode(self.path)),
515 ).fetchone()
516 ).fetchone()
516 if maybe is None:
517 if maybe is None:
517 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
518 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
518 return bin(maybe[0])
519 return bin(maybe[0])
519
520
520 def parents(self, node):
521 def parents(self, node):
521 gn = gitutil.togitnode(node)
522 gn = gitutil.togitnode(node)
522 gp = pycompat.fsdecode(self.path)
523 gp = pycompat.fsdecode(self.path)
523 ps = []
524 ps = []
524 for p in self._db.execute(
525 for p in self._db.execute(
525 '''SELECT p1filenode, p2filenode FROM changedfiles
526 '''SELECT p1filenode, p2filenode FROM changedfiles
526 WHERE filenode = ? AND filename = ?
527 WHERE filenode = ? AND filename = ?
527 ''',
528 ''',
528 (gn, gp),
529 (gn, gp),
529 ).fetchone():
530 ).fetchone():
530 if p is None:
531 if p is None:
531 commit = self._db.execute(
532 commit = self._db.execute(
532 "SELECT node FROM changedfiles "
533 "SELECT node FROM changedfiles "
533 "WHERE filenode = ? AND filename = ?",
534 "WHERE filenode = ? AND filename = ?",
534 (gn, gp),
535 (gn, gp),
535 ).fetchone()[0]
536 ).fetchone()[0]
536 # This filelog is missing some data. Build the
537 # This filelog is missing some data. Build the
537 # filelog, then recurse (which will always find data).
538 # filelog, then recurse (which will always find data).
538 if pycompat.ispy3:
539 if pycompat.ispy3:
539 commit = commit.decode('ascii')
540 commit = commit.decode('ascii')
540 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
541 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
541 return self.parents(node)
542 return self.parents(node)
542 else:
543 else:
543 ps.append(bin(p))
544 ps.append(bin(p))
544 return ps
545 return ps
545
546
546 def renamed(self, node):
547 def renamed(self, node):
547 # TODO: renames/copies
548 # TODO: renames/copies
548 return False
549 return False
@@ -1,418 +1,424 b''
1 #require pygit2 no-windows
1 #require pygit2 no-windows
2
2
3 Setup:
3 Setup:
4 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
4 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
5 > GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
5 > GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
6 > GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
6 > GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
7 > GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
7 > GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
8 > GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
8 > GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
9 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
9 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
10 > count=10
10 > count=10
11 > gitcommit() {
11 > gitcommit() {
12 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000";
12 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000";
13 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
13 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
14 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
14 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
15 > count=`expr $count + 1`
15 > count=`expr $count + 1`
16 > }
16 > }
17 $ git config --global init.defaultBranch master
17 $ git config --global init.defaultBranch master
18
18
19
19
20 $ hg version -v --config extensions.git= | grep '^[E ]'
20 $ hg version -v --config extensions.git= | grep '^[E ]'
21 Enabled extensions:
21 Enabled extensions:
22 git internal (pygit2 *) (glob)
22 git internal (pygit2 *) (glob)
23
23
24 Test auto-loading extension works:
24 Test auto-loading extension works:
25 $ mkdir nogit
25 $ mkdir nogit
26 $ cd nogit
26 $ cd nogit
27 $ mkdir .hg
27 $ mkdir .hg
28 $ echo git >> .hg/requires
28 $ echo git >> .hg/requires
29 $ hg status
29 $ hg status
30 abort: repository specified git format in .hg/requires but has no .git directory
30 abort: repository specified git format in .hg/requires but has no .git directory
31 [255]
31 [255]
32 $ git config --global init.defaultBranch master
32 $ git config --global init.defaultBranch master
33 $ git init
33 $ git init
34 Initialized empty Git repository in $TESTTMP/nogit/.git/
34 Initialized empty Git repository in $TESTTMP/nogit/.git/
35 This status invocation shows some hg gunk because we didn't use
35 This status invocation shows some hg gunk because we didn't use
36 `hg init --git`, which fixes up .git/info/exclude for us.
36 `hg init --git`, which fixes up .git/info/exclude for us.
37 $ hg status
37 $ hg status
38 ? .hg/cache/git-commits.sqlite
38 ? .hg/cache/git-commits.sqlite
39 ? .hg/cache/git-commits.sqlite-shm
39 ? .hg/cache/git-commits.sqlite-shm
40 ? .hg/cache/git-commits.sqlite-wal
40 ? .hg/cache/git-commits.sqlite-wal
41 ? .hg/requires
41 ? .hg/requires
42 $ cd ..
42 $ cd ..
43
43
44 Now globally enable extension for the rest of the test:
44 Now globally enable extension for the rest of the test:
45 $ cat <<EOF >> $HGRCPATH
45 $ cat <<EOF >> $HGRCPATH
46 > [extensions]
46 > [extensions]
47 > git=
47 > git=
48 > [git]
48 > [git]
49 > log-index-cache-miss = yes
49 > log-index-cache-miss = yes
50 > EOF
50 > EOF
51
51
52 Test some edge cases around a commitless repo first
52 Test some edge cases around a commitless repo first
53 $ mkdir empty
53 $ mkdir empty
54 $ cd empty
54 $ cd empty
55 $ git init
55 $ git init
56 Initialized empty Git repository in $TESTTMP/empty/.git/
56 Initialized empty Git repository in $TESTTMP/empty/.git/
57 $ hg init --git
57 $ hg init --git
58 $ hg heads
58 $ hg heads
59 [1]
59 [1]
60 $ hg tip
61 changeset: -1:000000000000
62 tag: tip
63 user:
64 date: Thu Jan 01 00:00:00 1970 +0000
65
60 $ cd ..
66 $ cd ..
61
67
62 Make a new repo with git:
68 Make a new repo with git:
63 $ mkdir foo
69 $ mkdir foo
64 $ cd foo
70 $ cd foo
65 $ git init
71 $ git init
66 Initialized empty Git repository in $TESTTMP/foo/.git/
72 Initialized empty Git repository in $TESTTMP/foo/.git/
67 Ignore the .hg directory within git:
73 Ignore the .hg directory within git:
68 $ echo .hg >> .git/info/exclude
74 $ echo .hg >> .git/info/exclude
69 $ echo alpha > alpha
75 $ echo alpha > alpha
70 $ git add alpha
76 $ git add alpha
71 $ gitcommit -am 'Add alpha'
77 $ gitcommit -am 'Add alpha'
72 $ echo beta > beta
78 $ echo beta > beta
73 $ git add beta
79 $ git add beta
74 $ gitcommit -am 'Add beta'
80 $ gitcommit -am 'Add beta'
75 $ echo gamma > gamma
81 $ echo gamma > gamma
76 $ git status
82 $ git status
77 On branch master
83 On branch master
78 Untracked files:
84 Untracked files:
79 (use "git add <file>..." to include in what will be committed)
85 (use "git add <file>..." to include in what will be committed)
80 (?)
86 (?)
81 gamma
87 gamma
82
88
83 nothing added to commit but untracked files present (use "git add" to track)
89 nothing added to commit but untracked files present (use "git add" to track)
84
90
85 Without creating the .hg, hg status fails:
91 Without creating the .hg, hg status fails:
86 $ hg status
92 $ hg status
87 abort: no repository found in '$TESTTMP/foo' (.hg not found)
93 abort: no repository found in '$TESTTMP/foo' (.hg not found)
88 [10]
94 [10]
89 But if you run hg init --git, it works:
95 But if you run hg init --git, it works:
90 $ hg init --git
96 $ hg init --git
91 $ hg id --traceback
97 $ hg id --traceback
92 heads mismatch, rebuilding dagcache
98 heads mismatch, rebuilding dagcache
93 3d9be8deba43 tip master
99 3d9be8deba43 tip master
94 $ hg status
100 $ hg status
95 ? gamma
101 ? gamma
96 Log works too:
102 Log works too:
97 $ hg log
103 $ hg log
98 changeset: 1:3d9be8deba43
104 changeset: 1:3d9be8deba43
99 bookmark: master
105 bookmark: master
100 tag: tip
106 tag: tip
101 user: test <test@example.org>
107 user: test <test@example.org>
102 date: Mon Jan 01 00:00:11 2007 +0000
108 date: Mon Jan 01 00:00:11 2007 +0000
103 summary: Add beta
109 summary: Add beta
104
110
105 changeset: 0:c5864c9d16fb
111 changeset: 0:c5864c9d16fb
106 user: test <test@example.org>
112 user: test <test@example.org>
107 date: Mon Jan 01 00:00:10 2007 +0000
113 date: Mon Jan 01 00:00:10 2007 +0000
108 summary: Add alpha
114 summary: Add alpha
109
115
110
116
111
117
112 and bookmarks:
118 and bookmarks:
113 $ hg bookmarks
119 $ hg bookmarks
114 * master 1:3d9be8deba43
120 * master 1:3d9be8deba43
115
121
116 diff even works transparently in both systems:
122 diff even works transparently in both systems:
117 $ echo blah >> alpha
123 $ echo blah >> alpha
118 $ git diff
124 $ git diff
119 diff --git a/alpha b/alpha
125 diff --git a/alpha b/alpha
120 index 4a58007..faed1b7 100644
126 index 4a58007..faed1b7 100644
121 --- a/alpha
127 --- a/alpha
122 +++ b/alpha
128 +++ b/alpha
123 @@ -1* +1,2 @@ (glob)
129 @@ -1* +1,2 @@ (glob)
124 alpha
130 alpha
125 +blah
131 +blah
126 $ hg diff --git
132 $ hg diff --git
127 diff --git a/alpha b/alpha
133 diff --git a/alpha b/alpha
128 --- a/alpha
134 --- a/alpha
129 +++ b/alpha
135 +++ b/alpha
130 @@ -1,1 +1,2 @@
136 @@ -1,1 +1,2 @@
131 alpha
137 alpha
132 +blah
138 +blah
133
139
134 Remove a file, it shows as such:
140 Remove a file, it shows as such:
135 $ rm alpha
141 $ rm alpha
136 $ hg status
142 $ hg status
137 ! alpha
143 ! alpha
138 ? gamma
144 ? gamma
139
145
140 Revert works:
146 Revert works:
141 $ hg revert alpha --traceback
147 $ hg revert alpha --traceback
142 $ hg status
148 $ hg status
143 ? gamma
149 ? gamma
144 $ git status
150 $ git status
145 On branch master
151 On branch master
146 Untracked files:
152 Untracked files:
147 (use "git add <file>..." to include in what will be committed)
153 (use "git add <file>..." to include in what will be committed)
148 (?)
154 (?)
149 gamma
155 gamma
150
156
151 nothing added to commit but untracked files present (use "git add" to track)
157 nothing added to commit but untracked files present (use "git add" to track)
152
158
153 Add shows sanely in both:
159 Add shows sanely in both:
154 $ hg add gamma
160 $ hg add gamma
155 $ hg status
161 $ hg status
156 A gamma
162 A gamma
157 $ hg files
163 $ hg files
158 alpha
164 alpha
159 beta
165 beta
160 gamma
166 gamma
161 $ git ls-files
167 $ git ls-files
162 alpha
168 alpha
163 beta
169 beta
164 gamma
170 gamma
165 $ git status
171 $ git status
166 On branch master
172 On branch master
167 Changes to be committed:
173 Changes to be committed:
168 (use "git restore --staged <file>..." to unstage) (?)
174 (use "git restore --staged <file>..." to unstage) (?)
169 (use "git reset HEAD <file>..." to unstage) (?)
175 (use "git reset HEAD <file>..." to unstage) (?)
170 (?)
176 (?)
171 new file: gamma
177 new file: gamma
172
178
173
179
174 forget does what it should as well:
180 forget does what it should as well:
175 $ hg forget gamma
181 $ hg forget gamma
176 $ hg status
182 $ hg status
177 ? gamma
183 ? gamma
178 $ git status
184 $ git status
179 On branch master
185 On branch master
180 Untracked files:
186 Untracked files:
181 (use "git add <file>..." to include in what will be committed)
187 (use "git add <file>..." to include in what will be committed)
182 (?)
188 (?)
183 gamma
189 gamma
184
190
185 nothing added to commit but untracked files present (use "git add" to track)
191 nothing added to commit but untracked files present (use "git add" to track)
186
192
187 clean up untracked file
193 clean up untracked file
188 $ rm gamma
194 $ rm gamma
189
195
190 hg log FILE
196 hg log FILE
191
197
192 $ echo a >> alpha
198 $ echo a >> alpha
193 $ hg ci -m 'more alpha' --traceback --date '1583522787 18000'
199 $ hg ci -m 'more alpha' --traceback --date '1583522787 18000'
194 $ echo b >> beta
200 $ echo b >> beta
195 $ hg ci -m 'more beta'
201 $ hg ci -m 'more beta'
196 heads mismatch, rebuilding dagcache
202 heads mismatch, rebuilding dagcache
197 $ echo a >> alpha
203 $ echo a >> alpha
198 $ hg ci -m 'even more alpha'
204 $ hg ci -m 'even more alpha'
199 heads mismatch, rebuilding dagcache
205 heads mismatch, rebuilding dagcache
200 $ hg log -G alpha
206 $ hg log -G alpha
201 heads mismatch, rebuilding dagcache
207 heads mismatch, rebuilding dagcache
202 @ changeset: 4:6626247b7dc8
208 @ changeset: 4:6626247b7dc8
203 : bookmark: master
209 : bookmark: master
204 : tag: tip
210 : tag: tip
205 : user: test <test>
211 : user: test <test>
206 : date: Thu Jan 01 00:00:00 1970 +0000
212 : date: Thu Jan 01 00:00:00 1970 +0000
207 : summary: even more alpha
213 : summary: even more alpha
208 :
214 :
209 o changeset: 2:a1983dd7fb19
215 o changeset: 2:a1983dd7fb19
210 : user: test <test>
216 : user: test <test>
211 : date: Fri Mar 06 14:26:27 2020 -0500
217 : date: Fri Mar 06 14:26:27 2020 -0500
212 : summary: more alpha
218 : summary: more alpha
213 :
219 :
214 o changeset: 0:c5864c9d16fb
220 o changeset: 0:c5864c9d16fb
215 user: test <test@example.org>
221 user: test <test@example.org>
216 date: Mon Jan 01 00:00:10 2007 +0000
222 date: Mon Jan 01 00:00:10 2007 +0000
217 summary: Add alpha
223 summary: Add alpha
218
224
219 $ hg log -G beta
225 $ hg log -G beta
220 o changeset: 3:d8ee22687733
226 o changeset: 3:d8ee22687733
221 : user: test <test>
227 : user: test <test>
222 : date: Thu Jan 01 00:00:00 1970 +0000
228 : date: Thu Jan 01 00:00:00 1970 +0000
223 : summary: more beta
229 : summary: more beta
224 :
230 :
225 o changeset: 1:3d9be8deba43
231 o changeset: 1:3d9be8deba43
226 | user: test <test@example.org>
232 | user: test <test@example.org>
227 ~ date: Mon Jan 01 00:00:11 2007 +0000
233 ~ date: Mon Jan 01 00:00:11 2007 +0000
228 summary: Add beta
234 summary: Add beta
229
235
230
236
231 $ hg log -r "children(3d9be8deba43)" -T"{node|short} {children}\n"
237 $ hg log -r "children(3d9be8deba43)" -T"{node|short} {children}\n"
232 a1983dd7fb19 3:d8ee22687733
238 a1983dd7fb19 3:d8ee22687733
233
239
234 hg annotate
240 hg annotate
235
241
236 $ hg annotate alpha
242 $ hg annotate alpha
237 0: alpha
243 0: alpha
238 2: a
244 2: a
239 4: a
245 4: a
240 $ hg annotate beta
246 $ hg annotate beta
241 1: beta
247 1: beta
242 3: b
248 3: b
243
249
244
250
245 Files in subdirectories. TODO: case-folding support, make this `A`
251 Files in subdirectories. TODO: case-folding support, make this `A`
246 instead of `a`.
252 instead of `a`.
247
253
248 $ mkdir a
254 $ mkdir a
249 $ echo "This is file mu." > a/mu
255 $ echo "This is file mu." > a/mu
250 $ hg ci -A -m 'Introduce file a/mu'
256 $ hg ci -A -m 'Introduce file a/mu'
251 adding a/mu
257 adding a/mu
252
258
253 Both hg and git agree a/mu is part of the repo
259 Both hg and git agree a/mu is part of the repo
254
260
255 $ git ls-files
261 $ git ls-files
256 a/mu
262 a/mu
257 alpha
263 alpha
258 beta
264 beta
259 $ hg files
265 $ hg files
260 a/mu
266 a/mu
261 alpha
267 alpha
262 beta
268 beta
263
269
264 hg and git status both clean
270 hg and git status both clean
265
271
266 $ git status
272 $ git status
267 On branch master
273 On branch master
268 nothing to commit, working tree clean
274 nothing to commit, working tree clean
269 $ hg status
275 $ hg status
270 heads mismatch, rebuilding dagcache
276 heads mismatch, rebuilding dagcache
271
277
272
278
273 node|shortest works correctly
279 node|shortest works correctly
274 $ hg log -T '{node}\n' | sort
280 $ hg log -T '{node}\n' | sort
275 3d9be8deba43482be2c81a4cb4be1f10d85fa8bc
281 3d9be8deba43482be2c81a4cb4be1f10d85fa8bc
276 6626247b7dc8f231b183b8a4761c89139baca2ad
282 6626247b7dc8f231b183b8a4761c89139baca2ad
277 a1983dd7fb19cbd83ad5a1c2fc8bf3d775dea12f
283 a1983dd7fb19cbd83ad5a1c2fc8bf3d775dea12f
278 ae1ab744f95bfd5b07cf573baef98a778058537b
284 ae1ab744f95bfd5b07cf573baef98a778058537b
279 c5864c9d16fb3431fe2c175ff84dc6accdbb2c18
285 c5864c9d16fb3431fe2c175ff84dc6accdbb2c18
280 d8ee22687733a1991813560b15128cd9734f4b48
286 d8ee22687733a1991813560b15128cd9734f4b48
281 $ hg log -r ae1ab744f95bfd5b07cf573baef98a778058537b --template "{shortest(node,1)}\n"
287 $ hg log -r ae1ab744f95bfd5b07cf573baef98a778058537b --template "{shortest(node,1)}\n"
282 ae
288 ae
283
289
284 This covers gitlog._partialmatch()
290 This covers gitlog._partialmatch()
285 $ hg log -r a
291 $ hg log -r a
286 abort: ambiguous revision identifier: a
292 abort: ambiguous revision identifier: a
287 [10]
293 [10]
288 $ hg log -r a1
294 $ hg log -r a1
289 changeset: 2:a1983dd7fb19
295 changeset: 2:a1983dd7fb19
290 user: test <test>
296 user: test <test>
291 date: Fri Mar 06 14:26:27 2020 -0500
297 date: Fri Mar 06 14:26:27 2020 -0500
292 summary: more alpha
298 summary: more alpha
293
299
294 $ hg log -r dead
300 $ hg log -r dead
295 abort: unknown revision 'dead'
301 abort: unknown revision 'dead'
296 [255]
302 [255]
297
303
298 This coveres changelog.findmissing()
304 This coveres changelog.findmissing()
299 $ hg merge --preview 3d9be8deba43
305 $ hg merge --preview 3d9be8deba43
300
306
301 This covers manifest.diff()
307 This covers manifest.diff()
302 $ hg diff -c 3d9be8deba43
308 $ hg diff -c 3d9be8deba43
303 diff -r c5864c9d16fb -r 3d9be8deba43 beta
309 diff -r c5864c9d16fb -r 3d9be8deba43 beta
304 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
310 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
305 +++ b/beta Mon Jan 01 00:00:11 2007 +0000
311 +++ b/beta Mon Jan 01 00:00:11 2007 +0000
306 @@ -0,0 +1,1 @@
312 @@ -0,0 +1,1 @@
307 +beta
313 +beta
308
314
309
315
310 Interactive commit should work as expected
316 Interactive commit should work as expected
311
317
312 $ echo bar >> alpha
318 $ echo bar >> alpha
313 $ echo bar >> beta
319 $ echo bar >> beta
314 $ hg commit -m "test interactive commit" -i --config ui.interactive=true --config ui.interface=text << EOF
320 $ hg commit -m "test interactive commit" -i --config ui.interactive=true --config ui.interface=text << EOF
315 > y
321 > y
316 > y
322 > y
317 > n
323 > n
318 > EOF
324 > EOF
319 diff --git a/alpha b/alpha
325 diff --git a/alpha b/alpha
320 1 hunks, 1 lines changed
326 1 hunks, 1 lines changed
321 examine changes to 'alpha'?
327 examine changes to 'alpha'?
322 (enter ? for help) [Ynesfdaq?] y
328 (enter ? for help) [Ynesfdaq?] y
323
329
324 @@ -1,3 +1,4 @@
330 @@ -1,3 +1,4 @@
325 alpha
331 alpha
326 a
332 a
327 a
333 a
328 +bar
334 +bar
329 record change 1/2 to 'alpha'?
335 record change 1/2 to 'alpha'?
330 (enter ? for help) [Ynesfdaq?] y
336 (enter ? for help) [Ynesfdaq?] y
331
337
332 diff --git a/beta b/beta
338 diff --git a/beta b/beta
333 1 hunks, 1 lines changed
339 1 hunks, 1 lines changed
334 examine changes to 'beta'?
340 examine changes to 'beta'?
335 (enter ? for help) [Ynesfdaq?] n
341 (enter ? for help) [Ynesfdaq?] n
336
342
337 Status should be consistent for both systems
343 Status should be consistent for both systems
338
344
339 $ hg status
345 $ hg status
340 heads mismatch, rebuilding dagcache
346 heads mismatch, rebuilding dagcache
341 M beta
347 M beta
342 $ git status | egrep -v '^$|^ \(use '
348 $ git status | egrep -v '^$|^ \(use '
343 On branch master
349 On branch master
344 Changes not staged for commit:
350 Changes not staged for commit:
345 modified: beta
351 modified: beta
346 no changes added to commit (use "git add" and/or "git commit -a")
352 no changes added to commit (use "git add" and/or "git commit -a")
347
353
348 Contents of each commit should be the same
354 Contents of each commit should be the same
349
355
350 $ hg ex -r .
356 $ hg ex -r .
351 # HG changeset patch
357 # HG changeset patch
352 # User test <test>
358 # User test <test>
353 # Date 0 0
359 # Date 0 0
354 # Thu Jan 01 00:00:00 1970 +0000
360 # Thu Jan 01 00:00:00 1970 +0000
355 # Node ID 80adc61cf57e99f6a412d83fee6239d1556cefcf
361 # Node ID 80adc61cf57e99f6a412d83fee6239d1556cefcf
356 # Parent ae1ab744f95bfd5b07cf573baef98a778058537b
362 # Parent ae1ab744f95bfd5b07cf573baef98a778058537b
357 test interactive commit
363 test interactive commit
358
364
359 diff -r ae1ab744f95b -r 80adc61cf57e alpha
365 diff -r ae1ab744f95b -r 80adc61cf57e alpha
360 --- a/alpha Thu Jan 01 00:00:00 1970 +0000
366 --- a/alpha Thu Jan 01 00:00:00 1970 +0000
361 +++ b/alpha Thu Jan 01 00:00:00 1970 +0000
367 +++ b/alpha Thu Jan 01 00:00:00 1970 +0000
362 @@ -1,3 +1,4 @@
368 @@ -1,3 +1,4 @@
363 alpha
369 alpha
364 a
370 a
365 a
371 a
366 +bar
372 +bar
367 $ git show
373 $ git show
368 commit 80adc61cf57e99f6a412d83fee6239d1556cefcf
374 commit 80adc61cf57e99f6a412d83fee6239d1556cefcf
369 Author: test <test>
375 Author: test <test>
370 Date: Thu Jan 1 00:00:00 1970 +0000
376 Date: Thu Jan 1 00:00:00 1970 +0000
371
377
372 test interactive commit
378 test interactive commit
373
379
374 diff --git a/alpha b/alpha
380 diff --git a/alpha b/alpha
375 index d112a75..d2a2e9a 100644
381 index d112a75..d2a2e9a 100644
376 --- a/alpha
382 --- a/alpha
377 +++ b/alpha
383 +++ b/alpha
378 @@ -1,3 +1,4 @@
384 @@ -1,3 +1,4 @@
379 alpha
385 alpha
380 a
386 a
381 a
387 a
382 +bar
388 +bar
383
389
384 Deleting files should also work (this was issue6398)
390 Deleting files should also work (this was issue6398)
385 $ hg revert -r . --all
391 $ hg revert -r . --all
386 reverting beta
392 reverting beta
387 $ hg rm beta
393 $ hg rm beta
388 $ hg ci -m 'remove beta'
394 $ hg ci -m 'remove beta'
389
395
390 This covers changelog.tiprev() (issue6510)
396 This covers changelog.tiprev() (issue6510)
391 $ hg log -r '(.^^):'
397 $ hg log -r '(.^^):'
392 heads mismatch, rebuilding dagcache
398 heads mismatch, rebuilding dagcache
393 changeset: 5:ae1ab744f95b
399 changeset: 5:ae1ab744f95b
394 user: test <test>
400 user: test <test>
395 date: Thu Jan 01 00:00:00 1970 +0000
401 date: Thu Jan 01 00:00:00 1970 +0000
396 summary: Introduce file a/mu
402 summary: Introduce file a/mu
397
403
398 changeset: 6:80adc61cf57e
404 changeset: 6:80adc61cf57e
399 user: test <test>
405 user: test <test>
400 date: Thu Jan 01 00:00:00 1970 +0000
406 date: Thu Jan 01 00:00:00 1970 +0000
401 summary: test interactive commit
407 summary: test interactive commit
402
408
403 changeset: 7:116aee5ecdff
409 changeset: 7:116aee5ecdff
404 bookmark: master
410 bookmark: master
405 tag: tip
411 tag: tip
406 user: test <test>
412 user: test <test>
407 date: Thu Jan 01 00:00:00 1970 +0000
413 date: Thu Jan 01 00:00:00 1970 +0000
408 summary: remove beta
414 summary: remove beta
409
415
410 This covers changelog.headrevs() with a non-None arg
416 This covers changelog.headrevs() with a non-None arg
411
417
412 TODO: Fix the user name. The stray > char also break `hg ci --amend`.
418 TODO: Fix the user name. The stray > char also break `hg ci --amend`.
413 $ hg log -r 'heads(.)' -Tcompact
419 $ hg log -r 'heads(.)' -Tcompact
414 7[tip][master] 116aee5ecdff 1970-01-01 00:00 +0000 test>
420 7[tip][master] 116aee5ecdff 1970-01-01 00:00 +0000 test>
415 remove beta
421 remove beta
416
422
417
423
418
424
General Comments 0
You need to be logged in to leave comments. Login now