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