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