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