##// END OF EJS Templates
obsfate: rename obsfate into obsolete in changeset_printer...
Boris Feld -
r34901:3a3adbcb default
parent child Browse files
Show More
@@ -1,3965 +1,3965 b''
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import itertools
12 12 import os
13 13 import re
14 14 import tempfile
15 15
16 16 from .i18n import _
17 17 from .node import (
18 18 hex,
19 19 nullid,
20 20 nullrev,
21 21 short,
22 22 )
23 23
24 24 from . import (
25 25 bookmarks,
26 26 changelog,
27 27 copies,
28 28 crecord as crecordmod,
29 29 dagop,
30 30 dirstateguard,
31 31 encoding,
32 32 error,
33 33 formatter,
34 34 graphmod,
35 35 match as matchmod,
36 36 mdiff,
37 37 obsolete,
38 38 patch,
39 39 pathutil,
40 40 pycompat,
41 41 registrar,
42 42 revlog,
43 43 revset,
44 44 scmutil,
45 45 smartset,
46 46 templatekw,
47 47 templater,
48 48 util,
49 49 vfs as vfsmod,
50 50 )
51 51 stringio = util.stringio
52 52
53 53 # templates of common command options
54 54
55 55 dryrunopts = [
56 56 ('n', 'dry-run', None,
57 57 _('do not perform actions, just print output')),
58 58 ]
59 59
60 60 remoteopts = [
61 61 ('e', 'ssh', '',
62 62 _('specify ssh command to use'), _('CMD')),
63 63 ('', 'remotecmd', '',
64 64 _('specify hg command to run on the remote side'), _('CMD')),
65 65 ('', 'insecure', None,
66 66 _('do not verify server certificate (ignoring web.cacerts config)')),
67 67 ]
68 68
69 69 walkopts = [
70 70 ('I', 'include', [],
71 71 _('include names matching the given patterns'), _('PATTERN')),
72 72 ('X', 'exclude', [],
73 73 _('exclude names matching the given patterns'), _('PATTERN')),
74 74 ]
75 75
76 76 commitopts = [
77 77 ('m', 'message', '',
78 78 _('use text as commit message'), _('TEXT')),
79 79 ('l', 'logfile', '',
80 80 _('read commit message from file'), _('FILE')),
81 81 ]
82 82
83 83 commitopts2 = [
84 84 ('d', 'date', '',
85 85 _('record the specified date as commit date'), _('DATE')),
86 86 ('u', 'user', '',
87 87 _('record the specified user as committer'), _('USER')),
88 88 ]
89 89
90 90 # hidden for now
91 91 formatteropts = [
92 92 ('T', 'template', '',
93 93 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
94 94 ]
95 95
96 96 templateopts = [
97 97 ('', 'style', '',
98 98 _('display using template map file (DEPRECATED)'), _('STYLE')),
99 99 ('T', 'template', '',
100 100 _('display with template'), _('TEMPLATE')),
101 101 ]
102 102
103 103 logopts = [
104 104 ('p', 'patch', None, _('show patch')),
105 105 ('g', 'git', None, _('use git extended diff format')),
106 106 ('l', 'limit', '',
107 107 _('limit number of changes displayed'), _('NUM')),
108 108 ('M', 'no-merges', None, _('do not show merges')),
109 109 ('', 'stat', None, _('output diffstat-style summary of changes')),
110 110 ('G', 'graph', None, _("show the revision DAG")),
111 111 ] + templateopts
112 112
113 113 diffopts = [
114 114 ('a', 'text', None, _('treat all files as text')),
115 115 ('g', 'git', None, _('use git extended diff format')),
116 116 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
117 117 ('', 'nodates', None, _('omit dates from diff headers'))
118 118 ]
119 119
120 120 diffwsopts = [
121 121 ('w', 'ignore-all-space', None,
122 122 _('ignore white space when comparing lines')),
123 123 ('b', 'ignore-space-change', None,
124 124 _('ignore changes in the amount of white space')),
125 125 ('B', 'ignore-blank-lines', None,
126 126 _('ignore changes whose lines are all blank')),
127 127 ('Z', 'ignore-space-at-eol', None,
128 128 _('ignore changes in whitespace at EOL')),
129 129 ]
130 130
131 131 diffopts2 = [
132 132 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
133 133 ('p', 'show-function', None, _('show which function each change is in')),
134 134 ('', 'reverse', None, _('produce a diff that undoes the changes')),
135 135 ] + diffwsopts + [
136 136 ('U', 'unified', '',
137 137 _('number of lines of context to show'), _('NUM')),
138 138 ('', 'stat', None, _('output diffstat-style summary of changes')),
139 139 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
140 140 ]
141 141
142 142 mergetoolopts = [
143 143 ('t', 'tool', '', _('specify merge tool')),
144 144 ]
145 145
146 146 similarityopts = [
147 147 ('s', 'similarity', '',
148 148 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
149 149 ]
150 150
151 151 subrepoopts = [
152 152 ('S', 'subrepos', None,
153 153 _('recurse into subrepositories'))
154 154 ]
155 155
156 156 debugrevlogopts = [
157 157 ('c', 'changelog', False, _('open changelog')),
158 158 ('m', 'manifest', False, _('open manifest')),
159 159 ('', 'dir', '', _('open directory manifest')),
160 160 ]
161 161
162 162 # special string such that everything below this line will be ingored in the
163 163 # editor text
164 164 _linebelow = "^HG: ------------------------ >8 ------------------------$"
165 165
166 166 def ishunk(x):
167 167 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
168 168 return isinstance(x, hunkclasses)
169 169
170 170 def newandmodified(chunks, originalchunks):
171 171 newlyaddedandmodifiedfiles = set()
172 172 for chunk in chunks:
173 173 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
174 174 originalchunks:
175 175 newlyaddedandmodifiedfiles.add(chunk.header.filename())
176 176 return newlyaddedandmodifiedfiles
177 177
178 178 def parsealiases(cmd):
179 179 return cmd.lstrip("^").split("|")
180 180
181 181 def setupwrapcolorwrite(ui):
182 182 # wrap ui.write so diff output can be labeled/colorized
183 183 def wrapwrite(orig, *args, **kw):
184 184 label = kw.pop('label', '')
185 185 for chunk, l in patch.difflabel(lambda: args):
186 186 orig(chunk, label=label + l)
187 187
188 188 oldwrite = ui.write
189 189 def wrap(*args, **kwargs):
190 190 return wrapwrite(oldwrite, *args, **kwargs)
191 191 setattr(ui, 'write', wrap)
192 192 return oldwrite
193 193
194 194 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
195 195 if usecurses:
196 196 if testfile:
197 197 recordfn = crecordmod.testdecorator(testfile,
198 198 crecordmod.testchunkselector)
199 199 else:
200 200 recordfn = crecordmod.chunkselector
201 201
202 202 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
203 203
204 204 else:
205 205 return patch.filterpatch(ui, originalhunks, operation)
206 206
207 207 def recordfilter(ui, originalhunks, operation=None):
208 208 """ Prompts the user to filter the originalhunks and return a list of
209 209 selected hunks.
210 210 *operation* is used for to build ui messages to indicate the user what
211 211 kind of filtering they are doing: reverting, committing, shelving, etc.
212 212 (see patch.filterpatch).
213 213 """
214 214 usecurses = crecordmod.checkcurses(ui)
215 215 testfile = ui.config('experimental', 'crecordtest')
216 216 oldwrite = setupwrapcolorwrite(ui)
217 217 try:
218 218 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
219 219 testfile, operation)
220 220 finally:
221 221 ui.write = oldwrite
222 222 return newchunks, newopts
223 223
224 224 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
225 225 filterfn, *pats, **opts):
226 226 from . import merge as mergemod
227 227 opts = pycompat.byteskwargs(opts)
228 228 if not ui.interactive():
229 229 if cmdsuggest:
230 230 msg = _('running non-interactively, use %s instead') % cmdsuggest
231 231 else:
232 232 msg = _('running non-interactively')
233 233 raise error.Abort(msg)
234 234
235 235 # make sure username is set before going interactive
236 236 if not opts.get('user'):
237 237 ui.username() # raise exception, username not provided
238 238
239 239 def recordfunc(ui, repo, message, match, opts):
240 240 """This is generic record driver.
241 241
242 242 Its job is to interactively filter local changes, and
243 243 accordingly prepare working directory into a state in which the
244 244 job can be delegated to a non-interactive commit command such as
245 245 'commit' or 'qrefresh'.
246 246
247 247 After the actual job is done by non-interactive command, the
248 248 working directory is restored to its original state.
249 249
250 250 In the end we'll record interesting changes, and everything else
251 251 will be left in place, so the user can continue working.
252 252 """
253 253
254 254 checkunfinished(repo, commit=True)
255 255 wctx = repo[None]
256 256 merge = len(wctx.parents()) > 1
257 257 if merge:
258 258 raise error.Abort(_('cannot partially commit a merge '
259 259 '(use "hg commit" instead)'))
260 260
261 261 def fail(f, msg):
262 262 raise error.Abort('%s: %s' % (f, msg))
263 263
264 264 force = opts.get('force')
265 265 if not force:
266 266 vdirs = []
267 267 match.explicitdir = vdirs.append
268 268 match.bad = fail
269 269
270 270 status = repo.status(match=match)
271 271 if not force:
272 272 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
273 273 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
274 274 diffopts.nodates = True
275 275 diffopts.git = True
276 276 diffopts.showfunc = True
277 277 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
278 278 originalchunks = patch.parsepatch(originaldiff)
279 279
280 280 # 1. filter patch, since we are intending to apply subset of it
281 281 try:
282 282 chunks, newopts = filterfn(ui, originalchunks)
283 283 except error.PatchError as err:
284 284 raise error.Abort(_('error parsing patch: %s') % err)
285 285 opts.update(newopts)
286 286
287 287 # We need to keep a backup of files that have been newly added and
288 288 # modified during the recording process because there is a previous
289 289 # version without the edit in the workdir
290 290 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
291 291 contenders = set()
292 292 for h in chunks:
293 293 try:
294 294 contenders.update(set(h.files()))
295 295 except AttributeError:
296 296 pass
297 297
298 298 changed = status.modified + status.added + status.removed
299 299 newfiles = [f for f in changed if f in contenders]
300 300 if not newfiles:
301 301 ui.status(_('no changes to record\n'))
302 302 return 0
303 303
304 304 modified = set(status.modified)
305 305
306 306 # 2. backup changed files, so we can restore them in the end
307 307
308 308 if backupall:
309 309 tobackup = changed
310 310 else:
311 311 tobackup = [f for f in newfiles if f in modified or f in \
312 312 newlyaddedandmodifiedfiles]
313 313 backups = {}
314 314 if tobackup:
315 315 backupdir = repo.vfs.join('record-backups')
316 316 try:
317 317 os.mkdir(backupdir)
318 318 except OSError as err:
319 319 if err.errno != errno.EEXIST:
320 320 raise
321 321 try:
322 322 # backup continues
323 323 for f in tobackup:
324 324 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
325 325 dir=backupdir)
326 326 os.close(fd)
327 327 ui.debug('backup %r as %r\n' % (f, tmpname))
328 328 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
329 329 backups[f] = tmpname
330 330
331 331 fp = stringio()
332 332 for c in chunks:
333 333 fname = c.filename()
334 334 if fname in backups:
335 335 c.write(fp)
336 336 dopatch = fp.tell()
337 337 fp.seek(0)
338 338
339 339 # 2.5 optionally review / modify patch in text editor
340 340 if opts.get('review', False):
341 341 patchtext = (crecordmod.diffhelptext
342 342 + crecordmod.patchhelptext
343 343 + fp.read())
344 344 reviewedpatch = ui.edit(patchtext, "",
345 345 action="diff",
346 346 repopath=repo.path)
347 347 fp.truncate(0)
348 348 fp.write(reviewedpatch)
349 349 fp.seek(0)
350 350
351 351 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
352 352 # 3a. apply filtered patch to clean repo (clean)
353 353 if backups:
354 354 # Equivalent to hg.revert
355 355 m = scmutil.matchfiles(repo, backups.keys())
356 356 mergemod.update(repo, repo.dirstate.p1(),
357 357 False, True, matcher=m)
358 358
359 359 # 3b. (apply)
360 360 if dopatch:
361 361 try:
362 362 ui.debug('applying patch\n')
363 363 ui.debug(fp.getvalue())
364 364 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
365 365 except error.PatchError as err:
366 366 raise error.Abort(str(err))
367 367 del fp
368 368
369 369 # 4. We prepared working directory according to filtered
370 370 # patch. Now is the time to delegate the job to
371 371 # commit/qrefresh or the like!
372 372
373 373 # Make all of the pathnames absolute.
374 374 newfiles = [repo.wjoin(nf) for nf in newfiles]
375 375 return commitfunc(ui, repo, *newfiles, **opts)
376 376 finally:
377 377 # 5. finally restore backed-up files
378 378 try:
379 379 dirstate = repo.dirstate
380 380 for realname, tmpname in backups.iteritems():
381 381 ui.debug('restoring %r to %r\n' % (tmpname, realname))
382 382
383 383 if dirstate[realname] == 'n':
384 384 # without normallookup, restoring timestamp
385 385 # may cause partially committed files
386 386 # to be treated as unmodified
387 387 dirstate.normallookup(realname)
388 388
389 389 # copystat=True here and above are a hack to trick any
390 390 # editors that have f open that we haven't modified them.
391 391 #
392 392 # Also note that this racy as an editor could notice the
393 393 # file's mtime before we've finished writing it.
394 394 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
395 395 os.unlink(tmpname)
396 396 if tobackup:
397 397 os.rmdir(backupdir)
398 398 except OSError:
399 399 pass
400 400
401 401 def recordinwlock(ui, repo, message, match, opts):
402 402 with repo.wlock():
403 403 return recordfunc(ui, repo, message, match, opts)
404 404
405 405 return commit(ui, repo, recordinwlock, pats, opts)
406 406
407 407
408 408 # extracted at module level as it's required each time a file will be added
409 409 # to dirnode class object below
410 410 pathsep = pycompat.ossep
411 411
412 412 class dirnode(object):
413 413 """
414 414 Represent a directory in user working copy with information required for
415 415 the purpose of tersing its status.
416 416
417 417 path is the path to the directory
418 418
419 419 statuses is a set of statuses of all files in this directory (this includes
420 420 all the files in all the subdirectories too)
421 421
422 422 files is a list of files which are direct child of this directory
423 423
424 424 subdirs is a dictionary of sub-directory name as the key and it's own
425 425 dirnode object as the value
426 426 """
427 427
428 428 def __init__(self, dirpath):
429 429 self.path = dirpath
430 430 self.statuses = set([])
431 431 self.files = []
432 432 self.subdirs = {}
433 433
434 434 def _addfileindir(self, filename, status):
435 435 """Add a file in this directory as a direct child."""
436 436 self.files.append((filename, status))
437 437
438 438 def addfile(self, filename, status):
439 439 """
440 440 Add a file to this directory or to its direct parent directory.
441 441
442 442 If the file is not direct child of this directory, we traverse to the
443 443 directory of which this file is a direct child of and add the file
444 444 there.
445 445 """
446 446
447 447 # the filename contains a path separator, it means it's not the direct
448 448 # child of this directory
449 449 if pathsep in filename:
450 450 subdir, filep = filename.split(pathsep, 1)
451 451
452 452 # does the dirnode object for subdir exists
453 453 if subdir not in self.subdirs:
454 454 subdirpath = os.path.join(self.path, subdir)
455 455 self.subdirs[subdir] = dirnode(subdirpath)
456 456
457 457 # try adding the file in subdir
458 458 self.subdirs[subdir].addfile(filep, status)
459 459
460 460 else:
461 461 self._addfileindir(filename, status)
462 462
463 463 if status not in self.statuses:
464 464 self.statuses.add(status)
465 465
466 466 def iterfilepaths(self):
467 467 """Yield (status, path) for files directly under this directory."""
468 468 for f, st in self.files:
469 469 yield st, os.path.join(self.path, f)
470 470
471 471 def tersewalk(self, terseargs):
472 472 """
473 473 Yield (status, path) obtained by processing the status of this
474 474 dirnode.
475 475
476 476 terseargs is the string of arguments passed by the user with `--terse`
477 477 flag.
478 478
479 479 Following are the cases which can happen:
480 480
481 481 1) All the files in the directory (including all the files in its
482 482 subdirectories) share the same status and the user has asked us to terse
483 483 that status. -> yield (status, dirpath)
484 484
485 485 2) Otherwise, we do following:
486 486
487 487 a) Yield (status, filepath) for all the files which are in this
488 488 directory (only the ones in this directory, not the subdirs)
489 489
490 490 b) Recurse the function on all the subdirectories of this
491 491 directory
492 492 """
493 493
494 494 if len(self.statuses) == 1:
495 495 onlyst = self.statuses.pop()
496 496
497 497 # Making sure we terse only when the status abbreviation is
498 498 # passed as terse argument
499 499 if onlyst in terseargs:
500 500 yield onlyst, self.path + pycompat.ossep
501 501 return
502 502
503 503 # add the files to status list
504 504 for st, fpath in self.iterfilepaths():
505 505 yield st, fpath
506 506
507 507 #recurse on the subdirs
508 508 for dirobj in self.subdirs.values():
509 509 for st, fpath in dirobj.tersewalk(terseargs):
510 510 yield st, fpath
511 511
512 512 def tersedir(statuslist, terseargs):
513 513 """
514 514 Terse the status if all the files in a directory shares the same status.
515 515
516 516 statuslist is scmutil.status() object which contains a list of files for
517 517 each status.
518 518 terseargs is string which is passed by the user as the argument to `--terse`
519 519 flag.
520 520
521 521 The function makes a tree of objects of dirnode class, and at each node it
522 522 stores the information required to know whether we can terse a certain
523 523 directory or not.
524 524 """
525 525 # the order matters here as that is used to produce final list
526 526 allst = ('m', 'a', 'r', 'd', 'u', 'i', 'c')
527 527
528 528 # checking the argument validity
529 529 for s in pycompat.bytestr(terseargs):
530 530 if s not in allst:
531 531 raise error.Abort(_("'%s' not recognized") % s)
532 532
533 533 # creating a dirnode object for the root of the repo
534 534 rootobj = dirnode('')
535 535 pstatus = ('modified', 'added', 'deleted', 'clean', 'unknown',
536 536 'ignored', 'removed')
537 537
538 538 tersedict = {}
539 539 for attrname in pstatus:
540 540 statuschar = attrname[0:1]
541 541 for f in getattr(statuslist, attrname):
542 542 rootobj.addfile(f, statuschar)
543 543 tersedict[statuschar] = []
544 544
545 545 # we won't be tersing the root dir, so add files in it
546 546 for st, fpath in rootobj.iterfilepaths():
547 547 tersedict[st].append(fpath)
548 548
549 549 # process each sub-directory and build tersedict
550 550 for subdir in rootobj.subdirs.values():
551 551 for st, f in subdir.tersewalk(terseargs):
552 552 tersedict[st].append(f)
553 553
554 554 tersedlist = []
555 555 for st in allst:
556 556 tersedict[st].sort()
557 557 tersedlist.append(tersedict[st])
558 558
559 559 return tersedlist
560 560
561 561 def _commentlines(raw):
562 562 '''Surround lineswith a comment char and a new line'''
563 563 lines = raw.splitlines()
564 564 commentedlines = ['# %s' % line for line in lines]
565 565 return '\n'.join(commentedlines) + '\n'
566 566
567 567 def _conflictsmsg(repo):
568 568 # avoid merge cycle
569 569 from . import merge as mergemod
570 570 mergestate = mergemod.mergestate.read(repo)
571 571 if not mergestate.active():
572 572 return
573 573
574 574 m = scmutil.match(repo[None])
575 575 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
576 576 if unresolvedlist:
577 577 mergeliststr = '\n'.join(
578 578 [' %s' % os.path.relpath(
579 579 os.path.join(repo.root, path),
580 580 pycompat.getcwd()) for path in unresolvedlist])
581 581 msg = _('''Unresolved merge conflicts:
582 582
583 583 %s
584 584
585 585 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
586 586 else:
587 587 msg = _('No unresolved merge conflicts.')
588 588
589 589 return _commentlines(msg)
590 590
591 591 def _helpmessage(continuecmd, abortcmd):
592 592 msg = _('To continue: %s\n'
593 593 'To abort: %s') % (continuecmd, abortcmd)
594 594 return _commentlines(msg)
595 595
596 596 def _rebasemsg():
597 597 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
598 598
599 599 def _histeditmsg():
600 600 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
601 601
602 602 def _unshelvemsg():
603 603 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
604 604
605 605 def _updatecleanmsg(dest=None):
606 606 warning = _('warning: this will discard uncommitted changes')
607 607 return 'hg update --clean %s (%s)' % (dest or '.', warning)
608 608
609 609 def _graftmsg():
610 610 # tweakdefaults requires `update` to have a rev hence the `.`
611 611 return _helpmessage('hg graft --continue', _updatecleanmsg())
612 612
613 613 def _mergemsg():
614 614 # tweakdefaults requires `update` to have a rev hence the `.`
615 615 return _helpmessage('hg commit', _updatecleanmsg())
616 616
617 617 def _bisectmsg():
618 618 msg = _('To mark the changeset good: hg bisect --good\n'
619 619 'To mark the changeset bad: hg bisect --bad\n'
620 620 'To abort: hg bisect --reset\n')
621 621 return _commentlines(msg)
622 622
623 623 def fileexistspredicate(filename):
624 624 return lambda repo: repo.vfs.exists(filename)
625 625
626 626 def _mergepredicate(repo):
627 627 return len(repo[None].parents()) > 1
628 628
629 629 STATES = (
630 630 # (state, predicate to detect states, helpful message function)
631 631 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
632 632 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
633 633 ('graft', fileexistspredicate('graftstate'), _graftmsg),
634 634 ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg),
635 635 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
636 636 # The merge state is part of a list that will be iterated over.
637 637 # They need to be last because some of the other unfinished states may also
638 638 # be in a merge or update state (eg. rebase, histedit, graft, etc).
639 639 # We want those to have priority.
640 640 ('merge', _mergepredicate, _mergemsg),
641 641 )
642 642
643 643 def _getrepostate(repo):
644 644 # experimental config: commands.status.skipstates
645 645 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
646 646 for state, statedetectionpredicate, msgfn in STATES:
647 647 if state in skip:
648 648 continue
649 649 if statedetectionpredicate(repo):
650 650 return (state, statedetectionpredicate, msgfn)
651 651
652 652 def morestatus(repo, fm):
653 653 statetuple = _getrepostate(repo)
654 654 label = 'status.morestatus'
655 655 if statetuple:
656 656 fm.startitem()
657 657 state, statedetectionpredicate, helpfulmsg = statetuple
658 658 statemsg = _('The repository is in an unfinished *%s* state.') % state
659 659 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
660 660 conmsg = _conflictsmsg(repo)
661 661 if conmsg:
662 662 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
663 663 if helpfulmsg:
664 664 helpmsg = helpfulmsg()
665 665 fm.write('helpmsg', '%s\n', helpmsg, label=label)
666 666
667 667 def findpossible(cmd, table, strict=False):
668 668 """
669 669 Return cmd -> (aliases, command table entry)
670 670 for each matching command.
671 671 Return debug commands (or their aliases) only if no normal command matches.
672 672 """
673 673 choice = {}
674 674 debugchoice = {}
675 675
676 676 if cmd in table:
677 677 # short-circuit exact matches, "log" alias beats "^log|history"
678 678 keys = [cmd]
679 679 else:
680 680 keys = table.keys()
681 681
682 682 allcmds = []
683 683 for e in keys:
684 684 aliases = parsealiases(e)
685 685 allcmds.extend(aliases)
686 686 found = None
687 687 if cmd in aliases:
688 688 found = cmd
689 689 elif not strict:
690 690 for a in aliases:
691 691 if a.startswith(cmd):
692 692 found = a
693 693 break
694 694 if found is not None:
695 695 if aliases[0].startswith("debug") or found.startswith("debug"):
696 696 debugchoice[found] = (aliases, table[e])
697 697 else:
698 698 choice[found] = (aliases, table[e])
699 699
700 700 if not choice and debugchoice:
701 701 choice = debugchoice
702 702
703 703 return choice, allcmds
704 704
705 705 def findcmd(cmd, table, strict=True):
706 706 """Return (aliases, command table entry) for command string."""
707 707 choice, allcmds = findpossible(cmd, table, strict)
708 708
709 709 if cmd in choice:
710 710 return choice[cmd]
711 711
712 712 if len(choice) > 1:
713 713 clist = sorted(choice)
714 714 raise error.AmbiguousCommand(cmd, clist)
715 715
716 716 if choice:
717 717 return list(choice.values())[0]
718 718
719 719 raise error.UnknownCommand(cmd, allcmds)
720 720
721 721 def findrepo(p):
722 722 while not os.path.isdir(os.path.join(p, ".hg")):
723 723 oldp, p = p, os.path.dirname(p)
724 724 if p == oldp:
725 725 return None
726 726
727 727 return p
728 728
729 729 def bailifchanged(repo, merge=True, hint=None):
730 730 """ enforce the precondition that working directory must be clean.
731 731
732 732 'merge' can be set to false if a pending uncommitted merge should be
733 733 ignored (such as when 'update --check' runs).
734 734
735 735 'hint' is the usual hint given to Abort exception.
736 736 """
737 737
738 738 if merge and repo.dirstate.p2() != nullid:
739 739 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
740 740 modified, added, removed, deleted = repo.status()[:4]
741 741 if modified or added or removed or deleted:
742 742 raise error.Abort(_('uncommitted changes'), hint=hint)
743 743 ctx = repo[None]
744 744 for s in sorted(ctx.substate):
745 745 ctx.sub(s).bailifchanged(hint=hint)
746 746
747 747 def logmessage(ui, opts):
748 748 """ get the log message according to -m and -l option """
749 749 message = opts.get('message')
750 750 logfile = opts.get('logfile')
751 751
752 752 if message and logfile:
753 753 raise error.Abort(_('options --message and --logfile are mutually '
754 754 'exclusive'))
755 755 if not message and logfile:
756 756 try:
757 757 if isstdiofilename(logfile):
758 758 message = ui.fin.read()
759 759 else:
760 760 message = '\n'.join(util.readfile(logfile).splitlines())
761 761 except IOError as inst:
762 762 raise error.Abort(_("can't read commit message '%s': %s") %
763 763 (logfile, encoding.strtolocal(inst.strerror)))
764 764 return message
765 765
766 766 def mergeeditform(ctxorbool, baseformname):
767 767 """return appropriate editform name (referencing a committemplate)
768 768
769 769 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
770 770 merging is committed.
771 771
772 772 This returns baseformname with '.merge' appended if it is a merge,
773 773 otherwise '.normal' is appended.
774 774 """
775 775 if isinstance(ctxorbool, bool):
776 776 if ctxorbool:
777 777 return baseformname + ".merge"
778 778 elif 1 < len(ctxorbool.parents()):
779 779 return baseformname + ".merge"
780 780
781 781 return baseformname + ".normal"
782 782
783 783 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
784 784 editform='', **opts):
785 785 """get appropriate commit message editor according to '--edit' option
786 786
787 787 'finishdesc' is a function to be called with edited commit message
788 788 (= 'description' of the new changeset) just after editing, but
789 789 before checking empty-ness. It should return actual text to be
790 790 stored into history. This allows to change description before
791 791 storing.
792 792
793 793 'extramsg' is a extra message to be shown in the editor instead of
794 794 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
795 795 is automatically added.
796 796
797 797 'editform' is a dot-separated list of names, to distinguish
798 798 the purpose of commit text editing.
799 799
800 800 'getcommiteditor' returns 'commitforceeditor' regardless of
801 801 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
802 802 they are specific for usage in MQ.
803 803 """
804 804 if edit or finishdesc or extramsg:
805 805 return lambda r, c, s: commitforceeditor(r, c, s,
806 806 finishdesc=finishdesc,
807 807 extramsg=extramsg,
808 808 editform=editform)
809 809 elif editform:
810 810 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
811 811 else:
812 812 return commiteditor
813 813
814 814 def loglimit(opts):
815 815 """get the log limit according to option -l/--limit"""
816 816 limit = opts.get('limit')
817 817 if limit:
818 818 try:
819 819 limit = int(limit)
820 820 except ValueError:
821 821 raise error.Abort(_('limit must be a positive integer'))
822 822 if limit <= 0:
823 823 raise error.Abort(_('limit must be positive'))
824 824 else:
825 825 limit = None
826 826 return limit
827 827
828 828 def makefilename(repo, pat, node, desc=None,
829 829 total=None, seqno=None, revwidth=None, pathname=None):
830 830 node_expander = {
831 831 'H': lambda: hex(node),
832 832 'R': lambda: str(repo.changelog.rev(node)),
833 833 'h': lambda: short(node),
834 834 'm': lambda: re.sub('[^\w]', '_', str(desc))
835 835 }
836 836 expander = {
837 837 '%': lambda: '%',
838 838 'b': lambda: os.path.basename(repo.root),
839 839 }
840 840
841 841 try:
842 842 if node:
843 843 expander.update(node_expander)
844 844 if node:
845 845 expander['r'] = (lambda:
846 846 str(repo.changelog.rev(node)).zfill(revwidth or 0))
847 847 if total is not None:
848 848 expander['N'] = lambda: str(total)
849 849 if seqno is not None:
850 850 expander['n'] = lambda: str(seqno)
851 851 if total is not None and seqno is not None:
852 852 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
853 853 if pathname is not None:
854 854 expander['s'] = lambda: os.path.basename(pathname)
855 855 expander['d'] = lambda: os.path.dirname(pathname) or '.'
856 856 expander['p'] = lambda: pathname
857 857
858 858 newname = []
859 859 patlen = len(pat)
860 860 i = 0
861 861 while i < patlen:
862 862 c = pat[i:i + 1]
863 863 if c == '%':
864 864 i += 1
865 865 c = pat[i:i + 1]
866 866 c = expander[c]()
867 867 newname.append(c)
868 868 i += 1
869 869 return ''.join(newname)
870 870 except KeyError as inst:
871 871 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
872 872 inst.args[0])
873 873
874 874 def isstdiofilename(pat):
875 875 """True if the given pat looks like a filename denoting stdin/stdout"""
876 876 return not pat or pat == '-'
877 877
878 878 class _unclosablefile(object):
879 879 def __init__(self, fp):
880 880 self._fp = fp
881 881
882 882 def close(self):
883 883 pass
884 884
885 885 def __iter__(self):
886 886 return iter(self._fp)
887 887
888 888 def __getattr__(self, attr):
889 889 return getattr(self._fp, attr)
890 890
891 891 def __enter__(self):
892 892 return self
893 893
894 894 def __exit__(self, exc_type, exc_value, exc_tb):
895 895 pass
896 896
897 897 def makefileobj(repo, pat, node=None, desc=None, total=None,
898 898 seqno=None, revwidth=None, mode='wb', modemap=None,
899 899 pathname=None):
900 900
901 901 writable = mode not in ('r', 'rb')
902 902
903 903 if isstdiofilename(pat):
904 904 if writable:
905 905 fp = repo.ui.fout
906 906 else:
907 907 fp = repo.ui.fin
908 908 return _unclosablefile(fp)
909 909 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
910 910 if modemap is not None:
911 911 mode = modemap.get(fn, mode)
912 912 if mode == 'wb':
913 913 modemap[fn] = 'ab'
914 914 return open(fn, mode)
915 915
916 916 def openrevlog(repo, cmd, file_, opts):
917 917 """opens the changelog, manifest, a filelog or a given revlog"""
918 918 cl = opts['changelog']
919 919 mf = opts['manifest']
920 920 dir = opts['dir']
921 921 msg = None
922 922 if cl and mf:
923 923 msg = _('cannot specify --changelog and --manifest at the same time')
924 924 elif cl and dir:
925 925 msg = _('cannot specify --changelog and --dir at the same time')
926 926 elif cl or mf or dir:
927 927 if file_:
928 928 msg = _('cannot specify filename with --changelog or --manifest')
929 929 elif not repo:
930 930 msg = _('cannot specify --changelog or --manifest or --dir '
931 931 'without a repository')
932 932 if msg:
933 933 raise error.Abort(msg)
934 934
935 935 r = None
936 936 if repo:
937 937 if cl:
938 938 r = repo.unfiltered().changelog
939 939 elif dir:
940 940 if 'treemanifest' not in repo.requirements:
941 941 raise error.Abort(_("--dir can only be used on repos with "
942 942 "treemanifest enabled"))
943 943 dirlog = repo.manifestlog._revlog.dirlog(dir)
944 944 if len(dirlog):
945 945 r = dirlog
946 946 elif mf:
947 947 r = repo.manifestlog._revlog
948 948 elif file_:
949 949 filelog = repo.file(file_)
950 950 if len(filelog):
951 951 r = filelog
952 952 if not r:
953 953 if not file_:
954 954 raise error.CommandError(cmd, _('invalid arguments'))
955 955 if not os.path.isfile(file_):
956 956 raise error.Abort(_("revlog '%s' not found") % file_)
957 957 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
958 958 file_[:-2] + ".i")
959 959 return r
960 960
961 961 def copy(ui, repo, pats, opts, rename=False):
962 962 # called with the repo lock held
963 963 #
964 964 # hgsep => pathname that uses "/" to separate directories
965 965 # ossep => pathname that uses os.sep to separate directories
966 966 cwd = repo.getcwd()
967 967 targets = {}
968 968 after = opts.get("after")
969 969 dryrun = opts.get("dry_run")
970 970 wctx = repo[None]
971 971
972 972 def walkpat(pat):
973 973 srcs = []
974 974 if after:
975 975 badstates = '?'
976 976 else:
977 977 badstates = '?r'
978 978 m = scmutil.match(wctx, [pat], opts, globbed=True)
979 979 for abs in wctx.walk(m):
980 980 state = repo.dirstate[abs]
981 981 rel = m.rel(abs)
982 982 exact = m.exact(abs)
983 983 if state in badstates:
984 984 if exact and state == '?':
985 985 ui.warn(_('%s: not copying - file is not managed\n') % rel)
986 986 if exact and state == 'r':
987 987 ui.warn(_('%s: not copying - file has been marked for'
988 988 ' remove\n') % rel)
989 989 continue
990 990 # abs: hgsep
991 991 # rel: ossep
992 992 srcs.append((abs, rel, exact))
993 993 return srcs
994 994
995 995 # abssrc: hgsep
996 996 # relsrc: ossep
997 997 # otarget: ossep
998 998 def copyfile(abssrc, relsrc, otarget, exact):
999 999 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1000 1000 if '/' in abstarget:
1001 1001 # We cannot normalize abstarget itself, this would prevent
1002 1002 # case only renames, like a => A.
1003 1003 abspath, absname = abstarget.rsplit('/', 1)
1004 1004 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1005 1005 reltarget = repo.pathto(abstarget, cwd)
1006 1006 target = repo.wjoin(abstarget)
1007 1007 src = repo.wjoin(abssrc)
1008 1008 state = repo.dirstate[abstarget]
1009 1009
1010 1010 scmutil.checkportable(ui, abstarget)
1011 1011
1012 1012 # check for collisions
1013 1013 prevsrc = targets.get(abstarget)
1014 1014 if prevsrc is not None:
1015 1015 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1016 1016 (reltarget, repo.pathto(abssrc, cwd),
1017 1017 repo.pathto(prevsrc, cwd)))
1018 1018 return
1019 1019
1020 1020 # check for overwrites
1021 1021 exists = os.path.lexists(target)
1022 1022 samefile = False
1023 1023 if exists and abssrc != abstarget:
1024 1024 if (repo.dirstate.normalize(abssrc) ==
1025 1025 repo.dirstate.normalize(abstarget)):
1026 1026 if not rename:
1027 1027 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1028 1028 return
1029 1029 exists = False
1030 1030 samefile = True
1031 1031
1032 1032 if not after and exists or after and state in 'mn':
1033 1033 if not opts['force']:
1034 1034 if state in 'mn':
1035 1035 msg = _('%s: not overwriting - file already committed\n')
1036 1036 if after:
1037 1037 flags = '--after --force'
1038 1038 else:
1039 1039 flags = '--force'
1040 1040 if rename:
1041 1041 hint = _('(hg rename %s to replace the file by '
1042 1042 'recording a rename)\n') % flags
1043 1043 else:
1044 1044 hint = _('(hg copy %s to replace the file by '
1045 1045 'recording a copy)\n') % flags
1046 1046 else:
1047 1047 msg = _('%s: not overwriting - file exists\n')
1048 1048 if rename:
1049 1049 hint = _('(hg rename --after to record the rename)\n')
1050 1050 else:
1051 1051 hint = _('(hg copy --after to record the copy)\n')
1052 1052 ui.warn(msg % reltarget)
1053 1053 ui.warn(hint)
1054 1054 return
1055 1055
1056 1056 if after:
1057 1057 if not exists:
1058 1058 if rename:
1059 1059 ui.warn(_('%s: not recording move - %s does not exist\n') %
1060 1060 (relsrc, reltarget))
1061 1061 else:
1062 1062 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1063 1063 (relsrc, reltarget))
1064 1064 return
1065 1065 elif not dryrun:
1066 1066 try:
1067 1067 if exists:
1068 1068 os.unlink(target)
1069 1069 targetdir = os.path.dirname(target) or '.'
1070 1070 if not os.path.isdir(targetdir):
1071 1071 os.makedirs(targetdir)
1072 1072 if samefile:
1073 1073 tmp = target + "~hgrename"
1074 1074 os.rename(src, tmp)
1075 1075 os.rename(tmp, target)
1076 1076 else:
1077 1077 util.copyfile(src, target)
1078 1078 srcexists = True
1079 1079 except IOError as inst:
1080 1080 if inst.errno == errno.ENOENT:
1081 1081 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1082 1082 srcexists = False
1083 1083 else:
1084 1084 ui.warn(_('%s: cannot copy - %s\n') %
1085 1085 (relsrc, encoding.strtolocal(inst.strerror)))
1086 1086 return True # report a failure
1087 1087
1088 1088 if ui.verbose or not exact:
1089 1089 if rename:
1090 1090 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1091 1091 else:
1092 1092 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1093 1093
1094 1094 targets[abstarget] = abssrc
1095 1095
1096 1096 # fix up dirstate
1097 1097 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1098 1098 dryrun=dryrun, cwd=cwd)
1099 1099 if rename and not dryrun:
1100 1100 if not after and srcexists and not samefile:
1101 1101 repo.wvfs.unlinkpath(abssrc)
1102 1102 wctx.forget([abssrc])
1103 1103
1104 1104 # pat: ossep
1105 1105 # dest ossep
1106 1106 # srcs: list of (hgsep, hgsep, ossep, bool)
1107 1107 # return: function that takes hgsep and returns ossep
1108 1108 def targetpathfn(pat, dest, srcs):
1109 1109 if os.path.isdir(pat):
1110 1110 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1111 1111 abspfx = util.localpath(abspfx)
1112 1112 if destdirexists:
1113 1113 striplen = len(os.path.split(abspfx)[0])
1114 1114 else:
1115 1115 striplen = len(abspfx)
1116 1116 if striplen:
1117 1117 striplen += len(pycompat.ossep)
1118 1118 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1119 1119 elif destdirexists:
1120 1120 res = lambda p: os.path.join(dest,
1121 1121 os.path.basename(util.localpath(p)))
1122 1122 else:
1123 1123 res = lambda p: dest
1124 1124 return res
1125 1125
1126 1126 # pat: ossep
1127 1127 # dest ossep
1128 1128 # srcs: list of (hgsep, hgsep, ossep, bool)
1129 1129 # return: function that takes hgsep and returns ossep
1130 1130 def targetpathafterfn(pat, dest, srcs):
1131 1131 if matchmod.patkind(pat):
1132 1132 # a mercurial pattern
1133 1133 res = lambda p: os.path.join(dest,
1134 1134 os.path.basename(util.localpath(p)))
1135 1135 else:
1136 1136 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1137 1137 if len(abspfx) < len(srcs[0][0]):
1138 1138 # A directory. Either the target path contains the last
1139 1139 # component of the source path or it does not.
1140 1140 def evalpath(striplen):
1141 1141 score = 0
1142 1142 for s in srcs:
1143 1143 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1144 1144 if os.path.lexists(t):
1145 1145 score += 1
1146 1146 return score
1147 1147
1148 1148 abspfx = util.localpath(abspfx)
1149 1149 striplen = len(abspfx)
1150 1150 if striplen:
1151 1151 striplen += len(pycompat.ossep)
1152 1152 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1153 1153 score = evalpath(striplen)
1154 1154 striplen1 = len(os.path.split(abspfx)[0])
1155 1155 if striplen1:
1156 1156 striplen1 += len(pycompat.ossep)
1157 1157 if evalpath(striplen1) > score:
1158 1158 striplen = striplen1
1159 1159 res = lambda p: os.path.join(dest,
1160 1160 util.localpath(p)[striplen:])
1161 1161 else:
1162 1162 # a file
1163 1163 if destdirexists:
1164 1164 res = lambda p: os.path.join(dest,
1165 1165 os.path.basename(util.localpath(p)))
1166 1166 else:
1167 1167 res = lambda p: dest
1168 1168 return res
1169 1169
1170 1170 pats = scmutil.expandpats(pats)
1171 1171 if not pats:
1172 1172 raise error.Abort(_('no source or destination specified'))
1173 1173 if len(pats) == 1:
1174 1174 raise error.Abort(_('no destination specified'))
1175 1175 dest = pats.pop()
1176 1176 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1177 1177 if not destdirexists:
1178 1178 if len(pats) > 1 or matchmod.patkind(pats[0]):
1179 1179 raise error.Abort(_('with multiple sources, destination must be an '
1180 1180 'existing directory'))
1181 1181 if util.endswithsep(dest):
1182 1182 raise error.Abort(_('destination %s is not a directory') % dest)
1183 1183
1184 1184 tfn = targetpathfn
1185 1185 if after:
1186 1186 tfn = targetpathafterfn
1187 1187 copylist = []
1188 1188 for pat in pats:
1189 1189 srcs = walkpat(pat)
1190 1190 if not srcs:
1191 1191 continue
1192 1192 copylist.append((tfn(pat, dest, srcs), srcs))
1193 1193 if not copylist:
1194 1194 raise error.Abort(_('no files to copy'))
1195 1195
1196 1196 errors = 0
1197 1197 for targetpath, srcs in copylist:
1198 1198 for abssrc, relsrc, exact in srcs:
1199 1199 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1200 1200 errors += 1
1201 1201
1202 1202 if errors:
1203 1203 ui.warn(_('(consider using --after)\n'))
1204 1204
1205 1205 return errors != 0
1206 1206
1207 1207 ## facility to let extension process additional data into an import patch
1208 1208 # list of identifier to be executed in order
1209 1209 extrapreimport = [] # run before commit
1210 1210 extrapostimport = [] # run after commit
1211 1211 # mapping from identifier to actual import function
1212 1212 #
1213 1213 # 'preimport' are run before the commit is made and are provided the following
1214 1214 # arguments:
1215 1215 # - repo: the localrepository instance,
1216 1216 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1217 1217 # - extra: the future extra dictionary of the changeset, please mutate it,
1218 1218 # - opts: the import options.
1219 1219 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1220 1220 # mutation of in memory commit and more. Feel free to rework the code to get
1221 1221 # there.
1222 1222 extrapreimportmap = {}
1223 1223 # 'postimport' are run after the commit is made and are provided the following
1224 1224 # argument:
1225 1225 # - ctx: the changectx created by import.
1226 1226 extrapostimportmap = {}
1227 1227
1228 1228 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
1229 1229 """Utility function used by commands.import to import a single patch
1230 1230
1231 1231 This function is explicitly defined here to help the evolve extension to
1232 1232 wrap this part of the import logic.
1233 1233
1234 1234 The API is currently a bit ugly because it a simple code translation from
1235 1235 the import command. Feel free to make it better.
1236 1236
1237 1237 :hunk: a patch (as a binary string)
1238 1238 :parents: nodes that will be parent of the created commit
1239 1239 :opts: the full dict of option passed to the import command
1240 1240 :msgs: list to save commit message to.
1241 1241 (used in case we need to save it when failing)
1242 1242 :updatefunc: a function that update a repo to a given node
1243 1243 updatefunc(<repo>, <node>)
1244 1244 """
1245 1245 # avoid cycle context -> subrepo -> cmdutil
1246 1246 from . import context
1247 1247 extractdata = patch.extract(ui, hunk)
1248 1248 tmpname = extractdata.get('filename')
1249 1249 message = extractdata.get('message')
1250 1250 user = opts.get('user') or extractdata.get('user')
1251 1251 date = opts.get('date') or extractdata.get('date')
1252 1252 branch = extractdata.get('branch')
1253 1253 nodeid = extractdata.get('nodeid')
1254 1254 p1 = extractdata.get('p1')
1255 1255 p2 = extractdata.get('p2')
1256 1256
1257 1257 nocommit = opts.get('no_commit')
1258 1258 importbranch = opts.get('import_branch')
1259 1259 update = not opts.get('bypass')
1260 1260 strip = opts["strip"]
1261 1261 prefix = opts["prefix"]
1262 1262 sim = float(opts.get('similarity') or 0)
1263 1263 if not tmpname:
1264 1264 return (None, None, False)
1265 1265
1266 1266 rejects = False
1267 1267
1268 1268 try:
1269 1269 cmdline_message = logmessage(ui, opts)
1270 1270 if cmdline_message:
1271 1271 # pickup the cmdline msg
1272 1272 message = cmdline_message
1273 1273 elif message:
1274 1274 # pickup the patch msg
1275 1275 message = message.strip()
1276 1276 else:
1277 1277 # launch the editor
1278 1278 message = None
1279 1279 ui.debug('message:\n%s\n' % message)
1280 1280
1281 1281 if len(parents) == 1:
1282 1282 parents.append(repo[nullid])
1283 1283 if opts.get('exact'):
1284 1284 if not nodeid or not p1:
1285 1285 raise error.Abort(_('not a Mercurial patch'))
1286 1286 p1 = repo[p1]
1287 1287 p2 = repo[p2 or nullid]
1288 1288 elif p2:
1289 1289 try:
1290 1290 p1 = repo[p1]
1291 1291 p2 = repo[p2]
1292 1292 # Without any options, consider p2 only if the
1293 1293 # patch is being applied on top of the recorded
1294 1294 # first parent.
1295 1295 if p1 != parents[0]:
1296 1296 p1 = parents[0]
1297 1297 p2 = repo[nullid]
1298 1298 except error.RepoError:
1299 1299 p1, p2 = parents
1300 1300 if p2.node() == nullid:
1301 1301 ui.warn(_("warning: import the patch as a normal revision\n"
1302 1302 "(use --exact to import the patch as a merge)\n"))
1303 1303 else:
1304 1304 p1, p2 = parents
1305 1305
1306 1306 n = None
1307 1307 if update:
1308 1308 if p1 != parents[0]:
1309 1309 updatefunc(repo, p1.node())
1310 1310 if p2 != parents[1]:
1311 1311 repo.setparents(p1.node(), p2.node())
1312 1312
1313 1313 if opts.get('exact') or importbranch:
1314 1314 repo.dirstate.setbranch(branch or 'default')
1315 1315
1316 1316 partial = opts.get('partial', False)
1317 1317 files = set()
1318 1318 try:
1319 1319 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1320 1320 files=files, eolmode=None, similarity=sim / 100.0)
1321 1321 except error.PatchError as e:
1322 1322 if not partial:
1323 1323 raise error.Abort(str(e))
1324 1324 if partial:
1325 1325 rejects = True
1326 1326
1327 1327 files = list(files)
1328 1328 if nocommit:
1329 1329 if message:
1330 1330 msgs.append(message)
1331 1331 else:
1332 1332 if opts.get('exact') or p2:
1333 1333 # If you got here, you either use --force and know what
1334 1334 # you are doing or used --exact or a merge patch while
1335 1335 # being updated to its first parent.
1336 1336 m = None
1337 1337 else:
1338 1338 m = scmutil.matchfiles(repo, files or [])
1339 1339 editform = mergeeditform(repo[None], 'import.normal')
1340 1340 if opts.get('exact'):
1341 1341 editor = None
1342 1342 else:
1343 1343 editor = getcommiteditor(editform=editform, **opts)
1344 1344 extra = {}
1345 1345 for idfunc in extrapreimport:
1346 1346 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1347 1347 overrides = {}
1348 1348 if partial:
1349 1349 overrides[('ui', 'allowemptycommit')] = True
1350 1350 with repo.ui.configoverride(overrides, 'import'):
1351 1351 n = repo.commit(message, user,
1352 1352 date, match=m,
1353 1353 editor=editor, extra=extra)
1354 1354 for idfunc in extrapostimport:
1355 1355 extrapostimportmap[idfunc](repo[n])
1356 1356 else:
1357 1357 if opts.get('exact') or importbranch:
1358 1358 branch = branch or 'default'
1359 1359 else:
1360 1360 branch = p1.branch()
1361 1361 store = patch.filestore()
1362 1362 try:
1363 1363 files = set()
1364 1364 try:
1365 1365 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1366 1366 files, eolmode=None)
1367 1367 except error.PatchError as e:
1368 1368 raise error.Abort(str(e))
1369 1369 if opts.get('exact'):
1370 1370 editor = None
1371 1371 else:
1372 1372 editor = getcommiteditor(editform='import.bypass')
1373 1373 memctx = context.memctx(repo, (p1.node(), p2.node()),
1374 1374 message,
1375 1375 files=files,
1376 1376 filectxfn=store,
1377 1377 user=user,
1378 1378 date=date,
1379 1379 branch=branch,
1380 1380 editor=editor)
1381 1381 n = memctx.commit()
1382 1382 finally:
1383 1383 store.close()
1384 1384 if opts.get('exact') and nocommit:
1385 1385 # --exact with --no-commit is still useful in that it does merge
1386 1386 # and branch bits
1387 1387 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1388 1388 elif opts.get('exact') and hex(n) != nodeid:
1389 1389 raise error.Abort(_('patch is damaged or loses information'))
1390 1390 msg = _('applied to working directory')
1391 1391 if n:
1392 1392 # i18n: refers to a short changeset id
1393 1393 msg = _('created %s') % short(n)
1394 1394 return (msg, n, rejects)
1395 1395 finally:
1396 1396 os.unlink(tmpname)
1397 1397
1398 1398 # facility to let extensions include additional data in an exported patch
1399 1399 # list of identifiers to be executed in order
1400 1400 extraexport = []
1401 1401 # mapping from identifier to actual export function
1402 1402 # function as to return a string to be added to the header or None
1403 1403 # it is given two arguments (sequencenumber, changectx)
1404 1404 extraexportmap = {}
1405 1405
1406 1406 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1407 1407 node = scmutil.binnode(ctx)
1408 1408 parents = [p.node() for p in ctx.parents() if p]
1409 1409 branch = ctx.branch()
1410 1410 if switch_parent:
1411 1411 parents.reverse()
1412 1412
1413 1413 if parents:
1414 1414 prev = parents[0]
1415 1415 else:
1416 1416 prev = nullid
1417 1417
1418 1418 write("# HG changeset patch\n")
1419 1419 write("# User %s\n" % ctx.user())
1420 1420 write("# Date %d %d\n" % ctx.date())
1421 1421 write("# %s\n" % util.datestr(ctx.date()))
1422 1422 if branch and branch != 'default':
1423 1423 write("# Branch %s\n" % branch)
1424 1424 write("# Node ID %s\n" % hex(node))
1425 1425 write("# Parent %s\n" % hex(prev))
1426 1426 if len(parents) > 1:
1427 1427 write("# Parent %s\n" % hex(parents[1]))
1428 1428
1429 1429 for headerid in extraexport:
1430 1430 header = extraexportmap[headerid](seqno, ctx)
1431 1431 if header is not None:
1432 1432 write('# %s\n' % header)
1433 1433 write(ctx.description().rstrip())
1434 1434 write("\n\n")
1435 1435
1436 1436 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1437 1437 write(chunk, label=label)
1438 1438
1439 1439 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1440 1440 opts=None, match=None):
1441 1441 '''export changesets as hg patches
1442 1442
1443 1443 Args:
1444 1444 repo: The repository from which we're exporting revisions.
1445 1445 revs: A list of revisions to export as revision numbers.
1446 1446 fntemplate: An optional string to use for generating patch file names.
1447 1447 fp: An optional file-like object to which patches should be written.
1448 1448 switch_parent: If True, show diffs against second parent when not nullid.
1449 1449 Default is false, which always shows diff against p1.
1450 1450 opts: diff options to use for generating the patch.
1451 1451 match: If specified, only export changes to files matching this matcher.
1452 1452
1453 1453 Returns:
1454 1454 Nothing.
1455 1455
1456 1456 Side Effect:
1457 1457 "HG Changeset Patch" data is emitted to one of the following
1458 1458 destinations:
1459 1459 fp is specified: All revs are written to the specified
1460 1460 file-like object.
1461 1461 fntemplate specified: Each rev is written to a unique file named using
1462 1462 the given template.
1463 1463 Neither fp nor template specified: All revs written to repo.ui.write()
1464 1464 '''
1465 1465
1466 1466 total = len(revs)
1467 1467 revwidth = max(len(str(rev)) for rev in revs)
1468 1468 filemode = {}
1469 1469
1470 1470 write = None
1471 1471 dest = '<unnamed>'
1472 1472 if fp:
1473 1473 dest = getattr(fp, 'name', dest)
1474 1474 def write(s, **kw):
1475 1475 fp.write(s)
1476 1476 elif not fntemplate:
1477 1477 write = repo.ui.write
1478 1478
1479 1479 for seqno, rev in enumerate(revs, 1):
1480 1480 ctx = repo[rev]
1481 1481 fo = None
1482 1482 if not fp and fntemplate:
1483 1483 desc_lines = ctx.description().rstrip().split('\n')
1484 1484 desc = desc_lines[0] #Commit always has a first line.
1485 1485 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1486 1486 total=total, seqno=seqno, revwidth=revwidth,
1487 1487 mode='wb', modemap=filemode)
1488 1488 dest = fo.name
1489 1489 def write(s, **kw):
1490 1490 fo.write(s)
1491 1491 if not dest.startswith('<'):
1492 1492 repo.ui.note("%s\n" % dest)
1493 1493 _exportsingle(
1494 1494 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1495 1495 if fo is not None:
1496 1496 fo.close()
1497 1497
1498 1498 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1499 1499 changes=None, stat=False, fp=None, prefix='',
1500 1500 root='', listsubrepos=False, hunksfilterfn=None):
1501 1501 '''show diff or diffstat.'''
1502 1502 if fp is None:
1503 1503 write = ui.write
1504 1504 else:
1505 1505 def write(s, **kw):
1506 1506 fp.write(s)
1507 1507
1508 1508 if root:
1509 1509 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1510 1510 else:
1511 1511 relroot = ''
1512 1512 if relroot != '':
1513 1513 # XXX relative roots currently don't work if the root is within a
1514 1514 # subrepo
1515 1515 uirelroot = match.uipath(relroot)
1516 1516 relroot += '/'
1517 1517 for matchroot in match.files():
1518 1518 if not matchroot.startswith(relroot):
1519 1519 ui.warn(_('warning: %s not inside relative root %s\n') % (
1520 1520 match.uipath(matchroot), uirelroot))
1521 1521
1522 1522 if stat:
1523 1523 diffopts = diffopts.copy(context=0)
1524 1524 width = 80
1525 1525 if not ui.plain():
1526 1526 width = ui.termwidth()
1527 1527 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1528 1528 prefix=prefix, relroot=relroot,
1529 1529 hunksfilterfn=hunksfilterfn)
1530 1530 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1531 1531 width=width):
1532 1532 write(chunk, label=label)
1533 1533 else:
1534 1534 for chunk, label in patch.diffui(repo, node1, node2, match,
1535 1535 changes, diffopts, prefix=prefix,
1536 1536 relroot=relroot,
1537 1537 hunksfilterfn=hunksfilterfn):
1538 1538 write(chunk, label=label)
1539 1539
1540 1540 if listsubrepos:
1541 1541 ctx1 = repo[node1]
1542 1542 ctx2 = repo[node2]
1543 1543 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1544 1544 tempnode2 = node2
1545 1545 try:
1546 1546 if node2 is not None:
1547 1547 tempnode2 = ctx2.substate[subpath][1]
1548 1548 except KeyError:
1549 1549 # A subrepo that existed in node1 was deleted between node1 and
1550 1550 # node2 (inclusive). Thus, ctx2's substate won't contain that
1551 1551 # subpath. The best we can do is to ignore it.
1552 1552 tempnode2 = None
1553 1553 submatch = matchmod.subdirmatcher(subpath, match)
1554 1554 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1555 1555 stat=stat, fp=fp, prefix=prefix)
1556 1556
1557 1557 def _changesetlabels(ctx):
1558 1558 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1559 1559 if ctx.obsolete():
1560 1560 labels.append('changeset.obsolete')
1561 1561 if ctx.isunstable():
1562 1562 labels.append('changeset.unstable')
1563 1563 for instability in ctx.instabilities():
1564 1564 labels.append('instability.%s' % instability)
1565 1565 return ' '.join(labels)
1566 1566
1567 1567 class changeset_printer(object):
1568 1568 '''show changeset information when templating not requested.'''
1569 1569
1570 1570 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1571 1571 self.ui = ui
1572 1572 self.repo = repo
1573 1573 self.buffered = buffered
1574 1574 self.matchfn = matchfn
1575 1575 self.diffopts = diffopts
1576 1576 self.header = {}
1577 1577 self.hunk = {}
1578 1578 self.lastheader = None
1579 1579 self.footer = None
1580 1580
1581 1581 def flush(self, ctx):
1582 1582 rev = ctx.rev()
1583 1583 if rev in self.header:
1584 1584 h = self.header[rev]
1585 1585 if h != self.lastheader:
1586 1586 self.lastheader = h
1587 1587 self.ui.write(h)
1588 1588 del self.header[rev]
1589 1589 if rev in self.hunk:
1590 1590 self.ui.write(self.hunk[rev])
1591 1591 del self.hunk[rev]
1592 1592 return 1
1593 1593 return 0
1594 1594
1595 1595 def close(self):
1596 1596 if self.footer:
1597 1597 self.ui.write(self.footer)
1598 1598
1599 1599 def show(self, ctx, copies=None, matchfn=None, hunksfilterfn=None,
1600 1600 **props):
1601 1601 props = pycompat.byteskwargs(props)
1602 1602 if self.buffered:
1603 1603 self.ui.pushbuffer(labeled=True)
1604 1604 self._show(ctx, copies, matchfn, hunksfilterfn, props)
1605 1605 self.hunk[ctx.rev()] = self.ui.popbuffer()
1606 1606 else:
1607 1607 self._show(ctx, copies, matchfn, hunksfilterfn, props)
1608 1608
1609 1609 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1610 1610 '''show a single changeset or file revision'''
1611 1611 changenode = ctx.node()
1612 1612 rev = ctx.rev()
1613 1613
1614 1614 if self.ui.quiet:
1615 1615 self.ui.write("%s\n" % scmutil.formatchangeid(ctx),
1616 1616 label='log.node')
1617 1617 return
1618 1618
1619 1619 date = util.datestr(ctx.date())
1620 1620
1621 1621 # i18n: column positioning for "hg log"
1622 1622 self.ui.write(_("changeset: %s\n") % scmutil.formatchangeid(ctx),
1623 1623 label=_changesetlabels(ctx))
1624 1624
1625 1625 # branches are shown first before any other names due to backwards
1626 1626 # compatibility
1627 1627 branch = ctx.branch()
1628 1628 # don't show the default branch name
1629 1629 if branch != 'default':
1630 1630 # i18n: column positioning for "hg log"
1631 1631 self.ui.write(_("branch: %s\n") % branch,
1632 1632 label='log.branch')
1633 1633
1634 1634 for nsname, ns in self.repo.names.iteritems():
1635 1635 # branches has special logic already handled above, so here we just
1636 1636 # skip it
1637 1637 if nsname == 'branches':
1638 1638 continue
1639 1639 # we will use the templatename as the color name since those two
1640 1640 # should be the same
1641 1641 for name in ns.names(self.repo, changenode):
1642 1642 self.ui.write(ns.logfmt % name,
1643 1643 label='log.%s' % ns.colorname)
1644 1644 if self.ui.debugflag:
1645 1645 # i18n: column positioning for "hg log"
1646 1646 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1647 1647 label='log.phase')
1648 1648 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1649 1649 label = 'log.parent changeset.%s' % pctx.phasestr()
1650 1650 # i18n: column positioning for "hg log"
1651 1651 self.ui.write(_("parent: %s\n") % scmutil.formatchangeid(pctx),
1652 1652 label=label)
1653 1653
1654 1654 if self.ui.debugflag and rev is not None:
1655 1655 mnode = ctx.manifestnode()
1656 1656 mrev = self.repo.manifestlog._revlog.rev(mnode)
1657 1657 # i18n: column positioning for "hg log"
1658 1658 self.ui.write(_("manifest: %s\n")
1659 1659 % scmutil.formatrevnode(self.ui, mrev, mnode),
1660 1660 label='ui.debug log.manifest')
1661 1661 # i18n: column positioning for "hg log"
1662 1662 self.ui.write(_("user: %s\n") % ctx.user(),
1663 1663 label='log.user')
1664 1664 # i18n: column positioning for "hg log"
1665 1665 self.ui.write(_("date: %s\n") % date,
1666 1666 label='log.date')
1667 1667
1668 1668 if ctx.isunstable():
1669 1669 # i18n: column positioning for "hg log"
1670 1670 instabilities = ctx.instabilities()
1671 1671 self.ui.write(_("instability: %s\n") % ', '.join(instabilities),
1672 1672 label='log.instability')
1673 1673
1674 1674 elif ctx.obsolete():
1675 1675 self._showobsfate(ctx)
1676 1676
1677 1677 self._exthook(ctx)
1678 1678
1679 1679 if self.ui.debugflag:
1680 1680 files = ctx.p1().status(ctx)[:3]
1681 1681 for key, value in zip([# i18n: column positioning for "hg log"
1682 1682 _("files:"),
1683 1683 # i18n: column positioning for "hg log"
1684 1684 _("files+:"),
1685 1685 # i18n: column positioning for "hg log"
1686 1686 _("files-:")], files):
1687 1687 if value:
1688 1688 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1689 1689 label='ui.debug log.files')
1690 1690 elif ctx.files() and self.ui.verbose:
1691 1691 # i18n: column positioning for "hg log"
1692 1692 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1693 1693 label='ui.note log.files')
1694 1694 if copies and self.ui.verbose:
1695 1695 copies = ['%s (%s)' % c for c in copies]
1696 1696 # i18n: column positioning for "hg log"
1697 1697 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1698 1698 label='ui.note log.copies')
1699 1699
1700 1700 extra = ctx.extra()
1701 1701 if extra and self.ui.debugflag:
1702 1702 for key, value in sorted(extra.items()):
1703 1703 # i18n: column positioning for "hg log"
1704 1704 self.ui.write(_("extra: %s=%s\n")
1705 1705 % (key, util.escapestr(value)),
1706 1706 label='ui.debug log.extra')
1707 1707
1708 1708 description = ctx.description().strip()
1709 1709 if description:
1710 1710 if self.ui.verbose:
1711 1711 self.ui.write(_("description:\n"),
1712 1712 label='ui.note log.description')
1713 1713 self.ui.write(description,
1714 1714 label='ui.note log.description')
1715 1715 self.ui.write("\n\n")
1716 1716 else:
1717 1717 # i18n: column positioning for "hg log"
1718 1718 self.ui.write(_("summary: %s\n") %
1719 1719 description.splitlines()[0],
1720 1720 label='log.summary')
1721 1721 self.ui.write("\n")
1722 1722
1723 1723 self.showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn)
1724 1724
1725 1725 def _showobsfate(self, ctx):
1726 1726 obsfate = templatekw.showobsfate(repo=self.repo, ctx=ctx, ui=self.ui)
1727 1727
1728 1728 if obsfate:
1729 1729 for obsfateline in obsfate:
1730 1730 # i18n: column positioning for "hg log"
1731 self.ui.write(_("obsfate: %s\n") % obsfateline,
1731 self.ui.write(_("obsolete: %s\n") % obsfateline,
1732 1732 label='log.obsfate')
1733 1733
1734 1734 def _exthook(self, ctx):
1735 1735 '''empty method used by extension as a hook point
1736 1736 '''
1737 1737
1738 1738 def showpatch(self, ctx, matchfn, hunksfilterfn=None):
1739 1739 if not matchfn:
1740 1740 matchfn = self.matchfn
1741 1741 if matchfn:
1742 1742 stat = self.diffopts.get('stat')
1743 1743 diff = self.diffopts.get('patch')
1744 1744 diffopts = patch.diffallopts(self.ui, self.diffopts)
1745 1745 node = ctx.node()
1746 1746 prev = ctx.p1().node()
1747 1747 if stat:
1748 1748 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1749 1749 match=matchfn, stat=True,
1750 1750 hunksfilterfn=hunksfilterfn)
1751 1751 if diff:
1752 1752 if stat:
1753 1753 self.ui.write("\n")
1754 1754 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1755 1755 match=matchfn, stat=False,
1756 1756 hunksfilterfn=hunksfilterfn)
1757 1757 self.ui.write("\n")
1758 1758
1759 1759 class jsonchangeset(changeset_printer):
1760 1760 '''format changeset information.'''
1761 1761
1762 1762 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1763 1763 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1764 1764 self.cache = {}
1765 1765 self._first = True
1766 1766
1767 1767 def close(self):
1768 1768 if not self._first:
1769 1769 self.ui.write("\n]\n")
1770 1770 else:
1771 1771 self.ui.write("[]\n")
1772 1772
1773 1773 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1774 1774 '''show a single changeset or file revision'''
1775 1775 rev = ctx.rev()
1776 1776 if rev is None:
1777 1777 jrev = jnode = 'null'
1778 1778 else:
1779 1779 jrev = '%d' % rev
1780 1780 jnode = '"%s"' % hex(ctx.node())
1781 1781 j = encoding.jsonescape
1782 1782
1783 1783 if self._first:
1784 1784 self.ui.write("[\n {")
1785 1785 self._first = False
1786 1786 else:
1787 1787 self.ui.write(",\n {")
1788 1788
1789 1789 if self.ui.quiet:
1790 1790 self.ui.write(('\n "rev": %s') % jrev)
1791 1791 self.ui.write((',\n "node": %s') % jnode)
1792 1792 self.ui.write('\n }')
1793 1793 return
1794 1794
1795 1795 self.ui.write(('\n "rev": %s') % jrev)
1796 1796 self.ui.write((',\n "node": %s') % jnode)
1797 1797 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1798 1798 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1799 1799 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1800 1800 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1801 1801 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1802 1802
1803 1803 self.ui.write((',\n "bookmarks": [%s]') %
1804 1804 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1805 1805 self.ui.write((',\n "tags": [%s]') %
1806 1806 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1807 1807 self.ui.write((',\n "parents": [%s]') %
1808 1808 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1809 1809
1810 1810 if self.ui.debugflag:
1811 1811 if rev is None:
1812 1812 jmanifestnode = 'null'
1813 1813 else:
1814 1814 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1815 1815 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1816 1816
1817 1817 self.ui.write((',\n "extra": {%s}') %
1818 1818 ", ".join('"%s": "%s"' % (j(k), j(v))
1819 1819 for k, v in ctx.extra().items()))
1820 1820
1821 1821 files = ctx.p1().status(ctx)
1822 1822 self.ui.write((',\n "modified": [%s]') %
1823 1823 ", ".join('"%s"' % j(f) for f in files[0]))
1824 1824 self.ui.write((',\n "added": [%s]') %
1825 1825 ", ".join('"%s"' % j(f) for f in files[1]))
1826 1826 self.ui.write((',\n "removed": [%s]') %
1827 1827 ", ".join('"%s"' % j(f) for f in files[2]))
1828 1828
1829 1829 elif self.ui.verbose:
1830 1830 self.ui.write((',\n "files": [%s]') %
1831 1831 ", ".join('"%s"' % j(f) for f in ctx.files()))
1832 1832
1833 1833 if copies:
1834 1834 self.ui.write((',\n "copies": {%s}') %
1835 1835 ", ".join('"%s": "%s"' % (j(k), j(v))
1836 1836 for k, v in copies))
1837 1837
1838 1838 matchfn = self.matchfn
1839 1839 if matchfn:
1840 1840 stat = self.diffopts.get('stat')
1841 1841 diff = self.diffopts.get('patch')
1842 1842 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1843 1843 node, prev = ctx.node(), ctx.p1().node()
1844 1844 if stat:
1845 1845 self.ui.pushbuffer()
1846 1846 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1847 1847 match=matchfn, stat=True)
1848 1848 self.ui.write((',\n "diffstat": "%s"')
1849 1849 % j(self.ui.popbuffer()))
1850 1850 if diff:
1851 1851 self.ui.pushbuffer()
1852 1852 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1853 1853 match=matchfn, stat=False)
1854 1854 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1855 1855
1856 1856 self.ui.write("\n }")
1857 1857
1858 1858 class changeset_templater(changeset_printer):
1859 1859 '''format changeset information.'''
1860 1860
1861 1861 # Arguments before "buffered" used to be positional. Consider not
1862 1862 # adding/removing arguments before "buffered" to not break callers.
1863 1863 def __init__(self, ui, repo, tmplspec, matchfn=None, diffopts=None,
1864 1864 buffered=False):
1865 1865 diffopts = diffopts or {}
1866 1866
1867 1867 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1868 1868 self.t = formatter.loadtemplater(ui, tmplspec,
1869 1869 cache=templatekw.defaulttempl)
1870 1870 self._counter = itertools.count()
1871 1871 self.cache = {}
1872 1872
1873 1873 self._tref = tmplspec.ref
1874 1874 self._parts = {'header': '', 'footer': '',
1875 1875 tmplspec.ref: tmplspec.ref,
1876 1876 'docheader': '', 'docfooter': '',
1877 1877 'separator': ''}
1878 1878 if tmplspec.mapfile:
1879 1879 # find correct templates for current mode, for backward
1880 1880 # compatibility with 'log -v/-q/--debug' using a mapfile
1881 1881 tmplmodes = [
1882 1882 (True, ''),
1883 1883 (self.ui.verbose, '_verbose'),
1884 1884 (self.ui.quiet, '_quiet'),
1885 1885 (self.ui.debugflag, '_debug'),
1886 1886 ]
1887 1887 for mode, postfix in tmplmodes:
1888 1888 for t in self._parts:
1889 1889 cur = t + postfix
1890 1890 if mode and cur in self.t:
1891 1891 self._parts[t] = cur
1892 1892 else:
1893 1893 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
1894 1894 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
1895 1895 self._parts.update(m)
1896 1896
1897 1897 if self._parts['docheader']:
1898 1898 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1899 1899
1900 1900 def close(self):
1901 1901 if self._parts['docfooter']:
1902 1902 if not self.footer:
1903 1903 self.footer = ""
1904 1904 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1905 1905 return super(changeset_templater, self).close()
1906 1906
1907 1907 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1908 1908 '''show a single changeset or file revision'''
1909 1909 props = props.copy()
1910 1910 props.update(templatekw.keywords)
1911 1911 props['templ'] = self.t
1912 1912 props['ctx'] = ctx
1913 1913 props['repo'] = self.repo
1914 1914 props['ui'] = self.repo.ui
1915 1915 props['index'] = index = next(self._counter)
1916 1916 props['revcache'] = {'copies': copies}
1917 1917 props['cache'] = self.cache
1918 1918 props = pycompat.strkwargs(props)
1919 1919
1920 1920 # write separator, which wouldn't work well with the header part below
1921 1921 # since there's inherently a conflict between header (across items) and
1922 1922 # separator (per item)
1923 1923 if self._parts['separator'] and index > 0:
1924 1924 self.ui.write(templater.stringify(self.t(self._parts['separator'])))
1925 1925
1926 1926 # write header
1927 1927 if self._parts['header']:
1928 1928 h = templater.stringify(self.t(self._parts['header'], **props))
1929 1929 if self.buffered:
1930 1930 self.header[ctx.rev()] = h
1931 1931 else:
1932 1932 if self.lastheader != h:
1933 1933 self.lastheader = h
1934 1934 self.ui.write(h)
1935 1935
1936 1936 # write changeset metadata, then patch if requested
1937 1937 key = self._parts[self._tref]
1938 1938 self.ui.write(templater.stringify(self.t(key, **props)))
1939 1939 self.showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn)
1940 1940
1941 1941 if self._parts['footer']:
1942 1942 if not self.footer:
1943 1943 self.footer = templater.stringify(
1944 1944 self.t(self._parts['footer'], **props))
1945 1945
1946 1946 def logtemplatespec(tmpl, mapfile):
1947 1947 if mapfile:
1948 1948 return formatter.templatespec('changeset', tmpl, mapfile)
1949 1949 else:
1950 1950 return formatter.templatespec('', tmpl, None)
1951 1951
1952 1952 def _lookuplogtemplate(ui, tmpl, style):
1953 1953 """Find the template matching the given template spec or style
1954 1954
1955 1955 See formatter.lookuptemplate() for details.
1956 1956 """
1957 1957
1958 1958 # ui settings
1959 1959 if not tmpl and not style: # template are stronger than style
1960 1960 tmpl = ui.config('ui', 'logtemplate')
1961 1961 if tmpl:
1962 1962 return logtemplatespec(templater.unquotestring(tmpl), None)
1963 1963 else:
1964 1964 style = util.expandpath(ui.config('ui', 'style'))
1965 1965
1966 1966 if not tmpl and style:
1967 1967 mapfile = style
1968 1968 if not os.path.split(mapfile)[0]:
1969 1969 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1970 1970 or templater.templatepath(mapfile))
1971 1971 if mapname:
1972 1972 mapfile = mapname
1973 1973 return logtemplatespec(None, mapfile)
1974 1974
1975 1975 if not tmpl:
1976 1976 return logtemplatespec(None, None)
1977 1977
1978 1978 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1979 1979
1980 1980 def makelogtemplater(ui, repo, tmpl, buffered=False):
1981 1981 """Create a changeset_templater from a literal template 'tmpl'"""
1982 1982 spec = logtemplatespec(tmpl, None)
1983 1983 return changeset_templater(ui, repo, spec, buffered=buffered)
1984 1984
1985 1985 def show_changeset(ui, repo, opts, buffered=False):
1986 1986 """show one changeset using template or regular display.
1987 1987
1988 1988 Display format will be the first non-empty hit of:
1989 1989 1. option 'template'
1990 1990 2. option 'style'
1991 1991 3. [ui] setting 'logtemplate'
1992 1992 4. [ui] setting 'style'
1993 1993 If all of these values are either the unset or the empty string,
1994 1994 regular display via changeset_printer() is done.
1995 1995 """
1996 1996 # options
1997 1997 match = None
1998 1998 if opts.get('patch') or opts.get('stat'):
1999 1999 match = scmutil.matchall(repo)
2000 2000
2001 2001 if opts.get('template') == 'json':
2002 2002 return jsonchangeset(ui, repo, match, opts, buffered)
2003 2003
2004 2004 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
2005 2005
2006 2006 if not spec.ref and not spec.tmpl and not spec.mapfile:
2007 2007 return changeset_printer(ui, repo, match, opts, buffered)
2008 2008
2009 2009 return changeset_templater(ui, repo, spec, match, opts, buffered)
2010 2010
2011 2011 def showmarker(fm, marker, index=None):
2012 2012 """utility function to display obsolescence marker in a readable way
2013 2013
2014 2014 To be used by debug function."""
2015 2015 if index is not None:
2016 2016 fm.write('index', '%i ', index)
2017 2017 fm.write('prednode', '%s ', hex(marker.prednode()))
2018 2018 succs = marker.succnodes()
2019 2019 fm.condwrite(succs, 'succnodes', '%s ',
2020 2020 fm.formatlist(map(hex, succs), name='node'))
2021 2021 fm.write('flag', '%X ', marker.flags())
2022 2022 parents = marker.parentnodes()
2023 2023 if parents is not None:
2024 2024 fm.write('parentnodes', '{%s} ',
2025 2025 fm.formatlist(map(hex, parents), name='node', sep=', '))
2026 2026 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
2027 2027 meta = marker.metadata().copy()
2028 2028 meta.pop('date', None)
2029 2029 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
2030 2030 fm.plain('\n')
2031 2031
2032 2032 def finddate(ui, repo, date):
2033 2033 """Find the tipmost changeset that matches the given date spec"""
2034 2034
2035 2035 df = util.matchdate(date)
2036 2036 m = scmutil.matchall(repo)
2037 2037 results = {}
2038 2038
2039 2039 def prep(ctx, fns):
2040 2040 d = ctx.date()
2041 2041 if df(d[0]):
2042 2042 results[ctx.rev()] = d
2043 2043
2044 2044 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
2045 2045 rev = ctx.rev()
2046 2046 if rev in results:
2047 2047 ui.status(_("found revision %s from %s\n") %
2048 2048 (rev, util.datestr(results[rev])))
2049 2049 return '%d' % rev
2050 2050
2051 2051 raise error.Abort(_("revision matching date not found"))
2052 2052
2053 2053 def increasingwindows(windowsize=8, sizelimit=512):
2054 2054 while True:
2055 2055 yield windowsize
2056 2056 if windowsize < sizelimit:
2057 2057 windowsize *= 2
2058 2058
2059 2059 class FileWalkError(Exception):
2060 2060 pass
2061 2061
2062 2062 def walkfilerevs(repo, match, follow, revs, fncache):
2063 2063 '''Walks the file history for the matched files.
2064 2064
2065 2065 Returns the changeset revs that are involved in the file history.
2066 2066
2067 2067 Throws FileWalkError if the file history can't be walked using
2068 2068 filelogs alone.
2069 2069 '''
2070 2070 wanted = set()
2071 2071 copies = []
2072 2072 minrev, maxrev = min(revs), max(revs)
2073 2073 def filerevgen(filelog, last):
2074 2074 """
2075 2075 Only files, no patterns. Check the history of each file.
2076 2076
2077 2077 Examines filelog entries within minrev, maxrev linkrev range
2078 2078 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2079 2079 tuples in backwards order
2080 2080 """
2081 2081 cl_count = len(repo)
2082 2082 revs = []
2083 2083 for j in xrange(0, last + 1):
2084 2084 linkrev = filelog.linkrev(j)
2085 2085 if linkrev < minrev:
2086 2086 continue
2087 2087 # only yield rev for which we have the changelog, it can
2088 2088 # happen while doing "hg log" during a pull or commit
2089 2089 if linkrev >= cl_count:
2090 2090 break
2091 2091
2092 2092 parentlinkrevs = []
2093 2093 for p in filelog.parentrevs(j):
2094 2094 if p != nullrev:
2095 2095 parentlinkrevs.append(filelog.linkrev(p))
2096 2096 n = filelog.node(j)
2097 2097 revs.append((linkrev, parentlinkrevs,
2098 2098 follow and filelog.renamed(n)))
2099 2099
2100 2100 return reversed(revs)
2101 2101 def iterfiles():
2102 2102 pctx = repo['.']
2103 2103 for filename in match.files():
2104 2104 if follow:
2105 2105 if filename not in pctx:
2106 2106 raise error.Abort(_('cannot follow file not in parent '
2107 2107 'revision: "%s"') % filename)
2108 2108 yield filename, pctx[filename].filenode()
2109 2109 else:
2110 2110 yield filename, None
2111 2111 for filename_node in copies:
2112 2112 yield filename_node
2113 2113
2114 2114 for file_, node in iterfiles():
2115 2115 filelog = repo.file(file_)
2116 2116 if not len(filelog):
2117 2117 if node is None:
2118 2118 # A zero count may be a directory or deleted file, so
2119 2119 # try to find matching entries on the slow path.
2120 2120 if follow:
2121 2121 raise error.Abort(
2122 2122 _('cannot follow nonexistent file: "%s"') % file_)
2123 2123 raise FileWalkError("Cannot walk via filelog")
2124 2124 else:
2125 2125 continue
2126 2126
2127 2127 if node is None:
2128 2128 last = len(filelog) - 1
2129 2129 else:
2130 2130 last = filelog.rev(node)
2131 2131
2132 2132 # keep track of all ancestors of the file
2133 2133 ancestors = {filelog.linkrev(last)}
2134 2134
2135 2135 # iterate from latest to oldest revision
2136 2136 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
2137 2137 if not follow:
2138 2138 if rev > maxrev:
2139 2139 continue
2140 2140 else:
2141 2141 # Note that last might not be the first interesting
2142 2142 # rev to us:
2143 2143 # if the file has been changed after maxrev, we'll
2144 2144 # have linkrev(last) > maxrev, and we still need
2145 2145 # to explore the file graph
2146 2146 if rev not in ancestors:
2147 2147 continue
2148 2148 # XXX insert 1327 fix here
2149 2149 if flparentlinkrevs:
2150 2150 ancestors.update(flparentlinkrevs)
2151 2151
2152 2152 fncache.setdefault(rev, []).append(file_)
2153 2153 wanted.add(rev)
2154 2154 if copied:
2155 2155 copies.append(copied)
2156 2156
2157 2157 return wanted
2158 2158
2159 2159 class _followfilter(object):
2160 2160 def __init__(self, repo, onlyfirst=False):
2161 2161 self.repo = repo
2162 2162 self.startrev = nullrev
2163 2163 self.roots = set()
2164 2164 self.onlyfirst = onlyfirst
2165 2165
2166 2166 def match(self, rev):
2167 2167 def realparents(rev):
2168 2168 if self.onlyfirst:
2169 2169 return self.repo.changelog.parentrevs(rev)[0:1]
2170 2170 else:
2171 2171 return filter(lambda x: x != nullrev,
2172 2172 self.repo.changelog.parentrevs(rev))
2173 2173
2174 2174 if self.startrev == nullrev:
2175 2175 self.startrev = rev
2176 2176 return True
2177 2177
2178 2178 if rev > self.startrev:
2179 2179 # forward: all descendants
2180 2180 if not self.roots:
2181 2181 self.roots.add(self.startrev)
2182 2182 for parent in realparents(rev):
2183 2183 if parent in self.roots:
2184 2184 self.roots.add(rev)
2185 2185 return True
2186 2186 else:
2187 2187 # backwards: all parents
2188 2188 if not self.roots:
2189 2189 self.roots.update(realparents(self.startrev))
2190 2190 if rev in self.roots:
2191 2191 self.roots.remove(rev)
2192 2192 self.roots.update(realparents(rev))
2193 2193 return True
2194 2194
2195 2195 return False
2196 2196
2197 2197 def walkchangerevs(repo, match, opts, prepare):
2198 2198 '''Iterate over files and the revs in which they changed.
2199 2199
2200 2200 Callers most commonly need to iterate backwards over the history
2201 2201 in which they are interested. Doing so has awful (quadratic-looking)
2202 2202 performance, so we use iterators in a "windowed" way.
2203 2203
2204 2204 We walk a window of revisions in the desired order. Within the
2205 2205 window, we first walk forwards to gather data, then in the desired
2206 2206 order (usually backwards) to display it.
2207 2207
2208 2208 This function returns an iterator yielding contexts. Before
2209 2209 yielding each context, the iterator will first call the prepare
2210 2210 function on each context in the window in forward order.'''
2211 2211
2212 2212 follow = opts.get('follow') or opts.get('follow_first')
2213 2213 revs = _logrevs(repo, opts)
2214 2214 if not revs:
2215 2215 return []
2216 2216 wanted = set()
2217 2217 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2218 2218 opts.get('removed'))
2219 2219 fncache = {}
2220 2220 change = repo.changectx
2221 2221
2222 2222 # First step is to fill wanted, the set of revisions that we want to yield.
2223 2223 # When it does not induce extra cost, we also fill fncache for revisions in
2224 2224 # wanted: a cache of filenames that were changed (ctx.files()) and that
2225 2225 # match the file filtering conditions.
2226 2226
2227 2227 if match.always():
2228 2228 # No files, no patterns. Display all revs.
2229 2229 wanted = revs
2230 2230 elif not slowpath:
2231 2231 # We only have to read through the filelog to find wanted revisions
2232 2232
2233 2233 try:
2234 2234 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2235 2235 except FileWalkError:
2236 2236 slowpath = True
2237 2237
2238 2238 # We decided to fall back to the slowpath because at least one
2239 2239 # of the paths was not a file. Check to see if at least one of them
2240 2240 # existed in history, otherwise simply return
2241 2241 for path in match.files():
2242 2242 if path == '.' or path in repo.store:
2243 2243 break
2244 2244 else:
2245 2245 return []
2246 2246
2247 2247 if slowpath:
2248 2248 # We have to read the changelog to match filenames against
2249 2249 # changed files
2250 2250
2251 2251 if follow:
2252 2252 raise error.Abort(_('can only follow copies/renames for explicit '
2253 2253 'filenames'))
2254 2254
2255 2255 # The slow path checks files modified in every changeset.
2256 2256 # This is really slow on large repos, so compute the set lazily.
2257 2257 class lazywantedset(object):
2258 2258 def __init__(self):
2259 2259 self.set = set()
2260 2260 self.revs = set(revs)
2261 2261
2262 2262 # No need to worry about locality here because it will be accessed
2263 2263 # in the same order as the increasing window below.
2264 2264 def __contains__(self, value):
2265 2265 if value in self.set:
2266 2266 return True
2267 2267 elif not value in self.revs:
2268 2268 return False
2269 2269 else:
2270 2270 self.revs.discard(value)
2271 2271 ctx = change(value)
2272 2272 matches = filter(match, ctx.files())
2273 2273 if matches:
2274 2274 fncache[value] = matches
2275 2275 self.set.add(value)
2276 2276 return True
2277 2277 return False
2278 2278
2279 2279 def discard(self, value):
2280 2280 self.revs.discard(value)
2281 2281 self.set.discard(value)
2282 2282
2283 2283 wanted = lazywantedset()
2284 2284
2285 2285 # it might be worthwhile to do this in the iterator if the rev range
2286 2286 # is descending and the prune args are all within that range
2287 2287 for rev in opts.get('prune', ()):
2288 2288 rev = repo[rev].rev()
2289 2289 ff = _followfilter(repo)
2290 2290 stop = min(revs[0], revs[-1])
2291 2291 for x in xrange(rev, stop - 1, -1):
2292 2292 if ff.match(x):
2293 2293 wanted = wanted - [x]
2294 2294
2295 2295 # Now that wanted is correctly initialized, we can iterate over the
2296 2296 # revision range, yielding only revisions in wanted.
2297 2297 def iterate():
2298 2298 if follow and match.always():
2299 2299 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2300 2300 def want(rev):
2301 2301 return ff.match(rev) and rev in wanted
2302 2302 else:
2303 2303 def want(rev):
2304 2304 return rev in wanted
2305 2305
2306 2306 it = iter(revs)
2307 2307 stopiteration = False
2308 2308 for windowsize in increasingwindows():
2309 2309 nrevs = []
2310 2310 for i in xrange(windowsize):
2311 2311 rev = next(it, None)
2312 2312 if rev is None:
2313 2313 stopiteration = True
2314 2314 break
2315 2315 elif want(rev):
2316 2316 nrevs.append(rev)
2317 2317 for rev in sorted(nrevs):
2318 2318 fns = fncache.get(rev)
2319 2319 ctx = change(rev)
2320 2320 if not fns:
2321 2321 def fns_generator():
2322 2322 for f in ctx.files():
2323 2323 if match(f):
2324 2324 yield f
2325 2325 fns = fns_generator()
2326 2326 prepare(ctx, fns)
2327 2327 for rev in nrevs:
2328 2328 yield change(rev)
2329 2329
2330 2330 if stopiteration:
2331 2331 break
2332 2332
2333 2333 return iterate()
2334 2334
2335 2335 def _makefollowlogfilematcher(repo, files, followfirst):
2336 2336 # When displaying a revision with --patch --follow FILE, we have
2337 2337 # to know which file of the revision must be diffed. With
2338 2338 # --follow, we want the names of the ancestors of FILE in the
2339 2339 # revision, stored in "fcache". "fcache" is populated by
2340 2340 # reproducing the graph traversal already done by --follow revset
2341 2341 # and relating revs to file names (which is not "correct" but
2342 2342 # good enough).
2343 2343 fcache = {}
2344 2344 fcacheready = [False]
2345 2345 pctx = repo['.']
2346 2346
2347 2347 def populate():
2348 2348 for fn in files:
2349 2349 fctx = pctx[fn]
2350 2350 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
2351 2351 for c in fctx.ancestors(followfirst=followfirst):
2352 2352 fcache.setdefault(c.rev(), set()).add(c.path())
2353 2353
2354 2354 def filematcher(rev):
2355 2355 if not fcacheready[0]:
2356 2356 # Lazy initialization
2357 2357 fcacheready[0] = True
2358 2358 populate()
2359 2359 return scmutil.matchfiles(repo, fcache.get(rev, []))
2360 2360
2361 2361 return filematcher
2362 2362
2363 2363 def _makenofollowlogfilematcher(repo, pats, opts):
2364 2364 '''hook for extensions to override the filematcher for non-follow cases'''
2365 2365 return None
2366 2366
2367 2367 def _makelogrevset(repo, pats, opts, revs):
2368 2368 """Return (expr, filematcher) where expr is a revset string built
2369 2369 from log options and file patterns or None. If --stat or --patch
2370 2370 are not passed filematcher is None. Otherwise it is a callable
2371 2371 taking a revision number and returning a match objects filtering
2372 2372 the files to be detailed when displaying the revision.
2373 2373 """
2374 2374 opt2revset = {
2375 2375 'no_merges': ('not merge()', None),
2376 2376 'only_merges': ('merge()', None),
2377 2377 '_ancestors': ('ancestors(%(val)s)', None),
2378 2378 '_fancestors': ('_firstancestors(%(val)s)', None),
2379 2379 '_descendants': ('descendants(%(val)s)', None),
2380 2380 '_fdescendants': ('_firstdescendants(%(val)s)', None),
2381 2381 '_matchfiles': ('_matchfiles(%(val)s)', None),
2382 2382 'date': ('date(%(val)r)', None),
2383 2383 'branch': ('branch(%(val)r)', ' or '),
2384 2384 '_patslog': ('filelog(%(val)r)', ' or '),
2385 2385 '_patsfollow': ('follow(%(val)r)', ' or '),
2386 2386 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
2387 2387 'keyword': ('keyword(%(val)r)', ' or '),
2388 2388 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
2389 2389 'user': ('user(%(val)r)', ' or '),
2390 2390 }
2391 2391
2392 2392 opts = dict(opts)
2393 2393 # follow or not follow?
2394 2394 follow = opts.get('follow') or opts.get('follow_first')
2395 2395 if opts.get('follow_first'):
2396 2396 followfirst = 1
2397 2397 else:
2398 2398 followfirst = 0
2399 2399 # --follow with FILE behavior depends on revs...
2400 2400 it = iter(revs)
2401 2401 startrev = next(it)
2402 2402 followdescendants = startrev < next(it, startrev)
2403 2403
2404 2404 # branch and only_branch are really aliases and must be handled at
2405 2405 # the same time
2406 2406 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2407 2407 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2408 2408 # pats/include/exclude are passed to match.match() directly in
2409 2409 # _matchfiles() revset but walkchangerevs() builds its matcher with
2410 2410 # scmutil.match(). The difference is input pats are globbed on
2411 2411 # platforms without shell expansion (windows).
2412 2412 wctx = repo[None]
2413 2413 match, pats = scmutil.matchandpats(wctx, pats, opts)
2414 2414 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2415 2415 opts.get('removed'))
2416 2416 if not slowpath:
2417 2417 for f in match.files():
2418 2418 if follow and f not in wctx:
2419 2419 # If the file exists, it may be a directory, so let it
2420 2420 # take the slow path.
2421 2421 if os.path.exists(repo.wjoin(f)):
2422 2422 slowpath = True
2423 2423 continue
2424 2424 else:
2425 2425 raise error.Abort(_('cannot follow file not in parent '
2426 2426 'revision: "%s"') % f)
2427 2427 filelog = repo.file(f)
2428 2428 if not filelog:
2429 2429 # A zero count may be a directory or deleted file, so
2430 2430 # try to find matching entries on the slow path.
2431 2431 if follow:
2432 2432 raise error.Abort(
2433 2433 _('cannot follow nonexistent file: "%s"') % f)
2434 2434 slowpath = True
2435 2435
2436 2436 # We decided to fall back to the slowpath because at least one
2437 2437 # of the paths was not a file. Check to see if at least one of them
2438 2438 # existed in history - in that case, we'll continue down the
2439 2439 # slowpath; otherwise, we can turn off the slowpath
2440 2440 if slowpath:
2441 2441 for path in match.files():
2442 2442 if path == '.' or path in repo.store:
2443 2443 break
2444 2444 else:
2445 2445 slowpath = False
2446 2446
2447 2447 fpats = ('_patsfollow', '_patsfollowfirst')
2448 2448 fnopats = (('_ancestors', '_fancestors'),
2449 2449 ('_descendants', '_fdescendants'))
2450 2450 if slowpath:
2451 2451 # See walkchangerevs() slow path.
2452 2452 #
2453 2453 # pats/include/exclude cannot be represented as separate
2454 2454 # revset expressions as their filtering logic applies at file
2455 2455 # level. For instance "-I a -X a" matches a revision touching
2456 2456 # "a" and "b" while "file(a) and not file(b)" does
2457 2457 # not. Besides, filesets are evaluated against the working
2458 2458 # directory.
2459 2459 matchargs = ['r:', 'd:relpath']
2460 2460 for p in pats:
2461 2461 matchargs.append('p:' + p)
2462 2462 for p in opts.get('include', []):
2463 2463 matchargs.append('i:' + p)
2464 2464 for p in opts.get('exclude', []):
2465 2465 matchargs.append('x:' + p)
2466 2466 matchargs = ','.join(('%r' % p) for p in matchargs)
2467 2467 opts['_matchfiles'] = matchargs
2468 2468 if follow:
2469 2469 opts[fnopats[0][followfirst]] = '.'
2470 2470 else:
2471 2471 if follow:
2472 2472 if pats:
2473 2473 # follow() revset interprets its file argument as a
2474 2474 # manifest entry, so use match.files(), not pats.
2475 2475 opts[fpats[followfirst]] = list(match.files())
2476 2476 else:
2477 2477 op = fnopats[followdescendants][followfirst]
2478 2478 opts[op] = 'rev(%d)' % startrev
2479 2479 else:
2480 2480 opts['_patslog'] = list(pats)
2481 2481
2482 2482 filematcher = None
2483 2483 if opts.get('patch') or opts.get('stat'):
2484 2484 # When following files, track renames via a special matcher.
2485 2485 # If we're forced to take the slowpath it means we're following
2486 2486 # at least one pattern/directory, so don't bother with rename tracking.
2487 2487 if follow and not match.always() and not slowpath:
2488 2488 # _makefollowlogfilematcher expects its files argument to be
2489 2489 # relative to the repo root, so use match.files(), not pats.
2490 2490 filematcher = _makefollowlogfilematcher(repo, match.files(),
2491 2491 followfirst)
2492 2492 else:
2493 2493 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2494 2494 if filematcher is None:
2495 2495 filematcher = lambda rev: match
2496 2496
2497 2497 expr = []
2498 2498 for op, val in sorted(opts.iteritems()):
2499 2499 if not val:
2500 2500 continue
2501 2501 if op not in opt2revset:
2502 2502 continue
2503 2503 revop, andor = opt2revset[op]
2504 2504 if '%(val)' not in revop:
2505 2505 expr.append(revop)
2506 2506 else:
2507 2507 if not isinstance(val, list):
2508 2508 e = revop % {'val': val}
2509 2509 else:
2510 2510 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2511 2511 expr.append(e)
2512 2512
2513 2513 if expr:
2514 2514 expr = '(' + ' and '.join(expr) + ')'
2515 2515 else:
2516 2516 expr = None
2517 2517 return expr, filematcher
2518 2518
2519 2519 def _logrevs(repo, opts):
2520 2520 # Default --rev value depends on --follow but --follow behavior
2521 2521 # depends on revisions resolved from --rev...
2522 2522 follow = opts.get('follow') or opts.get('follow_first')
2523 2523 if opts.get('rev'):
2524 2524 revs = scmutil.revrange(repo, opts['rev'])
2525 2525 elif follow and repo.dirstate.p1() == nullid:
2526 2526 revs = smartset.baseset()
2527 2527 elif follow:
2528 2528 revs = repo.revs('reverse(:.)')
2529 2529 else:
2530 2530 revs = smartset.spanset(repo)
2531 2531 revs.reverse()
2532 2532 return revs
2533 2533
2534 2534 def getgraphlogrevs(repo, pats, opts):
2535 2535 """Return (revs, expr, filematcher) where revs is an iterable of
2536 2536 revision numbers, expr is a revset string built from log options
2537 2537 and file patterns or None, and used to filter 'revs'. If --stat or
2538 2538 --patch are not passed filematcher is None. Otherwise it is a
2539 2539 callable taking a revision number and returning a match objects
2540 2540 filtering the files to be detailed when displaying the revision.
2541 2541 """
2542 2542 limit = loglimit(opts)
2543 2543 revs = _logrevs(repo, opts)
2544 2544 if not revs:
2545 2545 return smartset.baseset(), None, None
2546 2546 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2547 2547 if opts.get('rev'):
2548 2548 # User-specified revs might be unsorted, but don't sort before
2549 2549 # _makelogrevset because it might depend on the order of revs
2550 2550 if not (revs.isdescending() or revs.istopo()):
2551 2551 revs.sort(reverse=True)
2552 2552 if expr:
2553 2553 matcher = revset.match(repo.ui, expr)
2554 2554 revs = matcher(repo, revs)
2555 2555 if limit is not None:
2556 2556 limitedrevs = []
2557 2557 for idx, rev in enumerate(revs):
2558 2558 if idx >= limit:
2559 2559 break
2560 2560 limitedrevs.append(rev)
2561 2561 revs = smartset.baseset(limitedrevs)
2562 2562
2563 2563 return revs, expr, filematcher
2564 2564
2565 2565 def getlogrevs(repo, pats, opts):
2566 2566 """Return (revs, expr, filematcher) where revs is an iterable of
2567 2567 revision numbers, expr is a revset string built from log options
2568 2568 and file patterns or None, and used to filter 'revs'. If --stat or
2569 2569 --patch are not passed filematcher is None. Otherwise it is a
2570 2570 callable taking a revision number and returning a match objects
2571 2571 filtering the files to be detailed when displaying the revision.
2572 2572 """
2573 2573 limit = loglimit(opts)
2574 2574 revs = _logrevs(repo, opts)
2575 2575 if not revs:
2576 2576 return smartset.baseset([]), None, None
2577 2577 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2578 2578 if expr:
2579 2579 matcher = revset.match(repo.ui, expr)
2580 2580 revs = matcher(repo, revs)
2581 2581 if limit is not None:
2582 2582 limitedrevs = []
2583 2583 for idx, r in enumerate(revs):
2584 2584 if limit <= idx:
2585 2585 break
2586 2586 limitedrevs.append(r)
2587 2587 revs = smartset.baseset(limitedrevs)
2588 2588
2589 2589 return revs, expr, filematcher
2590 2590
2591 2591 def _parselinerangelogopt(repo, opts):
2592 2592 """Parse --line-range log option and return a list of tuples (filename,
2593 2593 (fromline, toline)).
2594 2594 """
2595 2595 linerangebyfname = []
2596 2596 for pat in opts.get('line_range', []):
2597 2597 try:
2598 2598 pat, linerange = pat.rsplit(',', 1)
2599 2599 except ValueError:
2600 2600 raise error.Abort(_('malformatted line-range pattern %s') % pat)
2601 2601 try:
2602 2602 fromline, toline = map(int, linerange.split('-'))
2603 2603 except ValueError:
2604 2604 raise error.Abort(_("invalid line range for %s") % pat)
2605 2605 msg = _("line range pattern '%s' must match exactly one file") % pat
2606 2606 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
2607 2607 linerangebyfname.append(
2608 2608 (fname, util.processlinerange(fromline, toline)))
2609 2609 return linerangebyfname
2610 2610
2611 2611 def getloglinerangerevs(repo, userrevs, opts):
2612 2612 """Return (revs, filematcher, hunksfilter).
2613 2613
2614 2614 "revs" are revisions obtained by processing "line-range" log options and
2615 2615 walking block ancestors of each specified file/line-range.
2616 2616
2617 2617 "filematcher(rev) -> match" is a factory function returning a match object
2618 2618 for a given revision for file patterns specified in --line-range option.
2619 2619 If neither --stat nor --patch options are passed, "filematcher" is None.
2620 2620
2621 2621 "hunksfilter(rev) -> filterfn(fctx, hunks)" is a factory function
2622 2622 returning a hunks filtering function.
2623 2623 If neither --stat nor --patch options are passed, "filterhunks" is None.
2624 2624 """
2625 2625 wctx = repo[None]
2626 2626
2627 2627 # Two-levels map of "rev -> file ctx -> [line range]".
2628 2628 linerangesbyrev = {}
2629 2629 for fname, (fromline, toline) in _parselinerangelogopt(repo, opts):
2630 2630 fctx = wctx.filectx(fname)
2631 2631 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
2632 2632 rev = fctx.introrev()
2633 2633 if rev not in userrevs:
2634 2634 continue
2635 2635 linerangesbyrev.setdefault(
2636 2636 rev, {}).setdefault(
2637 2637 fctx.path(), []).append(linerange)
2638 2638
2639 2639 filematcher = None
2640 2640 hunksfilter = None
2641 2641 if opts.get('patch') or opts.get('stat'):
2642 2642
2643 2643 def nofilterhunksfn(fctx, hunks):
2644 2644 return hunks
2645 2645
2646 2646 def hunksfilter(rev):
2647 2647 fctxlineranges = linerangesbyrev.get(rev)
2648 2648 if fctxlineranges is None:
2649 2649 return nofilterhunksfn
2650 2650
2651 2651 def filterfn(fctx, hunks):
2652 2652 lineranges = fctxlineranges.get(fctx.path())
2653 2653 if lineranges is not None:
2654 2654 for hr, lines in hunks:
2655 2655 if any(mdiff.hunkinrange(hr[2:], lr)
2656 2656 for lr in lineranges):
2657 2657 yield hr, lines
2658 2658 else:
2659 2659 for hunk in hunks:
2660 2660 yield hunk
2661 2661
2662 2662 return filterfn
2663 2663
2664 2664 def filematcher(rev):
2665 2665 files = list(linerangesbyrev.get(rev, []))
2666 2666 return scmutil.matchfiles(repo, files)
2667 2667
2668 2668 revs = sorted(linerangesbyrev, reverse=True)
2669 2669
2670 2670 return revs, filematcher, hunksfilter
2671 2671
2672 2672 def _graphnodeformatter(ui, displayer):
2673 2673 spec = ui.config('ui', 'graphnodetemplate')
2674 2674 if not spec:
2675 2675 return templatekw.showgraphnode # fast path for "{graphnode}"
2676 2676
2677 2677 spec = templater.unquotestring(spec)
2678 2678 templ = formatter.maketemplater(ui, spec)
2679 2679 cache = {}
2680 2680 if isinstance(displayer, changeset_templater):
2681 2681 cache = displayer.cache # reuse cache of slow templates
2682 2682 props = templatekw.keywords.copy()
2683 2683 props['templ'] = templ
2684 2684 props['cache'] = cache
2685 2685 def formatnode(repo, ctx):
2686 2686 props['ctx'] = ctx
2687 2687 props['repo'] = repo
2688 2688 props['ui'] = repo.ui
2689 2689 props['revcache'] = {}
2690 2690 return templ.render(props)
2691 2691 return formatnode
2692 2692
2693 2693 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2694 2694 filematcher=None, props=None):
2695 2695 props = props or {}
2696 2696 formatnode = _graphnodeformatter(ui, displayer)
2697 2697 state = graphmod.asciistate()
2698 2698 styles = state['styles']
2699 2699
2700 2700 # only set graph styling if HGPLAIN is not set.
2701 2701 if ui.plain('graph'):
2702 2702 # set all edge styles to |, the default pre-3.8 behaviour
2703 2703 styles.update(dict.fromkeys(styles, '|'))
2704 2704 else:
2705 2705 edgetypes = {
2706 2706 'parent': graphmod.PARENT,
2707 2707 'grandparent': graphmod.GRANDPARENT,
2708 2708 'missing': graphmod.MISSINGPARENT
2709 2709 }
2710 2710 for name, key in edgetypes.items():
2711 2711 # experimental config: experimental.graphstyle.*
2712 2712 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2713 2713 styles[key])
2714 2714 if not styles[key]:
2715 2715 styles[key] = None
2716 2716
2717 2717 # experimental config: experimental.graphshorten
2718 2718 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2719 2719
2720 2720 for rev, type, ctx, parents in dag:
2721 2721 char = formatnode(repo, ctx)
2722 2722 copies = None
2723 2723 if getrenamed and ctx.rev():
2724 2724 copies = []
2725 2725 for fn in ctx.files():
2726 2726 rename = getrenamed(fn, ctx.rev())
2727 2727 if rename:
2728 2728 copies.append((fn, rename[0]))
2729 2729 revmatchfn = None
2730 2730 if filematcher is not None:
2731 2731 revmatchfn = filematcher(ctx.rev())
2732 2732 edges = edgefn(type, char, state, rev, parents)
2733 2733 firstedge = next(edges)
2734 2734 width = firstedge[2]
2735 2735 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
2736 2736 _graphwidth=width, **props)
2737 2737 lines = displayer.hunk.pop(rev).split('\n')
2738 2738 if not lines[-1]:
2739 2739 del lines[-1]
2740 2740 displayer.flush(ctx)
2741 2741 for type, char, width, coldata in itertools.chain([firstedge], edges):
2742 2742 graphmod.ascii(ui, state, type, char, lines, coldata)
2743 2743 lines = []
2744 2744 displayer.close()
2745 2745
2746 2746 def graphlog(ui, repo, pats, opts):
2747 2747 # Parameters are identical to log command ones
2748 2748 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2749 2749 revdag = graphmod.dagwalker(repo, revs)
2750 2750
2751 2751 getrenamed = None
2752 2752 if opts.get('copies'):
2753 2753 endrev = None
2754 2754 if opts.get('rev'):
2755 2755 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2756 2756 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2757 2757
2758 2758 ui.pager('log')
2759 2759 displayer = show_changeset(ui, repo, opts, buffered=True)
2760 2760 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2761 2761 filematcher)
2762 2762
2763 2763 def checkunsupportedgraphflags(pats, opts):
2764 2764 for op in ["newest_first"]:
2765 2765 if op in opts and opts[op]:
2766 2766 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2767 2767 % op.replace("_", "-"))
2768 2768
2769 2769 def graphrevs(repo, nodes, opts):
2770 2770 limit = loglimit(opts)
2771 2771 nodes.reverse()
2772 2772 if limit is not None:
2773 2773 nodes = nodes[:limit]
2774 2774 return graphmod.nodes(repo, nodes)
2775 2775
2776 2776 def add(ui, repo, match, prefix, explicitonly, **opts):
2777 2777 join = lambda f: os.path.join(prefix, f)
2778 2778 bad = []
2779 2779
2780 2780 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2781 2781 names = []
2782 2782 wctx = repo[None]
2783 2783 cca = None
2784 2784 abort, warn = scmutil.checkportabilityalert(ui)
2785 2785 if abort or warn:
2786 2786 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2787 2787
2788 2788 badmatch = matchmod.badmatch(match, badfn)
2789 2789 dirstate = repo.dirstate
2790 2790 # We don't want to just call wctx.walk here, since it would return a lot of
2791 2791 # clean files, which we aren't interested in and takes time.
2792 2792 for f in sorted(dirstate.walk(badmatch, subrepos=sorted(wctx.substate),
2793 2793 unknown=True, ignored=False, full=False)):
2794 2794 exact = match.exact(f)
2795 2795 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2796 2796 if cca:
2797 2797 cca(f)
2798 2798 names.append(f)
2799 2799 if ui.verbose or not exact:
2800 2800 ui.status(_('adding %s\n') % match.rel(f))
2801 2801
2802 2802 for subpath in sorted(wctx.substate):
2803 2803 sub = wctx.sub(subpath)
2804 2804 try:
2805 2805 submatch = matchmod.subdirmatcher(subpath, match)
2806 2806 if opts.get(r'subrepos'):
2807 2807 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2808 2808 else:
2809 2809 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2810 2810 except error.LookupError:
2811 2811 ui.status(_("skipping missing subrepository: %s\n")
2812 2812 % join(subpath))
2813 2813
2814 2814 if not opts.get(r'dry_run'):
2815 2815 rejected = wctx.add(names, prefix)
2816 2816 bad.extend(f for f in rejected if f in match.files())
2817 2817 return bad
2818 2818
2819 2819 def addwebdirpath(repo, serverpath, webconf):
2820 2820 webconf[serverpath] = repo.root
2821 2821 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2822 2822
2823 2823 for r in repo.revs('filelog("path:.hgsub")'):
2824 2824 ctx = repo[r]
2825 2825 for subpath in ctx.substate:
2826 2826 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2827 2827
2828 2828 def forget(ui, repo, match, prefix, explicitonly):
2829 2829 join = lambda f: os.path.join(prefix, f)
2830 2830 bad = []
2831 2831 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2832 2832 wctx = repo[None]
2833 2833 forgot = []
2834 2834
2835 2835 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2836 2836 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2837 2837 if explicitonly:
2838 2838 forget = [f for f in forget if match.exact(f)]
2839 2839
2840 2840 for subpath in sorted(wctx.substate):
2841 2841 sub = wctx.sub(subpath)
2842 2842 try:
2843 2843 submatch = matchmod.subdirmatcher(subpath, match)
2844 2844 subbad, subforgot = sub.forget(submatch, prefix)
2845 2845 bad.extend([subpath + '/' + f for f in subbad])
2846 2846 forgot.extend([subpath + '/' + f for f in subforgot])
2847 2847 except error.LookupError:
2848 2848 ui.status(_("skipping missing subrepository: %s\n")
2849 2849 % join(subpath))
2850 2850
2851 2851 if not explicitonly:
2852 2852 for f in match.files():
2853 2853 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2854 2854 if f not in forgot:
2855 2855 if repo.wvfs.exists(f):
2856 2856 # Don't complain if the exact case match wasn't given.
2857 2857 # But don't do this until after checking 'forgot', so
2858 2858 # that subrepo files aren't normalized, and this op is
2859 2859 # purely from data cached by the status walk above.
2860 2860 if repo.dirstate.normalize(f) in repo.dirstate:
2861 2861 continue
2862 2862 ui.warn(_('not removing %s: '
2863 2863 'file is already untracked\n')
2864 2864 % match.rel(f))
2865 2865 bad.append(f)
2866 2866
2867 2867 for f in forget:
2868 2868 if ui.verbose or not match.exact(f):
2869 2869 ui.status(_('removing %s\n') % match.rel(f))
2870 2870
2871 2871 rejected = wctx.forget(forget, prefix)
2872 2872 bad.extend(f for f in rejected if f in match.files())
2873 2873 forgot.extend(f for f in forget if f not in rejected)
2874 2874 return bad, forgot
2875 2875
2876 2876 def files(ui, ctx, m, fm, fmt, subrepos):
2877 2877 rev = ctx.rev()
2878 2878 ret = 1
2879 2879 ds = ctx.repo().dirstate
2880 2880
2881 2881 for f in ctx.matches(m):
2882 2882 if rev is None and ds[f] == 'r':
2883 2883 continue
2884 2884 fm.startitem()
2885 2885 if ui.verbose:
2886 2886 fc = ctx[f]
2887 2887 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2888 2888 fm.data(abspath=f)
2889 2889 fm.write('path', fmt, m.rel(f))
2890 2890 ret = 0
2891 2891
2892 2892 for subpath in sorted(ctx.substate):
2893 2893 submatch = matchmod.subdirmatcher(subpath, m)
2894 2894 if (subrepos or m.exact(subpath) or any(submatch.files())):
2895 2895 sub = ctx.sub(subpath)
2896 2896 try:
2897 2897 recurse = m.exact(subpath) or subrepos
2898 2898 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2899 2899 ret = 0
2900 2900 except error.LookupError:
2901 2901 ui.status(_("skipping missing subrepository: %s\n")
2902 2902 % m.abs(subpath))
2903 2903
2904 2904 return ret
2905 2905
2906 2906 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2907 2907 join = lambda f: os.path.join(prefix, f)
2908 2908 ret = 0
2909 2909 s = repo.status(match=m, clean=True)
2910 2910 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2911 2911
2912 2912 wctx = repo[None]
2913 2913
2914 2914 if warnings is None:
2915 2915 warnings = []
2916 2916 warn = True
2917 2917 else:
2918 2918 warn = False
2919 2919
2920 2920 subs = sorted(wctx.substate)
2921 2921 total = len(subs)
2922 2922 count = 0
2923 2923 for subpath in subs:
2924 2924 count += 1
2925 2925 submatch = matchmod.subdirmatcher(subpath, m)
2926 2926 if subrepos or m.exact(subpath) or any(submatch.files()):
2927 2927 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2928 2928 sub = wctx.sub(subpath)
2929 2929 try:
2930 2930 if sub.removefiles(submatch, prefix, after, force, subrepos,
2931 2931 warnings):
2932 2932 ret = 1
2933 2933 except error.LookupError:
2934 2934 warnings.append(_("skipping missing subrepository: %s\n")
2935 2935 % join(subpath))
2936 2936 ui.progress(_('searching'), None)
2937 2937
2938 2938 # warn about failure to delete explicit files/dirs
2939 2939 deleteddirs = util.dirs(deleted)
2940 2940 files = m.files()
2941 2941 total = len(files)
2942 2942 count = 0
2943 2943 for f in files:
2944 2944 def insubrepo():
2945 2945 for subpath in wctx.substate:
2946 2946 if f.startswith(subpath + '/'):
2947 2947 return True
2948 2948 return False
2949 2949
2950 2950 count += 1
2951 2951 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2952 2952 isdir = f in deleteddirs or wctx.hasdir(f)
2953 2953 if (f in repo.dirstate or isdir or f == '.'
2954 2954 or insubrepo() or f in subs):
2955 2955 continue
2956 2956
2957 2957 if repo.wvfs.exists(f):
2958 2958 if repo.wvfs.isdir(f):
2959 2959 warnings.append(_('not removing %s: no tracked files\n')
2960 2960 % m.rel(f))
2961 2961 else:
2962 2962 warnings.append(_('not removing %s: file is untracked\n')
2963 2963 % m.rel(f))
2964 2964 # missing files will generate a warning elsewhere
2965 2965 ret = 1
2966 2966 ui.progress(_('deleting'), None)
2967 2967
2968 2968 if force:
2969 2969 list = modified + deleted + clean + added
2970 2970 elif after:
2971 2971 list = deleted
2972 2972 remaining = modified + added + clean
2973 2973 total = len(remaining)
2974 2974 count = 0
2975 2975 for f in remaining:
2976 2976 count += 1
2977 2977 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2978 2978 warnings.append(_('not removing %s: file still exists\n')
2979 2979 % m.rel(f))
2980 2980 ret = 1
2981 2981 ui.progress(_('skipping'), None)
2982 2982 else:
2983 2983 list = deleted + clean
2984 2984 total = len(modified) + len(added)
2985 2985 count = 0
2986 2986 for f in modified:
2987 2987 count += 1
2988 2988 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2989 2989 warnings.append(_('not removing %s: file is modified (use -f'
2990 2990 ' to force removal)\n') % m.rel(f))
2991 2991 ret = 1
2992 2992 for f in added:
2993 2993 count += 1
2994 2994 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2995 2995 warnings.append(_("not removing %s: file has been marked for add"
2996 2996 " (use 'hg forget' to undo add)\n") % m.rel(f))
2997 2997 ret = 1
2998 2998 ui.progress(_('skipping'), None)
2999 2999
3000 3000 list = sorted(list)
3001 3001 total = len(list)
3002 3002 count = 0
3003 3003 for f in list:
3004 3004 count += 1
3005 3005 if ui.verbose or not m.exact(f):
3006 3006 ui.progress(_('deleting'), count, total=total, unit=_('files'))
3007 3007 ui.status(_('removing %s\n') % m.rel(f))
3008 3008 ui.progress(_('deleting'), None)
3009 3009
3010 3010 with repo.wlock():
3011 3011 if not after:
3012 3012 for f in list:
3013 3013 if f in added:
3014 3014 continue # we never unlink added files on remove
3015 3015 repo.wvfs.unlinkpath(f, ignoremissing=True)
3016 3016 repo[None].forget(list)
3017 3017
3018 3018 if warn:
3019 3019 for warning in warnings:
3020 3020 ui.warn(warning)
3021 3021
3022 3022 return ret
3023 3023
3024 3024 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
3025 3025 err = 1
3026 3026
3027 3027 def write(path):
3028 3028 filename = None
3029 3029 if fntemplate:
3030 3030 filename = makefilename(repo, fntemplate, ctx.node(),
3031 3031 pathname=os.path.join(prefix, path))
3032 3032 with formatter.maybereopen(basefm, filename, opts) as fm:
3033 3033 data = ctx[path].data()
3034 3034 if opts.get('decode'):
3035 3035 data = repo.wwritedata(path, data)
3036 3036 fm.startitem()
3037 3037 fm.write('data', '%s', data)
3038 3038 fm.data(abspath=path, path=matcher.rel(path))
3039 3039
3040 3040 # Automation often uses hg cat on single files, so special case it
3041 3041 # for performance to avoid the cost of parsing the manifest.
3042 3042 if len(matcher.files()) == 1 and not matcher.anypats():
3043 3043 file = matcher.files()[0]
3044 3044 mfl = repo.manifestlog
3045 3045 mfnode = ctx.manifestnode()
3046 3046 try:
3047 3047 if mfnode and mfl[mfnode].find(file)[0]:
3048 3048 write(file)
3049 3049 return 0
3050 3050 except KeyError:
3051 3051 pass
3052 3052
3053 3053 for abs in ctx.walk(matcher):
3054 3054 write(abs)
3055 3055 err = 0
3056 3056
3057 3057 for subpath in sorted(ctx.substate):
3058 3058 sub = ctx.sub(subpath)
3059 3059 try:
3060 3060 submatch = matchmod.subdirmatcher(subpath, matcher)
3061 3061
3062 3062 if not sub.cat(submatch, basefm, fntemplate,
3063 3063 os.path.join(prefix, sub._path), **opts):
3064 3064 err = 0
3065 3065 except error.RepoLookupError:
3066 3066 ui.status(_("skipping missing subrepository: %s\n")
3067 3067 % os.path.join(prefix, subpath))
3068 3068
3069 3069 return err
3070 3070
3071 3071 def commit(ui, repo, commitfunc, pats, opts):
3072 3072 '''commit the specified files or all outstanding changes'''
3073 3073 date = opts.get('date')
3074 3074 if date:
3075 3075 opts['date'] = util.parsedate(date)
3076 3076 message = logmessage(ui, opts)
3077 3077 matcher = scmutil.match(repo[None], pats, opts)
3078 3078
3079 3079 dsguard = None
3080 3080 # extract addremove carefully -- this function can be called from a command
3081 3081 # that doesn't support addremove
3082 3082 if opts.get('addremove'):
3083 3083 dsguard = dirstateguard.dirstateguard(repo, 'commit')
3084 3084 with dsguard or util.nullcontextmanager():
3085 3085 if dsguard:
3086 3086 if scmutil.addremove(repo, matcher, "", opts) != 0:
3087 3087 raise error.Abort(
3088 3088 _("failed to mark all new/missing files as added/removed"))
3089 3089
3090 3090 return commitfunc(ui, repo, message, matcher, opts)
3091 3091
3092 3092 def samefile(f, ctx1, ctx2):
3093 3093 if f in ctx1.manifest():
3094 3094 a = ctx1.filectx(f)
3095 3095 if f in ctx2.manifest():
3096 3096 b = ctx2.filectx(f)
3097 3097 return (not a.cmp(b)
3098 3098 and a.flags() == b.flags())
3099 3099 else:
3100 3100 return False
3101 3101 else:
3102 3102 return f not in ctx2.manifest()
3103 3103
3104 3104 def amend(ui, repo, old, extra, pats, opts):
3105 3105 # avoid cycle context -> subrepo -> cmdutil
3106 3106 from . import context
3107 3107
3108 3108 # amend will reuse the existing user if not specified, but the obsolete
3109 3109 # marker creation requires that the current user's name is specified.
3110 3110 if obsolete.isenabled(repo, obsolete.createmarkersopt):
3111 3111 ui.username() # raise exception if username not set
3112 3112
3113 3113 ui.note(_('amending changeset %s\n') % old)
3114 3114 base = old.p1()
3115 3115
3116 3116 with repo.wlock(), repo.lock(), repo.transaction('amend'):
3117 3117 # Participating changesets:
3118 3118 #
3119 3119 # wctx o - workingctx that contains changes from working copy
3120 3120 # | to go into amending commit
3121 3121 # |
3122 3122 # old o - changeset to amend
3123 3123 # |
3124 3124 # base o - first parent of the changeset to amend
3125 3125 wctx = repo[None]
3126 3126
3127 3127 # Update extra dict from amended commit (e.g. to preserve graft
3128 3128 # source)
3129 3129 extra.update(old.extra())
3130 3130
3131 3131 # Also update it from the from the wctx
3132 3132 extra.update(wctx.extra())
3133 3133
3134 3134 user = opts.get('user') or old.user()
3135 3135 date = opts.get('date') or old.date()
3136 3136
3137 3137 # Parse the date to allow comparison between date and old.date()
3138 3138 date = util.parsedate(date)
3139 3139
3140 3140 if len(old.parents()) > 1:
3141 3141 # ctx.files() isn't reliable for merges, so fall back to the
3142 3142 # slower repo.status() method
3143 3143 files = set([fn for st in repo.status(base, old)[:3]
3144 3144 for fn in st])
3145 3145 else:
3146 3146 files = set(old.files())
3147 3147
3148 3148 # add/remove the files to the working copy if the "addremove" option
3149 3149 # was specified.
3150 3150 matcher = scmutil.match(wctx, pats, opts)
3151 3151 if (opts.get('addremove')
3152 3152 and scmutil.addremove(repo, matcher, "", opts)):
3153 3153 raise error.Abort(
3154 3154 _("failed to mark all new/missing files as added/removed"))
3155 3155
3156 3156 filestoamend = set(f for f in wctx.files() if matcher(f))
3157 3157
3158 3158 changes = (len(filestoamend) > 0)
3159 3159 if changes:
3160 3160 # Recompute copies (avoid recording a -> b -> a)
3161 3161 copied = copies.pathcopies(base, wctx, matcher)
3162 3162 if old.p2:
3163 3163 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
3164 3164
3165 3165 # Prune files which were reverted by the updates: if old
3166 3166 # introduced file X and the file was renamed in the working
3167 3167 # copy, then those two files are the same and
3168 3168 # we can discard X from our list of files. Likewise if X
3169 3169 # was deleted, it's no longer relevant
3170 3170 files.update(filestoamend)
3171 3171 files = [f for f in files if not samefile(f, wctx, base)]
3172 3172
3173 3173 def filectxfn(repo, ctx_, path):
3174 3174 try:
3175 3175 # If the file being considered is not amongst the files
3176 3176 # to be amended, we should return the file context from the
3177 3177 # old changeset. This avoids issues when only some files in
3178 3178 # the working copy are being amended but there are also
3179 3179 # changes to other files from the old changeset.
3180 3180 if path not in filestoamend:
3181 3181 return old.filectx(path)
3182 3182
3183 3183 fctx = wctx[path]
3184 3184
3185 3185 # Return None for removed files.
3186 3186 if not fctx.exists():
3187 3187 return None
3188 3188
3189 3189 flags = fctx.flags()
3190 3190 mctx = context.memfilectx(repo,
3191 3191 fctx.path(), fctx.data(),
3192 3192 islink='l' in flags,
3193 3193 isexec='x' in flags,
3194 3194 copied=copied.get(path))
3195 3195 return mctx
3196 3196 except KeyError:
3197 3197 return None
3198 3198 else:
3199 3199 ui.note(_('copying changeset %s to %s\n') % (old, base))
3200 3200
3201 3201 # Use version of files as in the old cset
3202 3202 def filectxfn(repo, ctx_, path):
3203 3203 try:
3204 3204 return old.filectx(path)
3205 3205 except KeyError:
3206 3206 return None
3207 3207
3208 3208 # See if we got a message from -m or -l, if not, open the editor with
3209 3209 # the message of the changeset to amend.
3210 3210 message = logmessage(ui, opts)
3211 3211
3212 3212 editform = mergeeditform(old, 'commit.amend')
3213 3213 editor = getcommiteditor(editform=editform,
3214 3214 **pycompat.strkwargs(opts))
3215 3215
3216 3216 if not message:
3217 3217 editor = getcommiteditor(edit=True, editform=editform)
3218 3218 message = old.description()
3219 3219
3220 3220 pureextra = extra.copy()
3221 3221 extra['amend_source'] = old.hex()
3222 3222
3223 3223 new = context.memctx(repo,
3224 3224 parents=[base.node(), old.p2().node()],
3225 3225 text=message,
3226 3226 files=files,
3227 3227 filectxfn=filectxfn,
3228 3228 user=user,
3229 3229 date=date,
3230 3230 extra=extra,
3231 3231 editor=editor)
3232 3232
3233 3233 newdesc = changelog.stripdesc(new.description())
3234 3234 if ((not changes)
3235 3235 and newdesc == old.description()
3236 3236 and user == old.user()
3237 3237 and date == old.date()
3238 3238 and pureextra == old.extra()):
3239 3239 # nothing changed. continuing here would create a new node
3240 3240 # anyway because of the amend_source noise.
3241 3241 #
3242 3242 # This not what we expect from amend.
3243 3243 return old.node()
3244 3244
3245 3245 if opts.get('secret'):
3246 3246 commitphase = 'secret'
3247 3247 else:
3248 3248 commitphase = old.phase()
3249 3249 overrides = {('phases', 'new-commit'): commitphase}
3250 3250 with ui.configoverride(overrides, 'amend'):
3251 3251 newid = repo.commitctx(new)
3252 3252
3253 3253 # Reroute the working copy parent to the new changeset
3254 3254 repo.setparents(newid, nullid)
3255 3255 mapping = {old.node(): (newid,)}
3256 3256 obsmetadata = None
3257 3257 if opts.get('note'):
3258 3258 obsmetadata = {'note': opts['note']}
3259 3259 scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata)
3260 3260
3261 3261 # Fixing the dirstate because localrepo.commitctx does not update
3262 3262 # it. This is rather convenient because we did not need to update
3263 3263 # the dirstate for all the files in the new commit which commitctx
3264 3264 # could have done if it updated the dirstate. Now, we can
3265 3265 # selectively update the dirstate only for the amended files.
3266 3266 dirstate = repo.dirstate
3267 3267
3268 3268 # Update the state of the files which were added and
3269 3269 # and modified in the amend to "normal" in the dirstate.
3270 3270 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3271 3271 for f in normalfiles:
3272 3272 dirstate.normal(f)
3273 3273
3274 3274 # Update the state of files which were removed in the amend
3275 3275 # to "removed" in the dirstate.
3276 3276 removedfiles = set(wctx.removed()) & filestoamend
3277 3277 for f in removedfiles:
3278 3278 dirstate.drop(f)
3279 3279
3280 3280 return newid
3281 3281
3282 3282 def commiteditor(repo, ctx, subs, editform=''):
3283 3283 if ctx.description():
3284 3284 return ctx.description()
3285 3285 return commitforceeditor(repo, ctx, subs, editform=editform,
3286 3286 unchangedmessagedetection=True)
3287 3287
3288 3288 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
3289 3289 editform='', unchangedmessagedetection=False):
3290 3290 if not extramsg:
3291 3291 extramsg = _("Leave message empty to abort commit.")
3292 3292
3293 3293 forms = [e for e in editform.split('.') if e]
3294 3294 forms.insert(0, 'changeset')
3295 3295 templatetext = None
3296 3296 while forms:
3297 3297 ref = '.'.join(forms)
3298 3298 if repo.ui.config('committemplate', ref):
3299 3299 templatetext = committext = buildcommittemplate(
3300 3300 repo, ctx, subs, extramsg, ref)
3301 3301 break
3302 3302 forms.pop()
3303 3303 else:
3304 3304 committext = buildcommittext(repo, ctx, subs, extramsg)
3305 3305
3306 3306 # run editor in the repository root
3307 3307 olddir = pycompat.getcwd()
3308 3308 os.chdir(repo.root)
3309 3309
3310 3310 # make in-memory changes visible to external process
3311 3311 tr = repo.currenttransaction()
3312 3312 repo.dirstate.write(tr)
3313 3313 pending = tr and tr.writepending() and repo.root
3314 3314
3315 3315 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
3316 3316 editform=editform, pending=pending,
3317 3317 repopath=repo.path, action='commit')
3318 3318 text = editortext
3319 3319
3320 3320 # strip away anything below this special string (used for editors that want
3321 3321 # to display the diff)
3322 3322 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3323 3323 if stripbelow:
3324 3324 text = text[:stripbelow.start()]
3325 3325
3326 3326 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
3327 3327 os.chdir(olddir)
3328 3328
3329 3329 if finishdesc:
3330 3330 text = finishdesc(text)
3331 3331 if not text.strip():
3332 3332 raise error.Abort(_("empty commit message"))
3333 3333 if unchangedmessagedetection and editortext == templatetext:
3334 3334 raise error.Abort(_("commit message unchanged"))
3335 3335
3336 3336 return text
3337 3337
3338 3338 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3339 3339 ui = repo.ui
3340 3340 spec = formatter.templatespec(ref, None, None)
3341 3341 t = changeset_templater(ui, repo, spec, None, {}, False)
3342 3342 t.t.cache.update((k, templater.unquotestring(v))
3343 3343 for k, v in repo.ui.configitems('committemplate'))
3344 3344
3345 3345 if not extramsg:
3346 3346 extramsg = '' # ensure that extramsg is string
3347 3347
3348 3348 ui.pushbuffer()
3349 3349 t.show(ctx, extramsg=extramsg)
3350 3350 return ui.popbuffer()
3351 3351
3352 3352 def hgprefix(msg):
3353 3353 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
3354 3354
3355 3355 def buildcommittext(repo, ctx, subs, extramsg):
3356 3356 edittext = []
3357 3357 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3358 3358 if ctx.description():
3359 3359 edittext.append(ctx.description())
3360 3360 edittext.append("")
3361 3361 edittext.append("") # Empty line between message and comments.
3362 3362 edittext.append(hgprefix(_("Enter commit message."
3363 3363 " Lines beginning with 'HG:' are removed.")))
3364 3364 edittext.append(hgprefix(extramsg))
3365 3365 edittext.append("HG: --")
3366 3366 edittext.append(hgprefix(_("user: %s") % ctx.user()))
3367 3367 if ctx.p2():
3368 3368 edittext.append(hgprefix(_("branch merge")))
3369 3369 if ctx.branch():
3370 3370 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
3371 3371 if bookmarks.isactivewdirparent(repo):
3372 3372 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
3373 3373 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
3374 3374 edittext.extend([hgprefix(_("added %s") % f) for f in added])
3375 3375 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
3376 3376 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
3377 3377 if not added and not modified and not removed:
3378 3378 edittext.append(hgprefix(_("no files changed")))
3379 3379 edittext.append("")
3380 3380
3381 3381 return "\n".join(edittext)
3382 3382
3383 3383 def commitstatus(repo, node, branch, bheads=None, opts=None):
3384 3384 if opts is None:
3385 3385 opts = {}
3386 3386 ctx = repo[node]
3387 3387 parents = ctx.parents()
3388 3388
3389 3389 if (not opts.get('amend') and bheads and node not in bheads and not
3390 3390 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3391 3391 repo.ui.status(_('created new head\n'))
3392 3392 # The message is not printed for initial roots. For the other
3393 3393 # changesets, it is printed in the following situations:
3394 3394 #
3395 3395 # Par column: for the 2 parents with ...
3396 3396 # N: null or no parent
3397 3397 # B: parent is on another named branch
3398 3398 # C: parent is a regular non head changeset
3399 3399 # H: parent was a branch head of the current branch
3400 3400 # Msg column: whether we print "created new head" message
3401 3401 # In the following, it is assumed that there already exists some
3402 3402 # initial branch heads of the current branch, otherwise nothing is
3403 3403 # printed anyway.
3404 3404 #
3405 3405 # Par Msg Comment
3406 3406 # N N y additional topo root
3407 3407 #
3408 3408 # B N y additional branch root
3409 3409 # C N y additional topo head
3410 3410 # H N n usual case
3411 3411 #
3412 3412 # B B y weird additional branch root
3413 3413 # C B y branch merge
3414 3414 # H B n merge with named branch
3415 3415 #
3416 3416 # C C y additional head from merge
3417 3417 # C H n merge with a head
3418 3418 #
3419 3419 # H H n head merge: head count decreases
3420 3420
3421 3421 if not opts.get('close_branch'):
3422 3422 for r in parents:
3423 3423 if r.closesbranch() and r.branch() == branch:
3424 3424 repo.ui.status(_('reopening closed branch head %d\n') % r)
3425 3425
3426 3426 if repo.ui.debugflag:
3427 3427 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3428 3428 elif repo.ui.verbose:
3429 3429 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3430 3430
3431 3431 def postcommitstatus(repo, pats, opts):
3432 3432 return repo.status(match=scmutil.match(repo[None], pats, opts))
3433 3433
3434 3434 def revert(ui, repo, ctx, parents, *pats, **opts):
3435 3435 parent, p2 = parents
3436 3436 node = ctx.node()
3437 3437
3438 3438 mf = ctx.manifest()
3439 3439 if node == p2:
3440 3440 parent = p2
3441 3441
3442 3442 # need all matching names in dirstate and manifest of target rev,
3443 3443 # so have to walk both. do not print errors if files exist in one
3444 3444 # but not other. in both cases, filesets should be evaluated against
3445 3445 # workingctx to get consistent result (issue4497). this means 'set:**'
3446 3446 # cannot be used to select missing files from target rev.
3447 3447
3448 3448 # `names` is a mapping for all elements in working copy and target revision
3449 3449 # The mapping is in the form:
3450 3450 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3451 3451 names = {}
3452 3452
3453 3453 with repo.wlock():
3454 3454 ## filling of the `names` mapping
3455 3455 # walk dirstate to fill `names`
3456 3456
3457 3457 interactive = opts.get('interactive', False)
3458 3458 wctx = repo[None]
3459 3459 m = scmutil.match(wctx, pats, opts)
3460 3460
3461 3461 # we'll need this later
3462 3462 targetsubs = sorted(s for s in wctx.substate if m(s))
3463 3463
3464 3464 if not m.always():
3465 3465 matcher = matchmod.badmatch(m, lambda x, y: False)
3466 3466 for abs in wctx.walk(matcher):
3467 3467 names[abs] = m.rel(abs), m.exact(abs)
3468 3468
3469 3469 # walk target manifest to fill `names`
3470 3470
3471 3471 def badfn(path, msg):
3472 3472 if path in names:
3473 3473 return
3474 3474 if path in ctx.substate:
3475 3475 return
3476 3476 path_ = path + '/'
3477 3477 for f in names:
3478 3478 if f.startswith(path_):
3479 3479 return
3480 3480 ui.warn("%s: %s\n" % (m.rel(path), msg))
3481 3481
3482 3482 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3483 3483 if abs not in names:
3484 3484 names[abs] = m.rel(abs), m.exact(abs)
3485 3485
3486 3486 # Find status of all file in `names`.
3487 3487 m = scmutil.matchfiles(repo, names)
3488 3488
3489 3489 changes = repo.status(node1=node, match=m,
3490 3490 unknown=True, ignored=True, clean=True)
3491 3491 else:
3492 3492 changes = repo.status(node1=node, match=m)
3493 3493 for kind in changes:
3494 3494 for abs in kind:
3495 3495 names[abs] = m.rel(abs), m.exact(abs)
3496 3496
3497 3497 m = scmutil.matchfiles(repo, names)
3498 3498
3499 3499 modified = set(changes.modified)
3500 3500 added = set(changes.added)
3501 3501 removed = set(changes.removed)
3502 3502 _deleted = set(changes.deleted)
3503 3503 unknown = set(changes.unknown)
3504 3504 unknown.update(changes.ignored)
3505 3505 clean = set(changes.clean)
3506 3506 modadded = set()
3507 3507
3508 3508 # We need to account for the state of the file in the dirstate,
3509 3509 # even when we revert against something else than parent. This will
3510 3510 # slightly alter the behavior of revert (doing back up or not, delete
3511 3511 # or just forget etc).
3512 3512 if parent == node:
3513 3513 dsmodified = modified
3514 3514 dsadded = added
3515 3515 dsremoved = removed
3516 3516 # store all local modifications, useful later for rename detection
3517 3517 localchanges = dsmodified | dsadded
3518 3518 modified, added, removed = set(), set(), set()
3519 3519 else:
3520 3520 changes = repo.status(node1=parent, match=m)
3521 3521 dsmodified = set(changes.modified)
3522 3522 dsadded = set(changes.added)
3523 3523 dsremoved = set(changes.removed)
3524 3524 # store all local modifications, useful later for rename detection
3525 3525 localchanges = dsmodified | dsadded
3526 3526
3527 3527 # only take into account for removes between wc and target
3528 3528 clean |= dsremoved - removed
3529 3529 dsremoved &= removed
3530 3530 # distinct between dirstate remove and other
3531 3531 removed -= dsremoved
3532 3532
3533 3533 modadded = added & dsmodified
3534 3534 added -= modadded
3535 3535
3536 3536 # tell newly modified apart.
3537 3537 dsmodified &= modified
3538 3538 dsmodified |= modified & dsadded # dirstate added may need backup
3539 3539 modified -= dsmodified
3540 3540
3541 3541 # We need to wait for some post-processing to update this set
3542 3542 # before making the distinction. The dirstate will be used for
3543 3543 # that purpose.
3544 3544 dsadded = added
3545 3545
3546 3546 # in case of merge, files that are actually added can be reported as
3547 3547 # modified, we need to post process the result
3548 3548 if p2 != nullid:
3549 3549 mergeadd = set(dsmodified)
3550 3550 for path in dsmodified:
3551 3551 if path in mf:
3552 3552 mergeadd.remove(path)
3553 3553 dsadded |= mergeadd
3554 3554 dsmodified -= mergeadd
3555 3555
3556 3556 # if f is a rename, update `names` to also revert the source
3557 3557 cwd = repo.getcwd()
3558 3558 for f in localchanges:
3559 3559 src = repo.dirstate.copied(f)
3560 3560 # XXX should we check for rename down to target node?
3561 3561 if src and src not in names and repo.dirstate[src] == 'r':
3562 3562 dsremoved.add(src)
3563 3563 names[src] = (repo.pathto(src, cwd), True)
3564 3564
3565 3565 # determine the exact nature of the deleted changesets
3566 3566 deladded = set(_deleted)
3567 3567 for path in _deleted:
3568 3568 if path in mf:
3569 3569 deladded.remove(path)
3570 3570 deleted = _deleted - deladded
3571 3571
3572 3572 # distinguish between file to forget and the other
3573 3573 added = set()
3574 3574 for abs in dsadded:
3575 3575 if repo.dirstate[abs] != 'a':
3576 3576 added.add(abs)
3577 3577 dsadded -= added
3578 3578
3579 3579 for abs in deladded:
3580 3580 if repo.dirstate[abs] == 'a':
3581 3581 dsadded.add(abs)
3582 3582 deladded -= dsadded
3583 3583
3584 3584 # For files marked as removed, we check if an unknown file is present at
3585 3585 # the same path. If a such file exists it may need to be backed up.
3586 3586 # Making the distinction at this stage helps have simpler backup
3587 3587 # logic.
3588 3588 removunk = set()
3589 3589 for abs in removed:
3590 3590 target = repo.wjoin(abs)
3591 3591 if os.path.lexists(target):
3592 3592 removunk.add(abs)
3593 3593 removed -= removunk
3594 3594
3595 3595 dsremovunk = set()
3596 3596 for abs in dsremoved:
3597 3597 target = repo.wjoin(abs)
3598 3598 if os.path.lexists(target):
3599 3599 dsremovunk.add(abs)
3600 3600 dsremoved -= dsremovunk
3601 3601
3602 3602 # action to be actually performed by revert
3603 3603 # (<list of file>, message>) tuple
3604 3604 actions = {'revert': ([], _('reverting %s\n')),
3605 3605 'add': ([], _('adding %s\n')),
3606 3606 'remove': ([], _('removing %s\n')),
3607 3607 'drop': ([], _('removing %s\n')),
3608 3608 'forget': ([], _('forgetting %s\n')),
3609 3609 'undelete': ([], _('undeleting %s\n')),
3610 3610 'noop': (None, _('no changes needed to %s\n')),
3611 3611 'unknown': (None, _('file not managed: %s\n')),
3612 3612 }
3613 3613
3614 3614 # "constant" that convey the backup strategy.
3615 3615 # All set to `discard` if `no-backup` is set do avoid checking
3616 3616 # no_backup lower in the code.
3617 3617 # These values are ordered for comparison purposes
3618 3618 backupinteractive = 3 # do backup if interactively modified
3619 3619 backup = 2 # unconditionally do backup
3620 3620 check = 1 # check if the existing file differs from target
3621 3621 discard = 0 # never do backup
3622 3622 if opts.get('no_backup'):
3623 3623 backupinteractive = backup = check = discard
3624 3624 if interactive:
3625 3625 dsmodifiedbackup = backupinteractive
3626 3626 else:
3627 3627 dsmodifiedbackup = backup
3628 3628 tobackup = set()
3629 3629
3630 3630 backupanddel = actions['remove']
3631 3631 if not opts.get('no_backup'):
3632 3632 backupanddel = actions['drop']
3633 3633
3634 3634 disptable = (
3635 3635 # dispatch table:
3636 3636 # file state
3637 3637 # action
3638 3638 # make backup
3639 3639
3640 3640 ## Sets that results that will change file on disk
3641 3641 # Modified compared to target, no local change
3642 3642 (modified, actions['revert'], discard),
3643 3643 # Modified compared to target, but local file is deleted
3644 3644 (deleted, actions['revert'], discard),
3645 3645 # Modified compared to target, local change
3646 3646 (dsmodified, actions['revert'], dsmodifiedbackup),
3647 3647 # Added since target
3648 3648 (added, actions['remove'], discard),
3649 3649 # Added in working directory
3650 3650 (dsadded, actions['forget'], discard),
3651 3651 # Added since target, have local modification
3652 3652 (modadded, backupanddel, backup),
3653 3653 # Added since target but file is missing in working directory
3654 3654 (deladded, actions['drop'], discard),
3655 3655 # Removed since target, before working copy parent
3656 3656 (removed, actions['add'], discard),
3657 3657 # Same as `removed` but an unknown file exists at the same path
3658 3658 (removunk, actions['add'], check),
3659 3659 # Removed since targe, marked as such in working copy parent
3660 3660 (dsremoved, actions['undelete'], discard),
3661 3661 # Same as `dsremoved` but an unknown file exists at the same path
3662 3662 (dsremovunk, actions['undelete'], check),
3663 3663 ## the following sets does not result in any file changes
3664 3664 # File with no modification
3665 3665 (clean, actions['noop'], discard),
3666 3666 # Existing file, not tracked anywhere
3667 3667 (unknown, actions['unknown'], discard),
3668 3668 )
3669 3669
3670 3670 for abs, (rel, exact) in sorted(names.items()):
3671 3671 # target file to be touch on disk (relative to cwd)
3672 3672 target = repo.wjoin(abs)
3673 3673 # search the entry in the dispatch table.
3674 3674 # if the file is in any of these sets, it was touched in the working
3675 3675 # directory parent and we are sure it needs to be reverted.
3676 3676 for table, (xlist, msg), dobackup in disptable:
3677 3677 if abs not in table:
3678 3678 continue
3679 3679 if xlist is not None:
3680 3680 xlist.append(abs)
3681 3681 if dobackup:
3682 3682 # If in interactive mode, don't automatically create
3683 3683 # .orig files (issue4793)
3684 3684 if dobackup == backupinteractive:
3685 3685 tobackup.add(abs)
3686 3686 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3687 3687 bakname = scmutil.origpath(ui, repo, rel)
3688 3688 ui.note(_('saving current version of %s as %s\n') %
3689 3689 (rel, bakname))
3690 3690 if not opts.get('dry_run'):
3691 3691 if interactive:
3692 3692 util.copyfile(target, bakname)
3693 3693 else:
3694 3694 util.rename(target, bakname)
3695 3695 if ui.verbose or not exact:
3696 3696 if not isinstance(msg, basestring):
3697 3697 msg = msg(abs)
3698 3698 ui.status(msg % rel)
3699 3699 elif exact:
3700 3700 ui.warn(msg % rel)
3701 3701 break
3702 3702
3703 3703 if not opts.get('dry_run'):
3704 3704 needdata = ('revert', 'add', 'undelete')
3705 3705 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3706 3706 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3707 3707
3708 3708 if targetsubs:
3709 3709 # Revert the subrepos on the revert list
3710 3710 for sub in targetsubs:
3711 3711 try:
3712 3712 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3713 3713 except KeyError:
3714 3714 raise error.Abort("subrepository '%s' does not exist in %s!"
3715 3715 % (sub, short(ctx.node())))
3716 3716
3717 3717 def _revertprefetch(repo, ctx, *files):
3718 3718 """Let extension changing the storage layer prefetch content"""
3719 3719
3720 3720 def _performrevert(repo, parents, ctx, actions, interactive=False,
3721 3721 tobackup=None):
3722 3722 """function that actually perform all the actions computed for revert
3723 3723
3724 3724 This is an independent function to let extension to plug in and react to
3725 3725 the imminent revert.
3726 3726
3727 3727 Make sure you have the working directory locked when calling this function.
3728 3728 """
3729 3729 parent, p2 = parents
3730 3730 node = ctx.node()
3731 3731 excluded_files = []
3732 3732 matcher_opts = {"exclude": excluded_files}
3733 3733
3734 3734 def checkout(f):
3735 3735 fc = ctx[f]
3736 3736 repo.wwrite(f, fc.data(), fc.flags())
3737 3737
3738 3738 def doremove(f):
3739 3739 try:
3740 3740 repo.wvfs.unlinkpath(f)
3741 3741 except OSError:
3742 3742 pass
3743 3743 repo.dirstate.remove(f)
3744 3744
3745 3745 audit_path = pathutil.pathauditor(repo.root, cached=True)
3746 3746 for f in actions['forget'][0]:
3747 3747 if interactive:
3748 3748 choice = repo.ui.promptchoice(
3749 3749 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3750 3750 if choice == 0:
3751 3751 repo.dirstate.drop(f)
3752 3752 else:
3753 3753 excluded_files.append(repo.wjoin(f))
3754 3754 else:
3755 3755 repo.dirstate.drop(f)
3756 3756 for f in actions['remove'][0]:
3757 3757 audit_path(f)
3758 3758 if interactive:
3759 3759 choice = repo.ui.promptchoice(
3760 3760 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3761 3761 if choice == 0:
3762 3762 doremove(f)
3763 3763 else:
3764 3764 excluded_files.append(repo.wjoin(f))
3765 3765 else:
3766 3766 doremove(f)
3767 3767 for f in actions['drop'][0]:
3768 3768 audit_path(f)
3769 3769 repo.dirstate.remove(f)
3770 3770
3771 3771 normal = None
3772 3772 if node == parent:
3773 3773 # We're reverting to our parent. If possible, we'd like status
3774 3774 # to report the file as clean. We have to use normallookup for
3775 3775 # merges to avoid losing information about merged/dirty files.
3776 3776 if p2 != nullid:
3777 3777 normal = repo.dirstate.normallookup
3778 3778 else:
3779 3779 normal = repo.dirstate.normal
3780 3780
3781 3781 newlyaddedandmodifiedfiles = set()
3782 3782 if interactive:
3783 3783 # Prompt the user for changes to revert
3784 3784 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3785 3785 m = scmutil.match(ctx, torevert, matcher_opts)
3786 3786 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3787 3787 diffopts.nodates = True
3788 3788 diffopts.git = True
3789 3789 operation = 'discard'
3790 3790 reversehunks = True
3791 3791 if node != parent:
3792 3792 operation = 'revert'
3793 3793 reversehunks = repo.ui.configbool('experimental',
3794 3794 'revertalternateinteractivemode')
3795 3795 if reversehunks:
3796 3796 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3797 3797 else:
3798 3798 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3799 3799 originalchunks = patch.parsepatch(diff)
3800 3800
3801 3801 try:
3802 3802
3803 3803 chunks, opts = recordfilter(repo.ui, originalchunks,
3804 3804 operation=operation)
3805 3805 if reversehunks:
3806 3806 chunks = patch.reversehunks(chunks)
3807 3807
3808 3808 except error.PatchError as err:
3809 3809 raise error.Abort(_('error parsing patch: %s') % err)
3810 3810
3811 3811 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3812 3812 if tobackup is None:
3813 3813 tobackup = set()
3814 3814 # Apply changes
3815 3815 fp = stringio()
3816 3816 for c in chunks:
3817 3817 # Create a backup file only if this hunk should be backed up
3818 3818 if ishunk(c) and c.header.filename() in tobackup:
3819 3819 abs = c.header.filename()
3820 3820 target = repo.wjoin(abs)
3821 3821 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3822 3822 util.copyfile(target, bakname)
3823 3823 tobackup.remove(abs)
3824 3824 c.write(fp)
3825 3825 dopatch = fp.tell()
3826 3826 fp.seek(0)
3827 3827 if dopatch:
3828 3828 try:
3829 3829 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3830 3830 except error.PatchError as err:
3831 3831 raise error.Abort(str(err))
3832 3832 del fp
3833 3833 else:
3834 3834 for f in actions['revert'][0]:
3835 3835 checkout(f)
3836 3836 if normal:
3837 3837 normal(f)
3838 3838
3839 3839 for f in actions['add'][0]:
3840 3840 # Don't checkout modified files, they are already created by the diff
3841 3841 if f not in newlyaddedandmodifiedfiles:
3842 3842 checkout(f)
3843 3843 repo.dirstate.add(f)
3844 3844
3845 3845 normal = repo.dirstate.normallookup
3846 3846 if node == parent and p2 == nullid:
3847 3847 normal = repo.dirstate.normal
3848 3848 for f in actions['undelete'][0]:
3849 3849 checkout(f)
3850 3850 normal(f)
3851 3851
3852 3852 copied = copies.pathcopies(repo[parent], ctx)
3853 3853
3854 3854 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3855 3855 if f in copied:
3856 3856 repo.dirstate.copy(copied[f], f)
3857 3857
3858 3858 class command(registrar.command):
3859 3859 def _doregister(self, func, name, *args, **kwargs):
3860 3860 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3861 3861 return super(command, self)._doregister(func, name, *args, **kwargs)
3862 3862
3863 3863 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3864 3864 # commands.outgoing. "missing" is "missing" of the result of
3865 3865 # "findcommonoutgoing()"
3866 3866 outgoinghooks = util.hooks()
3867 3867
3868 3868 # a list of (ui, repo) functions called by commands.summary
3869 3869 summaryhooks = util.hooks()
3870 3870
3871 3871 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3872 3872 #
3873 3873 # functions should return tuple of booleans below, if 'changes' is None:
3874 3874 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3875 3875 #
3876 3876 # otherwise, 'changes' is a tuple of tuples below:
3877 3877 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3878 3878 # - (desturl, destbranch, destpeer, outgoing)
3879 3879 summaryremotehooks = util.hooks()
3880 3880
3881 3881 # A list of state files kept by multistep operations like graft.
3882 3882 # Since graft cannot be aborted, it is considered 'clearable' by update.
3883 3883 # note: bisect is intentionally excluded
3884 3884 # (state file, clearable, allowcommit, error, hint)
3885 3885 unfinishedstates = [
3886 3886 ('graftstate', True, False, _('graft in progress'),
3887 3887 _("use 'hg graft --continue' or 'hg update' to abort")),
3888 3888 ('updatestate', True, False, _('last update was interrupted'),
3889 3889 _("use 'hg update' to get a consistent checkout"))
3890 3890 ]
3891 3891
3892 3892 def checkunfinished(repo, commit=False):
3893 3893 '''Look for an unfinished multistep operation, like graft, and abort
3894 3894 if found. It's probably good to check this right before
3895 3895 bailifchanged().
3896 3896 '''
3897 3897 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3898 3898 if commit and allowcommit:
3899 3899 continue
3900 3900 if repo.vfs.exists(f):
3901 3901 raise error.Abort(msg, hint=hint)
3902 3902
3903 3903 def clearunfinished(repo):
3904 3904 '''Check for unfinished operations (as above), and clear the ones
3905 3905 that are clearable.
3906 3906 '''
3907 3907 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3908 3908 if not clearable and repo.vfs.exists(f):
3909 3909 raise error.Abort(msg, hint=hint)
3910 3910 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3911 3911 if clearable and repo.vfs.exists(f):
3912 3912 util.unlink(repo.vfs.join(f))
3913 3913
3914 3914 afterresolvedstates = [
3915 3915 ('graftstate',
3916 3916 _('hg graft --continue')),
3917 3917 ]
3918 3918
3919 3919 def howtocontinue(repo):
3920 3920 '''Check for an unfinished operation and return the command to finish
3921 3921 it.
3922 3922
3923 3923 afterresolvedstates tuples define a .hg/{file} and the corresponding
3924 3924 command needed to finish it.
3925 3925
3926 3926 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3927 3927 a boolean.
3928 3928 '''
3929 3929 contmsg = _("continue: %s")
3930 3930 for f, msg in afterresolvedstates:
3931 3931 if repo.vfs.exists(f):
3932 3932 return contmsg % msg, True
3933 3933 if repo[None].dirty(missing=True, merge=False, branch=False):
3934 3934 return contmsg % _("hg commit"), False
3935 3935 return None, None
3936 3936
3937 3937 def checkafterresolved(repo):
3938 3938 '''Inform the user about the next action after completing hg resolve
3939 3939
3940 3940 If there's a matching afterresolvedstates, howtocontinue will yield
3941 3941 repo.ui.warn as the reporter.
3942 3942
3943 3943 Otherwise, it will yield repo.ui.note.
3944 3944 '''
3945 3945 msg, warning = howtocontinue(repo)
3946 3946 if msg is not None:
3947 3947 if warning:
3948 3948 repo.ui.warn("%s\n" % msg)
3949 3949 else:
3950 3950 repo.ui.note("%s\n" % msg)
3951 3951
3952 3952 def wrongtooltocontinue(repo, task):
3953 3953 '''Raise an abort suggesting how to properly continue if there is an
3954 3954 active task.
3955 3955
3956 3956 Uses howtocontinue() to find the active task.
3957 3957
3958 3958 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3959 3959 a hint.
3960 3960 '''
3961 3961 after = howtocontinue(repo)
3962 3962 hint = None
3963 3963 if after[1]:
3964 3964 hint = after[0]
3965 3965 raise error.Abort(_('no %s in progress') % task, hint=hint)
@@ -1,1013 +1,1013 b''
1 1 #if windows
2 2 $ PYTHONPATH="$TESTDIR/../contrib;$PYTHONPATH"
3 3 #else
4 4 $ PYTHONPATH="$TESTDIR/../contrib:$PYTHONPATH"
5 5 #endif
6 6 $ export PYTHONPATH
7 7
8 8 typical client does not want echo-back messages, so test without it:
9 9
10 10 $ grep -v '^promptecho ' < $HGRCPATH >> $HGRCPATH.new
11 11 $ mv $HGRCPATH.new $HGRCPATH
12 12
13 13 $ hg init repo
14 14 $ cd repo
15 15
16 16 >>> from __future__ import absolute_import, print_function
17 17 >>> import os
18 18 >>> import sys
19 19 >>> from hgclient import check, readchannel, runcommand
20 20 >>> @check
21 21 ... def hellomessage(server):
22 22 ... ch, data = readchannel(server)
23 23 ... print('%c, %r' % (ch, data))
24 24 ... # run an arbitrary command to make sure the next thing the server
25 25 ... # sends isn't part of the hello message
26 26 ... runcommand(server, ['id'])
27 27 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
28 28 *** runcommand id
29 29 000000000000 tip
30 30
31 31 >>> from hgclient import check
32 32 >>> @check
33 33 ... def unknowncommand(server):
34 34 ... server.stdin.write('unknowncommand\n')
35 35 abort: unknown command unknowncommand
36 36
37 37 >>> from hgclient import check, readchannel, runcommand
38 38 >>> @check
39 39 ... def checkruncommand(server):
40 40 ... # hello block
41 41 ... readchannel(server)
42 42 ...
43 43 ... # no args
44 44 ... runcommand(server, [])
45 45 ...
46 46 ... # global options
47 47 ... runcommand(server, ['id', '--quiet'])
48 48 ...
49 49 ... # make sure global options don't stick through requests
50 50 ... runcommand(server, ['id'])
51 51 ...
52 52 ... # --config
53 53 ... runcommand(server, ['id', '--config', 'ui.quiet=True'])
54 54 ...
55 55 ... # make sure --config doesn't stick
56 56 ... runcommand(server, ['id'])
57 57 ...
58 58 ... # negative return code should be masked
59 59 ... runcommand(server, ['id', '-runknown'])
60 60 *** runcommand
61 61 Mercurial Distributed SCM
62 62
63 63 basic commands:
64 64
65 65 add add the specified files on the next commit
66 66 annotate show changeset information by line for each file
67 67 clone make a copy of an existing repository
68 68 commit commit the specified files or all outstanding changes
69 69 diff diff repository (or selected files)
70 70 export dump the header and diffs for one or more changesets
71 71 forget forget the specified files on the next commit
72 72 init create a new repository in the given directory
73 73 log show revision history of entire repository or files
74 74 merge merge another revision into working directory
75 75 pull pull changes from the specified source
76 76 push push changes to the specified destination
77 77 remove remove the specified files on the next commit
78 78 serve start stand-alone webserver
79 79 status show changed files in the working directory
80 80 summary summarize working directory state
81 81 update update working directory (or switch revisions)
82 82
83 83 (use 'hg help' for the full list of commands or 'hg -v' for details)
84 84 *** runcommand id --quiet
85 85 000000000000
86 86 *** runcommand id
87 87 000000000000 tip
88 88 *** runcommand id --config ui.quiet=True
89 89 000000000000
90 90 *** runcommand id
91 91 000000000000 tip
92 92 *** runcommand id -runknown
93 93 abort: unknown revision 'unknown'!
94 94 [255]
95 95
96 96 >>> from hgclient import check, readchannel
97 97 >>> @check
98 98 ... def inputeof(server):
99 99 ... readchannel(server)
100 100 ... server.stdin.write('runcommand\n')
101 101 ... # close stdin while server is waiting for input
102 102 ... server.stdin.close()
103 103 ...
104 104 ... # server exits with 1 if the pipe closed while reading the command
105 105 ... print('server exit code =', server.wait())
106 106 server exit code = 1
107 107
108 108 >>> from hgclient import check, readchannel, runcommand, stringio
109 109 >>> @check
110 110 ... def serverinput(server):
111 111 ... readchannel(server)
112 112 ...
113 113 ... patch = """
114 114 ... # HG changeset patch
115 115 ... # User test
116 116 ... # Date 0 0
117 117 ... # Node ID c103a3dec114d882c98382d684d8af798d09d857
118 118 ... # Parent 0000000000000000000000000000000000000000
119 119 ... 1
120 120 ...
121 121 ... diff -r 000000000000 -r c103a3dec114 a
122 122 ... --- /dev/null Thu Jan 01 00:00:00 1970 +0000
123 123 ... +++ b/a Thu Jan 01 00:00:00 1970 +0000
124 124 ... @@ -0,0 +1,1 @@
125 125 ... +1
126 126 ... """
127 127 ...
128 128 ... runcommand(server, ['import', '-'], input=stringio(patch))
129 129 ... runcommand(server, ['log'])
130 130 *** runcommand import -
131 131 applying patch from stdin
132 132 *** runcommand log
133 133 changeset: 0:eff892de26ec
134 134 tag: tip
135 135 user: test
136 136 date: Thu Jan 01 00:00:00 1970 +0000
137 137 summary: 1
138 138
139 139
140 140 check that "histedit --commands=-" can read rules from the input channel:
141 141
142 142 >>> import cStringIO
143 143 >>> from hgclient import check, readchannel, runcommand
144 144 >>> @check
145 145 ... def serverinput(server):
146 146 ... readchannel(server)
147 147 ... rules = 'pick eff892de26ec\n'
148 148 ... runcommand(server, ['histedit', '0', '--commands=-',
149 149 ... '--config', 'extensions.histedit='],
150 150 ... input=cStringIO.StringIO(rules))
151 151 *** runcommand histedit 0 --commands=- --config extensions.histedit=
152 152
153 153 check that --cwd doesn't persist between requests:
154 154
155 155 $ mkdir foo
156 156 $ touch foo/bar
157 157 >>> from hgclient import check, readchannel, runcommand
158 158 >>> @check
159 159 ... def cwd(server):
160 160 ... readchannel(server)
161 161 ... runcommand(server, ['--cwd', 'foo', 'st', 'bar'])
162 162 ... runcommand(server, ['st', 'foo/bar'])
163 163 *** runcommand --cwd foo st bar
164 164 ? bar
165 165 *** runcommand st foo/bar
166 166 ? foo/bar
167 167
168 168 $ rm foo/bar
169 169
170 170
171 171 check that local configs for the cached repo aren't inherited when -R is used:
172 172
173 173 $ cat <<EOF >> .hg/hgrc
174 174 > [ui]
175 175 > foo = bar
176 176 > EOF
177 177
178 178 >>> from hgclient import check, readchannel, runcommand, sep
179 179 >>> @check
180 180 ... def localhgrc(server):
181 181 ... readchannel(server)
182 182 ...
183 183 ... # the cached repo local hgrc contains ui.foo=bar, so showconfig should
184 184 ... # show it
185 185 ... runcommand(server, ['showconfig'], outfilter=sep)
186 186 ...
187 187 ... # but not for this repo
188 188 ... runcommand(server, ['init', 'foo'])
189 189 ... runcommand(server, ['-R', 'foo', 'showconfig', 'ui', 'defaults'])
190 190 *** runcommand showconfig
191 191 bundle.mainreporoot=$TESTTMP/repo
192 192 devel.all-warnings=true
193 193 devel.default-date=0 0
194 194 extensions.fsmonitor= (fsmonitor !)
195 195 largefiles.usercache=$TESTTMP/.cache/largefiles
196 196 ui.slash=True
197 197 ui.interactive=False
198 198 ui.mergemarkers=detailed
199 199 ui.usehttp2=true (?)
200 200 ui.foo=bar
201 201 ui.nontty=true
202 202 web.address=localhost
203 203 web\.ipv6=(?:True|False) (re)
204 204 *** runcommand init foo
205 205 *** runcommand -R foo showconfig ui defaults
206 206 ui.slash=True
207 207 ui.interactive=False
208 208 ui.mergemarkers=detailed
209 209 ui.usehttp2=true (?)
210 210 ui.nontty=true
211 211
212 212 $ rm -R foo
213 213
214 214 #if windows
215 215 $ PYTHONPATH="$TESTTMP/repo;$PYTHONPATH"
216 216 #else
217 217 $ PYTHONPATH="$TESTTMP/repo:$PYTHONPATH"
218 218 #endif
219 219
220 220 $ cat <<EOF > hook.py
221 221 > from __future__ import print_function
222 222 > import sys
223 223 > def hook(**args):
224 224 > print('hook talking')
225 225 > print('now try to read something: %r' % sys.stdin.read())
226 226 > EOF
227 227
228 228 >>> from hgclient import check, readchannel, runcommand, stringio
229 229 >>> @check
230 230 ... def hookoutput(server):
231 231 ... readchannel(server)
232 232 ... runcommand(server, ['--config',
233 233 ... 'hooks.pre-identify=python:hook.hook',
234 234 ... 'id'],
235 235 ... input=stringio('some input'))
236 236 *** runcommand --config hooks.pre-identify=python:hook.hook id
237 237 eff892de26ec tip
238 238
239 239 Clean hook cached version
240 240 $ rm hook.py*
241 241 $ rm -Rf __pycache__
242 242
243 243 $ echo a >> a
244 244 >>> import os
245 245 >>> from hgclient import check, readchannel, runcommand
246 246 >>> @check
247 247 ... def outsidechanges(server):
248 248 ... readchannel(server)
249 249 ... runcommand(server, ['status'])
250 250 ... os.system('hg ci -Am2')
251 251 ... runcommand(server, ['tip'])
252 252 ... runcommand(server, ['status'])
253 253 *** runcommand status
254 254 M a
255 255 *** runcommand tip
256 256 changeset: 1:d3a0a68be6de
257 257 tag: tip
258 258 user: test
259 259 date: Thu Jan 01 00:00:00 1970 +0000
260 260 summary: 2
261 261
262 262 *** runcommand status
263 263
264 264 >>> import os
265 265 >>> from hgclient import check, readchannel, runcommand
266 266 >>> @check
267 267 ... def bookmarks(server):
268 268 ... readchannel(server)
269 269 ... runcommand(server, ['bookmarks'])
270 270 ...
271 271 ... # changes .hg/bookmarks
272 272 ... os.system('hg bookmark -i bm1')
273 273 ... os.system('hg bookmark -i bm2')
274 274 ... runcommand(server, ['bookmarks'])
275 275 ...
276 276 ... # changes .hg/bookmarks.current
277 277 ... os.system('hg upd bm1 -q')
278 278 ... runcommand(server, ['bookmarks'])
279 279 ...
280 280 ... runcommand(server, ['bookmarks', 'bm3'])
281 281 ... f = open('a', 'ab')
282 282 ... f.write('a\n')
283 283 ... f.close()
284 284 ... runcommand(server, ['commit', '-Amm'])
285 285 ... runcommand(server, ['bookmarks'])
286 286 ... print('')
287 287 *** runcommand bookmarks
288 288 no bookmarks set
289 289 *** runcommand bookmarks
290 290 bm1 1:d3a0a68be6de
291 291 bm2 1:d3a0a68be6de
292 292 *** runcommand bookmarks
293 293 * bm1 1:d3a0a68be6de
294 294 bm2 1:d3a0a68be6de
295 295 *** runcommand bookmarks bm3
296 296 *** runcommand commit -Amm
297 297 *** runcommand bookmarks
298 298 bm1 1:d3a0a68be6de
299 299 bm2 1:d3a0a68be6de
300 300 * bm3 2:aef17e88f5f0
301 301
302 302
303 303 >>> import os
304 304 >>> from hgclient import check, readchannel, runcommand
305 305 >>> @check
306 306 ... def tagscache(server):
307 307 ... readchannel(server)
308 308 ... runcommand(server, ['id', '-t', '-r', '0'])
309 309 ... os.system('hg tag -r 0 foo')
310 310 ... runcommand(server, ['id', '-t', '-r', '0'])
311 311 *** runcommand id -t -r 0
312 312
313 313 *** runcommand id -t -r 0
314 314 foo
315 315
316 316 >>> import os
317 317 >>> from hgclient import check, readchannel, runcommand
318 318 >>> @check
319 319 ... def setphase(server):
320 320 ... readchannel(server)
321 321 ... runcommand(server, ['phase', '-r', '.'])
322 322 ... os.system('hg phase -r . -p')
323 323 ... runcommand(server, ['phase', '-r', '.'])
324 324 *** runcommand phase -r .
325 325 3: draft
326 326 *** runcommand phase -r .
327 327 3: public
328 328
329 329 $ echo a >> a
330 330 >>> from hgclient import check, readchannel, runcommand
331 331 >>> @check
332 332 ... def rollback(server):
333 333 ... readchannel(server)
334 334 ... runcommand(server, ['phase', '-r', '.', '-p'])
335 335 ... runcommand(server, ['commit', '-Am.'])
336 336 ... runcommand(server, ['rollback'])
337 337 ... runcommand(server, ['phase', '-r', '.'])
338 338 ... print('')
339 339 *** runcommand phase -r . -p
340 340 no phases changed
341 341 *** runcommand commit -Am.
342 342 *** runcommand rollback
343 343 repository tip rolled back to revision 3 (undo commit)
344 344 working directory now based on revision 3
345 345 *** runcommand phase -r .
346 346 3: public
347 347
348 348
349 349 >>> import os
350 350 >>> from hgclient import check, readchannel, runcommand
351 351 >>> @check
352 352 ... def branch(server):
353 353 ... readchannel(server)
354 354 ... runcommand(server, ['branch'])
355 355 ... os.system('hg branch foo')
356 356 ... runcommand(server, ['branch'])
357 357 ... os.system('hg branch default')
358 358 *** runcommand branch
359 359 default
360 360 marked working directory as branch foo
361 361 (branches are permanent and global, did you want a bookmark?)
362 362 *** runcommand branch
363 363 foo
364 364 marked working directory as branch default
365 365 (branches are permanent and global, did you want a bookmark?)
366 366
367 367 $ touch .hgignore
368 368 >>> import os
369 369 >>> from hgclient import check, readchannel, runcommand
370 370 >>> @check
371 371 ... def hgignore(server):
372 372 ... readchannel(server)
373 373 ... runcommand(server, ['commit', '-Am.'])
374 374 ... f = open('ignored-file', 'ab')
375 375 ... f.write('')
376 376 ... f.close()
377 377 ... f = open('.hgignore', 'ab')
378 378 ... f.write('ignored-file')
379 379 ... f.close()
380 380 ... runcommand(server, ['status', '-i', '-u'])
381 381 ... print('')
382 382 *** runcommand commit -Am.
383 383 adding .hgignore
384 384 *** runcommand status -i -u
385 385 I ignored-file
386 386
387 387
388 388 cache of non-public revisions should be invalidated on repository change
389 389 (issue4855):
390 390
391 391 >>> import os
392 392 >>> from hgclient import check, readchannel, runcommand
393 393 >>> @check
394 394 ... def phasesetscacheaftercommit(server):
395 395 ... readchannel(server)
396 396 ... # load _phasecache._phaserevs and _phasesets
397 397 ... runcommand(server, ['log', '-qr', 'draft()'])
398 398 ... # create draft commits by another process
399 399 ... for i in xrange(5, 7):
400 400 ... f = open('a', 'ab')
401 401 ... f.seek(0, os.SEEK_END)
402 402 ... f.write('a\n')
403 403 ... f.close()
404 404 ... os.system('hg commit -Aqm%d' % i)
405 405 ... # new commits should be listed as draft revisions
406 406 ... runcommand(server, ['log', '-qr', 'draft()'])
407 407 ... print('')
408 408 *** runcommand log -qr draft()
409 409 4:7966c8e3734d
410 410 *** runcommand log -qr draft()
411 411 4:7966c8e3734d
412 412 5:41f6602d1c4f
413 413 6:10501e202c35
414 414
415 415
416 416 >>> import os
417 417 >>> from hgclient import check, readchannel, runcommand
418 418 >>> @check
419 419 ... def phasesetscacheafterstrip(server):
420 420 ... readchannel(server)
421 421 ... # load _phasecache._phaserevs and _phasesets
422 422 ... runcommand(server, ['log', '-qr', 'draft()'])
423 423 ... # strip cached revisions by another process
424 424 ... os.system('hg --config extensions.strip= strip -q 5')
425 425 ... # shouldn't abort by "unknown revision '6'"
426 426 ... runcommand(server, ['log', '-qr', 'draft()'])
427 427 ... print('')
428 428 *** runcommand log -qr draft()
429 429 4:7966c8e3734d
430 430 5:41f6602d1c4f
431 431 6:10501e202c35
432 432 *** runcommand log -qr draft()
433 433 4:7966c8e3734d
434 434
435 435
436 436 cache of phase roots should be invalidated on strip (issue3827):
437 437
438 438 >>> import os
439 439 >>> from hgclient import check, readchannel, runcommand, sep
440 440 >>> @check
441 441 ... def phasecacheafterstrip(server):
442 442 ... readchannel(server)
443 443 ...
444 444 ... # create new head, 5:731265503d86
445 445 ... runcommand(server, ['update', '-C', '0'])
446 446 ... f = open('a', 'ab')
447 447 ... f.write('a\n')
448 448 ... f.close()
449 449 ... runcommand(server, ['commit', '-Am.', 'a'])
450 450 ... runcommand(server, ['log', '-Gq'])
451 451 ...
452 452 ... # make it public; draft marker moves to 4:7966c8e3734d
453 453 ... runcommand(server, ['phase', '-p', '.'])
454 454 ... # load _phasecache.phaseroots
455 455 ... runcommand(server, ['phase', '.'], outfilter=sep)
456 456 ...
457 457 ... # strip 1::4 outside server
458 458 ... os.system('hg -q --config extensions.mq= strip 1')
459 459 ...
460 460 ... # shouldn't raise "7966c8e3734d: no node!"
461 461 ... runcommand(server, ['branches'])
462 462 *** runcommand update -C 0
463 463 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
464 464 (leaving bookmark bm3)
465 465 *** runcommand commit -Am. a
466 466 created new head
467 467 *** runcommand log -Gq
468 468 @ 5:731265503d86
469 469 |
470 470 | o 4:7966c8e3734d
471 471 | |
472 472 | o 3:b9b85890c400
473 473 | |
474 474 | o 2:aef17e88f5f0
475 475 | |
476 476 | o 1:d3a0a68be6de
477 477 |/
478 478 o 0:eff892de26ec
479 479
480 480 *** runcommand phase -p .
481 481 *** runcommand phase .
482 482 5: public
483 483 *** runcommand branches
484 484 default 1:731265503d86
485 485
486 486 in-memory cache must be reloaded if transaction is aborted. otherwise
487 487 changelog and manifest would have invalid node:
488 488
489 489 $ echo a >> a
490 490 >>> from hgclient import check, readchannel, runcommand
491 491 >>> @check
492 492 ... def txabort(server):
493 493 ... readchannel(server)
494 494 ... runcommand(server, ['commit', '--config', 'hooks.pretxncommit=false',
495 495 ... '-mfoo'])
496 496 ... runcommand(server, ['verify'])
497 497 *** runcommand commit --config hooks.pretxncommit=false -mfoo
498 498 transaction abort!
499 499 rollback completed
500 500 abort: pretxncommit hook exited with status 1
501 501 [255]
502 502 *** runcommand verify
503 503 checking changesets
504 504 checking manifests
505 505 crosschecking files in changesets and manifests
506 506 checking files
507 507 1 files, 2 changesets, 2 total revisions
508 508 $ hg revert --no-backup -aq
509 509
510 510 $ cat >> .hg/hgrc << EOF
511 511 > [experimental]
512 512 > evolution.createmarkers=True
513 513 > EOF
514 514
515 515 >>> import os
516 516 >>> from hgclient import check, readchannel, runcommand
517 517 >>> @check
518 518 ... def obsolete(server):
519 519 ... readchannel(server)
520 520 ...
521 521 ... runcommand(server, ['up', 'null'])
522 522 ... runcommand(server, ['phase', '-df', 'tip'])
523 523 ... cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
524 524 ... if os.name == 'nt':
525 525 ... cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
526 526 ... os.system(cmd)
527 527 ... runcommand(server, ['log', '--hidden'])
528 528 ... runcommand(server, ['log'])
529 529 *** runcommand up null
530 530 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
531 531 *** runcommand phase -df tip
532 532 obsoleted 1 changesets
533 533 *** runcommand log --hidden
534 534 changeset: 1:731265503d86
535 535 tag: tip
536 536 user: test
537 537 date: Thu Jan 01 00:00:00 1970 +0000
538 obsfate: pruned
538 obsolete: pruned
539 539 summary: .
540 540
541 541 changeset: 0:eff892de26ec
542 542 bookmark: bm1
543 543 bookmark: bm2
544 544 bookmark: bm3
545 545 user: test
546 546 date: Thu Jan 01 00:00:00 1970 +0000
547 547 summary: 1
548 548
549 549 *** runcommand log
550 550 changeset: 0:eff892de26ec
551 551 bookmark: bm1
552 552 bookmark: bm2
553 553 bookmark: bm3
554 554 tag: tip
555 555 user: test
556 556 date: Thu Jan 01 00:00:00 1970 +0000
557 557 summary: 1
558 558
559 559
560 560 $ cat <<EOF >> .hg/hgrc
561 561 > [extensions]
562 562 > mq =
563 563 > EOF
564 564
565 565 >>> import os
566 566 >>> from hgclient import check, readchannel, runcommand
567 567 >>> @check
568 568 ... def mqoutsidechanges(server):
569 569 ... readchannel(server)
570 570 ...
571 571 ... # load repo.mq
572 572 ... runcommand(server, ['qapplied'])
573 573 ... os.system('hg qnew 0.diff')
574 574 ... # repo.mq should be invalidated
575 575 ... runcommand(server, ['qapplied'])
576 576 ...
577 577 ... runcommand(server, ['qpop', '--all'])
578 578 ... os.system('hg qqueue --create foo')
579 579 ... # repo.mq should be recreated to point to new queue
580 580 ... runcommand(server, ['qqueue', '--active'])
581 581 *** runcommand qapplied
582 582 *** runcommand qapplied
583 583 0.diff
584 584 *** runcommand qpop --all
585 585 popping 0.diff
586 586 patch queue now empty
587 587 *** runcommand qqueue --active
588 588 foo
589 589
590 590 $ cat <<EOF > dbgui.py
591 591 > import os
592 592 > import sys
593 593 > from mercurial import commands, registrar
594 594 > cmdtable = {}
595 595 > command = registrar.command(cmdtable)
596 596 > @command(b"debuggetpass", norepo=True)
597 597 > def debuggetpass(ui):
598 598 > ui.write("%s\\n" % ui.getpass())
599 599 > @command(b"debugprompt", norepo=True)
600 600 > def debugprompt(ui):
601 601 > ui.write("%s\\n" % ui.prompt("prompt:"))
602 602 > @command(b"debugreadstdin", norepo=True)
603 603 > def debugreadstdin(ui):
604 604 > ui.write("read: %r\n" % sys.stdin.read(1))
605 605 > @command(b"debugwritestdout", norepo=True)
606 606 > def debugwritestdout(ui):
607 607 > os.write(1, "low-level stdout fd and\n")
608 608 > sys.stdout.write("stdout should be redirected to /dev/null\n")
609 609 > sys.stdout.flush()
610 610 > EOF
611 611 $ cat <<EOF >> .hg/hgrc
612 612 > [extensions]
613 613 > dbgui = dbgui.py
614 614 > EOF
615 615
616 616 >>> from hgclient import check, readchannel, runcommand, stringio
617 617 >>> @check
618 618 ... def getpass(server):
619 619 ... readchannel(server)
620 620 ... runcommand(server, ['debuggetpass', '--config',
621 621 ... 'ui.interactive=True'],
622 622 ... input=stringio('1234\n'))
623 623 ... runcommand(server, ['debuggetpass', '--config',
624 624 ... 'ui.interactive=True'],
625 625 ... input=stringio('\n'))
626 626 ... runcommand(server, ['debuggetpass', '--config',
627 627 ... 'ui.interactive=True'],
628 628 ... input=stringio(''))
629 629 ... runcommand(server, ['debugprompt', '--config',
630 630 ... 'ui.interactive=True'],
631 631 ... input=stringio('5678\n'))
632 632 ... runcommand(server, ['debugreadstdin'])
633 633 ... runcommand(server, ['debugwritestdout'])
634 634 *** runcommand debuggetpass --config ui.interactive=True
635 635 password: 1234
636 636 *** runcommand debuggetpass --config ui.interactive=True
637 637 password:
638 638 *** runcommand debuggetpass --config ui.interactive=True
639 639 password: abort: response expected
640 640 [255]
641 641 *** runcommand debugprompt --config ui.interactive=True
642 642 prompt: 5678
643 643 *** runcommand debugreadstdin
644 644 read: ''
645 645 *** runcommand debugwritestdout
646 646
647 647
648 648 run commandserver in commandserver, which is silly but should work:
649 649
650 650 >>> from __future__ import print_function
651 651 >>> from hgclient import check, readchannel, runcommand, stringio
652 652 >>> @check
653 653 ... def nested(server):
654 654 ... print('%c, %r' % readchannel(server))
655 655 ... class nestedserver(object):
656 656 ... stdin = stringio('getencoding\n')
657 657 ... stdout = stringio()
658 658 ... runcommand(server, ['serve', '--cmdserver', 'pipe'],
659 659 ... output=nestedserver.stdout, input=nestedserver.stdin)
660 660 ... nestedserver.stdout.seek(0)
661 661 ... print('%c, %r' % readchannel(nestedserver)) # hello
662 662 ... print('%c, %r' % readchannel(nestedserver)) # getencoding
663 663 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
664 664 *** runcommand serve --cmdserver pipe
665 665 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
666 666 r, '*' (glob)
667 667
668 668
669 669 start without repository:
670 670
671 671 $ cd ..
672 672
673 673 >>> from __future__ import print_function
674 674 >>> from hgclient import check, readchannel, runcommand
675 675 >>> @check
676 676 ... def hellomessage(server):
677 677 ... ch, data = readchannel(server)
678 678 ... print('%c, %r' % (ch, data))
679 679 ... # run an arbitrary command to make sure the next thing the server
680 680 ... # sends isn't part of the hello message
681 681 ... runcommand(server, ['id'])
682 682 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
683 683 *** runcommand id
684 684 abort: there is no Mercurial repository here (.hg not found)
685 685 [255]
686 686
687 687 >>> from hgclient import check, readchannel, runcommand
688 688 >>> @check
689 689 ... def startwithoutrepo(server):
690 690 ... readchannel(server)
691 691 ... runcommand(server, ['init', 'repo2'])
692 692 ... runcommand(server, ['id', '-R', 'repo2'])
693 693 *** runcommand init repo2
694 694 *** runcommand id -R repo2
695 695 000000000000 tip
696 696
697 697
698 698 don't fall back to cwd if invalid -R path is specified (issue4805):
699 699
700 700 $ cd repo
701 701 $ hg serve --cmdserver pipe -R ../nonexistent
702 702 abort: repository ../nonexistent not found!
703 703 [255]
704 704 $ cd ..
705 705
706 706
707 707 unix domain socket:
708 708
709 709 $ cd repo
710 710 $ hg update -q
711 711
712 712 #if unix-socket unix-permissions
713 713
714 714 >>> from __future__ import print_function
715 715 >>> from hgclient import check, readchannel, runcommand, stringio, unixserver
716 716 >>> server = unixserver('.hg/server.sock', '.hg/server.log')
717 717 >>> def hellomessage(conn):
718 718 ... ch, data = readchannel(conn)
719 719 ... print('%c, %r' % (ch, data))
720 720 ... runcommand(conn, ['id'])
721 721 >>> check(hellomessage, server.connect)
722 722 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
723 723 *** runcommand id
724 724 eff892de26ec tip bm1/bm2/bm3
725 725 >>> def unknowncommand(conn):
726 726 ... readchannel(conn)
727 727 ... conn.stdin.write('unknowncommand\n')
728 728 >>> check(unknowncommand, server.connect) # error sent to server.log
729 729 >>> def serverinput(conn):
730 730 ... readchannel(conn)
731 731 ... patch = """
732 732 ... # HG changeset patch
733 733 ... # User test
734 734 ... # Date 0 0
735 735 ... 2
736 736 ...
737 737 ... diff -r eff892de26ec -r 1ed24be7e7a0 a
738 738 ... --- a/a
739 739 ... +++ b/a
740 740 ... @@ -1,1 +1,2 @@
741 741 ... 1
742 742 ... +2
743 743 ... """
744 744 ... runcommand(conn, ['import', '-'], input=stringio(patch))
745 745 ... runcommand(conn, ['log', '-rtip', '-q'])
746 746 >>> check(serverinput, server.connect)
747 747 *** runcommand import -
748 748 applying patch from stdin
749 749 *** runcommand log -rtip -q
750 750 2:1ed24be7e7a0
751 751 >>> server.shutdown()
752 752
753 753 $ cat .hg/server.log
754 754 listening at .hg/server.sock
755 755 abort: unknown command unknowncommand
756 756 killed!
757 757 $ rm .hg/server.log
758 758
759 759 if server crashed before hello, traceback will be sent to 'e' channel as
760 760 last ditch:
761 761
762 762 $ cat <<EOF >> .hg/hgrc
763 763 > [cmdserver]
764 764 > log = inexistent/path.log
765 765 > EOF
766 766 >>> from __future__ import print_function
767 767 >>> from hgclient import check, readchannel, unixserver
768 768 >>> server = unixserver('.hg/server.sock', '.hg/server.log')
769 769 >>> def earlycrash(conn):
770 770 ... while True:
771 771 ... try:
772 772 ... ch, data = readchannel(conn)
773 773 ... if not data.startswith(' '):
774 774 ... print('%c, %r' % (ch, data))
775 775 ... except EOFError:
776 776 ... break
777 777 >>> check(earlycrash, server.connect)
778 778 e, 'Traceback (most recent call last):\n'
779 779 e, "IOError: *" (glob)
780 780 >>> server.shutdown()
781 781
782 782 $ cat .hg/server.log | grep -v '^ '
783 783 listening at .hg/server.sock
784 784 Traceback (most recent call last):
785 785 IOError: * (glob)
786 786 killed!
787 787 #endif
788 788 #if no-unix-socket
789 789
790 790 $ hg serve --cmdserver unix -a .hg/server.sock
791 791 abort: unsupported platform
792 792 [255]
793 793
794 794 #endif
795 795
796 796 $ cd ..
797 797
798 798 Test that accessing to invalid changelog cache is avoided at
799 799 subsequent operations even if repo object is reused even after failure
800 800 of transaction (see 0a7610758c42 also)
801 801
802 802 "hg log" after failure of transaction is needed to detect invalid
803 803 cache in repoview: this can't detect by "hg verify" only.
804 804
805 805 Combination of "finalization" and "empty-ness of changelog" (2 x 2 =
806 806 4) are tested, because '00changelog.i' are differently changed in each
807 807 cases.
808 808
809 809 $ cat > $TESTTMP/failafterfinalize.py <<EOF
810 810 > # extension to abort transaction after finalization forcibly
811 811 > from mercurial import commands, error, extensions, lock as lockmod
812 812 > from mercurial import registrar
813 813 > cmdtable = {}
814 814 > command = registrar.command(cmdtable)
815 815 > configtable = {}
816 816 > configitem = registrar.configitem(configtable)
817 817 > configitem('failafterfinalize', 'fail',
818 818 > default=None,
819 819 > )
820 820 > def fail(tr):
821 821 > raise error.Abort('fail after finalization')
822 822 > def reposetup(ui, repo):
823 823 > class failrepo(repo.__class__):
824 824 > def commitctx(self, ctx, error=False):
825 825 > if self.ui.configbool('failafterfinalize', 'fail'):
826 826 > # 'sorted()' by ASCII code on category names causes
827 827 > # invoking 'fail' after finalization of changelog
828 828 > # using "'cl-%i' % id(self)" as category name
829 829 > self.currenttransaction().addfinalize('zzzzzzzz', fail)
830 830 > return super(failrepo, self).commitctx(ctx, error)
831 831 > repo.__class__ = failrepo
832 832 > EOF
833 833
834 834 $ hg init repo3
835 835 $ cd repo3
836 836
837 837 $ cat <<EOF >> $HGRCPATH
838 838 > [ui]
839 839 > logtemplate = {rev} {desc|firstline} ({files})\n
840 840 >
841 841 > [extensions]
842 842 > failafterfinalize = $TESTTMP/failafterfinalize.py
843 843 > EOF
844 844
845 845 - test failure with "empty changelog"
846 846
847 847 $ echo foo > foo
848 848 $ hg add foo
849 849
850 850 (failure before finalization)
851 851
852 852 >>> from hgclient import check, readchannel, runcommand
853 853 >>> @check
854 854 ... def abort(server):
855 855 ... readchannel(server)
856 856 ... runcommand(server, ['commit',
857 857 ... '--config', 'hooks.pretxncommit=false',
858 858 ... '-mfoo'])
859 859 ... runcommand(server, ['log'])
860 860 ... runcommand(server, ['verify', '-q'])
861 861 *** runcommand commit --config hooks.pretxncommit=false -mfoo
862 862 transaction abort!
863 863 rollback completed
864 864 abort: pretxncommit hook exited with status 1
865 865 [255]
866 866 *** runcommand log
867 867 *** runcommand verify -q
868 868
869 869 (failure after finalization)
870 870
871 871 >>> from hgclient import check, readchannel, runcommand
872 872 >>> @check
873 873 ... def abort(server):
874 874 ... readchannel(server)
875 875 ... runcommand(server, ['commit',
876 876 ... '--config', 'failafterfinalize.fail=true',
877 877 ... '-mfoo'])
878 878 ... runcommand(server, ['log'])
879 879 ... runcommand(server, ['verify', '-q'])
880 880 *** runcommand commit --config failafterfinalize.fail=true -mfoo
881 881 transaction abort!
882 882 rollback completed
883 883 abort: fail after finalization
884 884 [255]
885 885 *** runcommand log
886 886 *** runcommand verify -q
887 887
888 888 - test failure with "not-empty changelog"
889 889
890 890 $ echo bar > bar
891 891 $ hg add bar
892 892 $ hg commit -mbar bar
893 893
894 894 (failure before finalization)
895 895
896 896 >>> from hgclient import check, readchannel, runcommand
897 897 >>> @check
898 898 ... def abort(server):
899 899 ... readchannel(server)
900 900 ... runcommand(server, ['commit',
901 901 ... '--config', 'hooks.pretxncommit=false',
902 902 ... '-mfoo', 'foo'])
903 903 ... runcommand(server, ['log'])
904 904 ... runcommand(server, ['verify', '-q'])
905 905 *** runcommand commit --config hooks.pretxncommit=false -mfoo foo
906 906 transaction abort!
907 907 rollback completed
908 908 abort: pretxncommit hook exited with status 1
909 909 [255]
910 910 *** runcommand log
911 911 0 bar (bar)
912 912 *** runcommand verify -q
913 913
914 914 (failure after finalization)
915 915
916 916 >>> from hgclient import check, readchannel, runcommand
917 917 >>> @check
918 918 ... def abort(server):
919 919 ... readchannel(server)
920 920 ... runcommand(server, ['commit',
921 921 ... '--config', 'failafterfinalize.fail=true',
922 922 ... '-mfoo', 'foo'])
923 923 ... runcommand(server, ['log'])
924 924 ... runcommand(server, ['verify', '-q'])
925 925 *** runcommand commit --config failafterfinalize.fail=true -mfoo foo
926 926 transaction abort!
927 927 rollback completed
928 928 abort: fail after finalization
929 929 [255]
930 930 *** runcommand log
931 931 0 bar (bar)
932 932 *** runcommand verify -q
933 933
934 934 $ cd ..
935 935
936 936 Test symlink traversal over cached audited paths:
937 937 -------------------------------------------------
938 938
939 939 #if symlink
940 940
941 941 set up symlink hell
942 942
943 943 $ mkdir merge-symlink-out
944 944 $ hg init merge-symlink
945 945 $ cd merge-symlink
946 946 $ touch base
947 947 $ hg commit -qAm base
948 948 $ ln -s ../merge-symlink-out a
949 949 $ hg commit -qAm 'symlink a -> ../merge-symlink-out'
950 950 $ hg up -q 0
951 951 $ mkdir a
952 952 $ touch a/poisoned
953 953 $ hg commit -qAm 'file a/poisoned'
954 954 $ hg log -G -T '{rev}: {desc}\n'
955 955 @ 2: file a/poisoned
956 956 |
957 957 | o 1: symlink a -> ../merge-symlink-out
958 958 |/
959 959 o 0: base
960 960
961 961
962 962 try trivial merge after update: cache of audited paths should be discarded,
963 963 and the merge should fail (issue5628)
964 964
965 965 $ hg up -q null
966 966 >>> from hgclient import check, readchannel, runcommand
967 967 >>> @check
968 968 ... def merge(server):
969 969 ... readchannel(server)
970 970 ... # audit a/poisoned as a good path
971 971 ... runcommand(server, ['up', '-qC', '2'])
972 972 ... runcommand(server, ['up', '-qC', '1'])
973 973 ... # here a is a symlink, so a/poisoned is bad
974 974 ... runcommand(server, ['merge', '2'])
975 975 *** runcommand up -qC 2
976 976 *** runcommand up -qC 1
977 977 *** runcommand merge 2
978 978 a: path conflict - a file or link has the same name as a directory
979 979 the local file has been renamed to a~aa04623eb0c3
980 980 resolve manually then use 'hg resolve --mark a'
981 981 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
982 982 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
983 983 [1]
984 984 $ ls ../merge-symlink-out
985 985
986 986 cache of repo.auditor should be discarded, so matcher would never traverse
987 987 symlinks:
988 988
989 989 $ hg up -qC 0
990 990 $ touch ../merge-symlink-out/poisoned
991 991 >>> from hgclient import check, readchannel, runcommand
992 992 >>> @check
993 993 ... def files(server):
994 994 ... readchannel(server)
995 995 ... runcommand(server, ['up', '-qC', '2'])
996 996 ... # audit a/poisoned as a good path
997 997 ... runcommand(server, ['files', 'a/poisoned'])
998 998 ... runcommand(server, ['up', '-qC', '0'])
999 999 ... runcommand(server, ['up', '-qC', '1'])
1000 1000 ... # here 'a' is a symlink, so a/poisoned should be warned
1001 1001 ... runcommand(server, ['files', 'a/poisoned'])
1002 1002 *** runcommand up -qC 2
1003 1003 *** runcommand files a/poisoned
1004 1004 a/poisoned
1005 1005 *** runcommand up -qC 0
1006 1006 *** runcommand up -qC 1
1007 1007 *** runcommand files a/poisoned
1008 1008 abort: path 'a/poisoned' traverses symbolic link 'a'
1009 1009 [255]
1010 1010
1011 1011 $ cd ..
1012 1012
1013 1013 #endif
@@ -1,2545 +1,2545 b''
1 1 This test file test the various templates related to obsmarkers.
2 2
3 3 Global setup
4 4 ============
5 5
6 6 $ . $TESTDIR/testlib/obsmarker-common.sh
7 7 $ cat >> $HGRCPATH <<EOF
8 8 > [ui]
9 9 > interactive = true
10 10 > [phases]
11 11 > publish=False
12 12 > [experimental]
13 13 > evolution=true
14 14 > [templates]
15 15 > obsfatesuccessors = "{if(successors, " as ")}{join(successors, ", ")}"
16 16 > obsfateverb = "{obsfateverb(successors)}"
17 17 > obsfateoperations = "{if(obsfateoperations(markers), " using {join(obsfateoperations(markers), ", ")}")}"
18 18 > obsfateusers = "{if(obsfateusers(markers), " by {join(obsfateusers(markers), ", ")}")}"
19 19 > obsfatedate = "{if(obsfatedate(markers), "{ifeq(min(obsfatedate(markers)), max(obsfatedate(markers)), " (at {min(obsfatedate(markers))|isodate})", " (between {min(obsfatedate(markers))|isodate} and {max(obsfatedate(markers))|isodate})")}")}"
20 20 > obsfatetempl = "{obsfateverb}{obsfateoperations}{obsfatesuccessors}{obsfateusers}{obsfatedate}; "
21 21 > [alias]
22 22 > tlog = log -G -T '{node|short}\
23 23 > {if(predecessors, "\n Predecessors: {predecessors}")}\
24 24 > {if(predecessors, "\n semi-colon: {join(predecessors, "; ")}")}\
25 25 > {if(predecessors, "\n json: {predecessors|json}")}\
26 26 > {if(predecessors, "\n map: {join(predecessors % "{rev}:{node}", " ")}")}\
27 27 > {if(successorssets, "\n Successors: {successorssets}")}\
28 28 > {if(successorssets, "\n multi-line: {join(successorssets, "\n multi-line: ")}")}\
29 29 > {if(successorssets, "\n json: {successorssets|json}")}\n'
30 30 > fatelog = log -G -T '{node|short}\n{if(succsandmarkers, " Obsfate: {succsandmarkers % "{obsfatetempl}"} \n" )}'
31 31 > fatelogjson = log -G -T '{node|short}\n{if(succsandmarkers, " Obsfate: {succsandmarkers|json}\n")}'
32 32 > fatelogkw = log -G -T '{node|short}\n{if(obsfate, "{obsfate % " Obsfate: {fate}\n"}")}'
33 33 > EOF
34 34
35 35 Test templates on amended commit
36 36 ================================
37 37
38 38 Test setup
39 39 ----------
40 40
41 41 $ hg init $TESTTMP/templates-local-amend
42 42 $ cd $TESTTMP/templates-local-amend
43 43 $ mkcommit ROOT
44 44 $ mkcommit A0
45 45 $ echo 42 >> A0
46 46 $ hg commit --amend -m "A1" --config devel.default-date="1234567890 0"
47 47 $ hg commit --amend -m "A2" --config devel.default-date="987654321 0" --config devel.user.obsmarker=test2
48 48
49 49 $ hg log --hidden -G
50 50 @ changeset: 3:d004c8f274b9
51 51 | tag: tip
52 52 | parent: 0:ea207398892e
53 53 | user: test
54 54 | date: Thu Jan 01 00:00:00 1970 +0000
55 55 | summary: A2
56 56 |
57 57 | x changeset: 2:a468dc9b3633
58 58 |/ parent: 0:ea207398892e
59 59 | user: test
60 60 | date: Thu Jan 01 00:00:00 1970 +0000
61 | obsfate: rewritten using amend as 3:d004c8f274b9 by test2
61 | obsolete: rewritten using amend as 3:d004c8f274b9 by test2
62 62 | summary: A1
63 63 |
64 64 | x changeset: 1:471f378eab4c
65 65 |/ user: test
66 66 | date: Thu Jan 01 00:00:00 1970 +0000
67 | obsfate: rewritten using amend as 2:a468dc9b3633
67 | obsolete: rewritten using amend as 2:a468dc9b3633
68 68 | summary: A0
69 69 |
70 70 o changeset: 0:ea207398892e
71 71 user: test
72 72 date: Thu Jan 01 00:00:00 1970 +0000
73 73 summary: ROOT
74 74
75 75 Check templates
76 76 ---------------
77 77 $ hg up 'desc(A0)' --hidden
78 78 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 79
80 80 Predecessors template should show current revision as it is the working copy
81 81 $ hg tlog
82 82 o d004c8f274b9
83 83 | Predecessors: 1:471f378eab4c
84 84 | semi-colon: 1:471f378eab4c
85 85 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
86 86 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
87 87 | @ 471f378eab4c
88 88 |/ Successors: 3:d004c8f274b9
89 89 | multi-line: 3:d004c8f274b9
90 90 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
91 91 o ea207398892e
92 92
93 93 $ hg fatelog
94 94 o d004c8f274b9
95 95 |
96 96 | @ 471f378eab4c
97 97 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test, test2 (between 2001-04-19 04:25 +0000 and 2009-02-13 23:31 +0000);
98 98 o ea207398892e
99 99
100 100
101 101 $ hg fatelogkw
102 102 o d004c8f274b9
103 103 |
104 104 | @ 471f378eab4c
105 105 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test, test2
106 106 o ea207398892e
107 107
108 108
109 109 $ hg log -G --config ui.logtemplate=
110 110 o changeset: 3:d004c8f274b9
111 111 | tag: tip
112 112 | parent: 0:ea207398892e
113 113 | user: test
114 114 | date: Thu Jan 01 00:00:00 1970 +0000
115 115 | summary: A2
116 116 |
117 117 | @ changeset: 1:471f378eab4c
118 118 |/ user: test
119 119 | date: Thu Jan 01 00:00:00 1970 +0000
120 | obsfate: rewritten using amend as 3:d004c8f274b9 by test, test2
120 | obsolete: rewritten using amend as 3:d004c8f274b9 by test, test2
121 121 | summary: A0
122 122 |
123 123 o changeset: 0:ea207398892e
124 124 user: test
125 125 date: Thu Jan 01 00:00:00 1970 +0000
126 126 summary: ROOT
127 127
128 128
129 129 $ hg log -G -T "default"
130 130 o changeset: 3:d004c8f274b9
131 131 | tag: tip
132 132 | parent: 0:ea207398892e
133 133 | user: test
134 134 | date: Thu Jan 01 00:00:00 1970 +0000
135 135 | summary: A2
136 136 |
137 137 | @ changeset: 1:471f378eab4c
138 138 |/ user: test
139 139 | date: Thu Jan 01 00:00:00 1970 +0000
140 140 | obsfate: rewritten using amend as 3:d004c8f274b9 by test, test2
141 141 | summary: A0
142 142 |
143 143 o changeset: 0:ea207398892e
144 144 user: test
145 145 date: Thu Jan 01 00:00:00 1970 +0000
146 146 summary: ROOT
147 147
148 148 $ hg up 'desc(A1)' --hidden
149 149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 150
151 151 Predecessors template should show current revision as it is the working copy
152 152 $ hg tlog
153 153 o d004c8f274b9
154 154 | Predecessors: 2:a468dc9b3633
155 155 | semi-colon: 2:a468dc9b3633
156 156 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
157 157 | map: 2:a468dc9b36338b14fdb7825f55ce3df4e71517ad
158 158 | @ a468dc9b3633
159 159 |/ Successors: 3:d004c8f274b9
160 160 | multi-line: 3:d004c8f274b9
161 161 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
162 162 o ea207398892e
163 163
164 164 $ hg fatelog
165 165 o d004c8f274b9
166 166 |
167 167 | @ a468dc9b3633
168 168 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
169 169 o ea207398892e
170 170
171 171 Predecessors template should show all the predecessors as we force their display
172 172 with --hidden
173 173 $ hg tlog --hidden
174 174 o d004c8f274b9
175 175 | Predecessors: 2:a468dc9b3633
176 176 | semi-colon: 2:a468dc9b3633
177 177 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
178 178 | map: 2:a468dc9b36338b14fdb7825f55ce3df4e71517ad
179 179 | @ a468dc9b3633
180 180 |/ Predecessors: 1:471f378eab4c
181 181 | semi-colon: 1:471f378eab4c
182 182 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
183 183 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
184 184 | Successors: 3:d004c8f274b9
185 185 | multi-line: 3:d004c8f274b9
186 186 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
187 187 | x 471f378eab4c
188 188 |/ Successors: 2:a468dc9b3633
189 189 | multi-line: 2:a468dc9b3633
190 190 | json: [["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]]
191 191 o ea207398892e
192 192
193 193 $ hg fatelog --hidden
194 194 o d004c8f274b9
195 195 |
196 196 | @ a468dc9b3633
197 197 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
198 198 | x 471f378eab4c
199 199 |/ Obsfate: rewritten using amend as 2:a468dc9b3633 by test (at 2009-02-13 23:31 +0000);
200 200 o ea207398892e
201 201
202 202
203 203 Predecessors template shouldn't show anything as all obsolete commit are not
204 204 visible.
205 205 $ hg up 'desc(A2)'
206 206 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
207 207 $ hg tlog
208 208 @ d004c8f274b9
209 209 |
210 210 o ea207398892e
211 211
212 212 $ hg tlog --hidden
213 213 @ d004c8f274b9
214 214 | Predecessors: 2:a468dc9b3633
215 215 | semi-colon: 2:a468dc9b3633
216 216 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
217 217 | map: 2:a468dc9b36338b14fdb7825f55ce3df4e71517ad
218 218 | x a468dc9b3633
219 219 |/ Predecessors: 1:471f378eab4c
220 220 | semi-colon: 1:471f378eab4c
221 221 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
222 222 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
223 223 | Successors: 3:d004c8f274b9
224 224 | multi-line: 3:d004c8f274b9
225 225 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
226 226 | x 471f378eab4c
227 227 |/ Successors: 2:a468dc9b3633
228 228 | multi-line: 2:a468dc9b3633
229 229 | json: [["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]]
230 230 o ea207398892e
231 231
232 232 $ hg fatelog
233 233 @ d004c8f274b9
234 234 |
235 235 o ea207398892e
236 236
237 237
238 238 $ hg fatelog --hidden
239 239 @ d004c8f274b9
240 240 |
241 241 | x a468dc9b3633
242 242 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
243 243 | x 471f378eab4c
244 244 |/ Obsfate: rewritten using amend as 2:a468dc9b3633 by test (at 2009-02-13 23:31 +0000);
245 245 o ea207398892e
246 246
247 247 $ hg fatelogjson --hidden
248 248 @ d004c8f274b9
249 249 |
250 250 | x a468dc9b3633
251 251 |/ Obsfate: [{"markers": [["a468dc9b36338b14fdb7825f55ce3df4e71517ad", ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], 0, [["operation", "amend"], ["user", "test2"]], [987654321.0, 0], null]], "successors": ["d004c8f274b9ec480a47a93c10dac5eee63adb78"]}]
252 252 | x 471f378eab4c
253 253 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], 0, [["operation", "amend"], ["user", "test"]], [1234567890.0, 0], null]], "successors": ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]}]
254 254 o ea207398892e
255 255
256 256
257 257 Check other fatelog implementations
258 258 -----------------------------------
259 259
260 260 $ hg fatelogkw --hidden -q
261 261 @ d004c8f274b9
262 262 |
263 263 | x a468dc9b3633
264 264 |/ Obsfate: rewritten using amend as 3:d004c8f274b9
265 265 | x 471f378eab4c
266 266 |/ Obsfate: rewritten using amend as 2:a468dc9b3633
267 267 o ea207398892e
268 268
269 269 $ hg fatelogkw --hidden
270 270 @ d004c8f274b9
271 271 |
272 272 | x a468dc9b3633
273 273 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test2
274 274 | x 471f378eab4c
275 275 |/ Obsfate: rewritten using amend as 2:a468dc9b3633
276 276 o ea207398892e
277 277
278 278 $ hg fatelogkw --hidden -v
279 279 @ d004c8f274b9
280 280 |
281 281 | x a468dc9b3633
282 282 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000)
283 283 | x 471f378eab4c
284 284 |/ Obsfate: rewritten using amend as 2:a468dc9b3633 by test (at 2009-02-13 23:31 +0000)
285 285 o ea207398892e
286 286
287 287
288 288 $ hg log -G -T "default" --hidden
289 289 @ changeset: 3:d004c8f274b9
290 290 | tag: tip
291 291 | parent: 0:ea207398892e
292 292 | user: test
293 293 | date: Thu Jan 01 00:00:00 1970 +0000
294 294 | summary: A2
295 295 |
296 296 | x changeset: 2:a468dc9b3633
297 297 |/ parent: 0:ea207398892e
298 298 | user: test
299 299 | date: Thu Jan 01 00:00:00 1970 +0000
300 300 | obsfate: rewritten using amend as 3:d004c8f274b9 by test2
301 301 | summary: A1
302 302 |
303 303 | x changeset: 1:471f378eab4c
304 304 |/ user: test
305 305 | date: Thu Jan 01 00:00:00 1970 +0000
306 306 | obsfate: rewritten using amend as 2:a468dc9b3633
307 307 | summary: A0
308 308 |
309 309 o changeset: 0:ea207398892e
310 310 user: test
311 311 date: Thu Jan 01 00:00:00 1970 +0000
312 312 summary: ROOT
313 313
314 314 $ hg log -G -T "default" --hidden -v
315 315 @ changeset: 3:d004c8f274b9
316 316 | tag: tip
317 317 | parent: 0:ea207398892e
318 318 | user: test
319 319 | date: Thu Jan 01 00:00:00 1970 +0000
320 320 | files: A0
321 321 | description:
322 322 | A2
323 323 |
324 324 |
325 325 | x changeset: 2:a468dc9b3633
326 326 |/ parent: 0:ea207398892e
327 327 | user: test
328 328 | date: Thu Jan 01 00:00:00 1970 +0000
329 329 | obsfate: rewritten using amend as 3:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000)
330 330 | files: A0
331 331 | description:
332 332 | A1
333 333 |
334 334 |
335 335 | x changeset: 1:471f378eab4c
336 336 |/ user: test
337 337 | date: Thu Jan 01 00:00:00 1970 +0000
338 338 | obsfate: rewritten using amend as 2:a468dc9b3633 by test (at 2009-02-13 23:31 +0000)
339 339 | files: A0
340 340 | description:
341 341 | A0
342 342 |
343 343 |
344 344 o changeset: 0:ea207398892e
345 345 user: test
346 346 date: Thu Jan 01 00:00:00 1970 +0000
347 347 files: ROOT
348 348 description:
349 349 ROOT
350 350
351 351
352 352 Test templates with splitted commit
353 353 ===================================
354 354
355 355 $ hg init $TESTTMP/templates-local-split
356 356 $ cd $TESTTMP/templates-local-split
357 357 $ mkcommit ROOT
358 358 $ echo 42 >> a
359 359 $ echo 43 >> b
360 360 $ hg commit -A -m "A0"
361 361 adding a
362 362 adding b
363 363 $ hg log --hidden -G
364 364 @ changeset: 1:471597cad322
365 365 | tag: tip
366 366 | user: test
367 367 | date: Thu Jan 01 00:00:00 1970 +0000
368 368 | summary: A0
369 369 |
370 370 o changeset: 0:ea207398892e
371 371 user: test
372 372 date: Thu Jan 01 00:00:00 1970 +0000
373 373 summary: ROOT
374 374
375 375 # Simulate split
376 376 $ hg up -r "desc(ROOT)"
377 377 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
378 378 $ echo 42 >> a
379 379 $ hg commit -A -m "A0"
380 380 adding a
381 381 created new head
382 382 $ echo 43 >> b
383 383 $ hg commit -A -m "A0"
384 384 adding b
385 385 $ hg debugobsolete `getid "1"` `getid "2"` `getid "3"`
386 386 obsoleted 1 changesets
387 387
388 388 $ hg log --hidden -G
389 389 @ changeset: 3:f257fde29c7a
390 390 | tag: tip
391 391 | user: test
392 392 | date: Thu Jan 01 00:00:00 1970 +0000
393 393 | summary: A0
394 394 |
395 395 o changeset: 2:337fec4d2edc
396 396 | parent: 0:ea207398892e
397 397 | user: test
398 398 | date: Thu Jan 01 00:00:00 1970 +0000
399 399 | summary: A0
400 400 |
401 401 | x changeset: 1:471597cad322
402 402 |/ user: test
403 403 | date: Thu Jan 01 00:00:00 1970 +0000
404 | obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a
404 | obsolete: split as 2:337fec4d2edc, 3:f257fde29c7a
405 405 | summary: A0
406 406 |
407 407 o changeset: 0:ea207398892e
408 408 user: test
409 409 date: Thu Jan 01 00:00:00 1970 +0000
410 410 summary: ROOT
411 411
412 412 Check templates
413 413 ---------------
414 414
415 415 $ hg up 'obsolete()' --hidden
416 416 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
417 417
418 418 Predecessors template should show current revision as it is the working copy
419 419 $ hg tlog
420 420 o f257fde29c7a
421 421 | Predecessors: 1:471597cad322
422 422 | semi-colon: 1:471597cad322
423 423 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
424 424 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
425 425 o 337fec4d2edc
426 426 | Predecessors: 1:471597cad322
427 427 | semi-colon: 1:471597cad322
428 428 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
429 429 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
430 430 | @ 471597cad322
431 431 |/ Successors: 2:337fec4d2edc 3:f257fde29c7a
432 432 | multi-line: 2:337fec4d2edc 3:f257fde29c7a
433 433 | json: [["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]]
434 434 o ea207398892e
435 435
436 436
437 437 $ hg fatelog
438 438 o f257fde29c7a
439 439 |
440 440 o 337fec4d2edc
441 441 |
442 442 | @ 471597cad322
443 443 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test (at 1970-01-01 00:00 +0000);
444 444 o ea207398892e
445 445
446 446 $ hg up f257fde29c7a
447 447 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
448 448
449 449 Predecessors template should not show a predecessor as it's not displayed in
450 450 the log
451 451 $ hg tlog
452 452 @ f257fde29c7a
453 453 |
454 454 o 337fec4d2edc
455 455 |
456 456 o ea207398892e
457 457
458 458 Predecessors template should show both predecessors as we force their display
459 459 with --hidden
460 460 $ hg tlog --hidden
461 461 @ f257fde29c7a
462 462 | Predecessors: 1:471597cad322
463 463 | semi-colon: 1:471597cad322
464 464 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
465 465 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
466 466 o 337fec4d2edc
467 467 | Predecessors: 1:471597cad322
468 468 | semi-colon: 1:471597cad322
469 469 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
470 470 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
471 471 | x 471597cad322
472 472 |/ Successors: 2:337fec4d2edc 3:f257fde29c7a
473 473 | multi-line: 2:337fec4d2edc 3:f257fde29c7a
474 474 | json: [["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]]
475 475 o ea207398892e
476 476
477 477
478 478 $ hg fatelog --hidden
479 479 @ f257fde29c7a
480 480 |
481 481 o 337fec4d2edc
482 482 |
483 483 | x 471597cad322
484 484 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test (at 1970-01-01 00:00 +0000);
485 485 o ea207398892e
486 486
487 487 $ hg fatelogjson --hidden
488 488 @ f257fde29c7a
489 489 |
490 490 o 337fec4d2edc
491 491 |
492 492 | x 471597cad322
493 493 |/ Obsfate: [{"markers": [["471597cad322d1f659bb169751be9133dad92ef3", ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]}]
494 494 o ea207398892e
495 495
496 496 Check other fatelog implementations
497 497 -----------------------------------
498 498
499 499 $ hg fatelogkw --hidden -q
500 500 @ f257fde29c7a
501 501 |
502 502 o 337fec4d2edc
503 503 |
504 504 | x 471597cad322
505 505 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a
506 506 o ea207398892e
507 507
508 508 $ hg fatelogkw --hidden
509 509 @ f257fde29c7a
510 510 |
511 511 o 337fec4d2edc
512 512 |
513 513 | x 471597cad322
514 514 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a
515 515 o ea207398892e
516 516
517 517 $ hg fatelogkw --hidden -v
518 518 @ f257fde29c7a
519 519 |
520 520 o 337fec4d2edc
521 521 |
522 522 | x 471597cad322
523 523 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test (at 1970-01-01 00:00 +0000)
524 524 o ea207398892e
525 525
526 526
527 527 $ hg log -G -T "default" --hidden
528 528 @ changeset: 3:f257fde29c7a
529 529 | tag: tip
530 530 | user: test
531 531 | date: Thu Jan 01 00:00:00 1970 +0000
532 532 | summary: A0
533 533 |
534 534 o changeset: 2:337fec4d2edc
535 535 | parent: 0:ea207398892e
536 536 | user: test
537 537 | date: Thu Jan 01 00:00:00 1970 +0000
538 538 | summary: A0
539 539 |
540 540 | x changeset: 1:471597cad322
541 541 |/ user: test
542 542 | date: Thu Jan 01 00:00:00 1970 +0000
543 543 | obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a
544 544 | summary: A0
545 545 |
546 546 o changeset: 0:ea207398892e
547 547 user: test
548 548 date: Thu Jan 01 00:00:00 1970 +0000
549 549 summary: ROOT
550 550
551 551
552 552 Test templates with folded commit
553 553 =================================
554 554
555 555 Test setup
556 556 ----------
557 557
558 558 $ hg init $TESTTMP/templates-local-fold
559 559 $ cd $TESTTMP/templates-local-fold
560 560 $ mkcommit ROOT
561 561 $ mkcommit A0
562 562 $ mkcommit B0
563 563 $ hg log --hidden -G
564 564 @ changeset: 2:0dec01379d3b
565 565 | tag: tip
566 566 | user: test
567 567 | date: Thu Jan 01 00:00:00 1970 +0000
568 568 | summary: B0
569 569 |
570 570 o changeset: 1:471f378eab4c
571 571 | user: test
572 572 | date: Thu Jan 01 00:00:00 1970 +0000
573 573 | summary: A0
574 574 |
575 575 o changeset: 0:ea207398892e
576 576 user: test
577 577 date: Thu Jan 01 00:00:00 1970 +0000
578 578 summary: ROOT
579 579
580 580 Simulate a fold
581 581 $ hg up -r "desc(ROOT)"
582 582 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
583 583 $ echo "A0" > A0
584 584 $ echo "B0" > B0
585 585 $ hg commit -A -m "C0"
586 586 adding A0
587 587 adding B0
588 588 created new head
589 589 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(C0)"`
590 590 obsoleted 1 changesets
591 591 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(C0)"`
592 592 obsoleted 1 changesets
593 593
594 594 $ hg log --hidden -G
595 595 @ changeset: 3:eb5a0daa2192
596 596 | tag: tip
597 597 | parent: 0:ea207398892e
598 598 | user: test
599 599 | date: Thu Jan 01 00:00:00 1970 +0000
600 600 | summary: C0
601 601 |
602 602 | x changeset: 2:0dec01379d3b
603 603 | | user: test
604 604 | | date: Thu Jan 01 00:00:00 1970 +0000
605 | | obsfate: rewritten as 3:eb5a0daa2192
605 | | obsolete: rewritten as 3:eb5a0daa2192
606 606 | | summary: B0
607 607 | |
608 608 | x changeset: 1:471f378eab4c
609 609 |/ user: test
610 610 | date: Thu Jan 01 00:00:00 1970 +0000
611 | obsfate: rewritten as 3:eb5a0daa2192
611 | obsolete: rewritten as 3:eb5a0daa2192
612 612 | summary: A0
613 613 |
614 614 o changeset: 0:ea207398892e
615 615 user: test
616 616 date: Thu Jan 01 00:00:00 1970 +0000
617 617 summary: ROOT
618 618
619 619 Check templates
620 620 ---------------
621 621
622 622 $ hg up 'desc(A0)' --hidden
623 623 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
624 624
625 625 Predecessors template should show current revision as it is the working copy
626 626 $ hg tlog
627 627 o eb5a0daa2192
628 628 | Predecessors: 1:471f378eab4c
629 629 | semi-colon: 1:471f378eab4c
630 630 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
631 631 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
632 632 | @ 471f378eab4c
633 633 |/ Successors: 3:eb5a0daa2192
634 634 | multi-line: 3:eb5a0daa2192
635 635 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
636 636 o ea207398892e
637 637
638 638
639 639 $ hg fatelog
640 640 o eb5a0daa2192
641 641 |
642 642 | @ 471f378eab4c
643 643 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
644 644 o ea207398892e
645 645
646 646 $ hg up 'desc(B0)' --hidden
647 647 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
648 648
649 649 Predecessors template should show both predecessors as they should be both
650 650 displayed
651 651 $ hg tlog
652 652 o eb5a0daa2192
653 653 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
654 654 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
655 655 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
656 656 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
657 657 | @ 0dec01379d3b
658 658 | | Successors: 3:eb5a0daa2192
659 659 | | multi-line: 3:eb5a0daa2192
660 660 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
661 661 | x 471f378eab4c
662 662 |/ Successors: 3:eb5a0daa2192
663 663 | multi-line: 3:eb5a0daa2192
664 664 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
665 665 o ea207398892e
666 666
667 667
668 668 $ hg fatelog
669 669 o eb5a0daa2192
670 670 |
671 671 | @ 0dec01379d3b
672 672 | | Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
673 673 | x 471f378eab4c
674 674 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
675 675 o ea207398892e
676 676
677 677 $ hg up 'desc(C0)'
678 678 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
679 679
680 680 Predecessors template should not show predecessors as they are not displayed in
681 681 the log
682 682 $ hg tlog
683 683 @ eb5a0daa2192
684 684 |
685 685 o ea207398892e
686 686
687 687 Predecessors template should show both predecessors as we force their display
688 688 with --hidden
689 689 $ hg tlog --hidden
690 690 @ eb5a0daa2192
691 691 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
692 692 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
693 693 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
694 694 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
695 695 | x 0dec01379d3b
696 696 | | Successors: 3:eb5a0daa2192
697 697 | | multi-line: 3:eb5a0daa2192
698 698 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
699 699 | x 471f378eab4c
700 700 |/ Successors: 3:eb5a0daa2192
701 701 | multi-line: 3:eb5a0daa2192
702 702 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
703 703 o ea207398892e
704 704
705 705
706 706 $ hg fatelog --hidden
707 707 @ eb5a0daa2192
708 708 |
709 709 | x 0dec01379d3b
710 710 | | Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
711 711 | x 471f378eab4c
712 712 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
713 713 o ea207398892e
714 714
715 715
716 716 $ hg fatelogjson --hidden
717 717 @ eb5a0daa2192
718 718 |
719 719 | x 0dec01379d3b
720 720 | | Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
721 721 | x 471f378eab4c
722 722 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
723 723 o ea207398892e
724 724
725 725 Check other fatelog implementations
726 726 -----------------------------------
727 727
728 728 $ hg fatelogkw --hidden -q
729 729 @ eb5a0daa2192
730 730 |
731 731 | x 0dec01379d3b
732 732 | | Obsfate: rewritten as 3:eb5a0daa2192
733 733 | x 471f378eab4c
734 734 |/ Obsfate: rewritten as 3:eb5a0daa2192
735 735 o ea207398892e
736 736
737 737 $ hg fatelogkw --hidden
738 738 @ eb5a0daa2192
739 739 |
740 740 | x 0dec01379d3b
741 741 | | Obsfate: rewritten as 3:eb5a0daa2192
742 742 | x 471f378eab4c
743 743 |/ Obsfate: rewritten as 3:eb5a0daa2192
744 744 o ea207398892e
745 745
746 746 $ hg fatelogkw --hidden -v
747 747 @ eb5a0daa2192
748 748 |
749 749 | x 0dec01379d3b
750 750 | | Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000)
751 751 | x 471f378eab4c
752 752 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000)
753 753 o ea207398892e
754 754
755 755 $ hg log -G -T "default" --hidden
756 756 @ changeset: 3:eb5a0daa2192
757 757 | tag: tip
758 758 | parent: 0:ea207398892e
759 759 | user: test
760 760 | date: Thu Jan 01 00:00:00 1970 +0000
761 761 | summary: C0
762 762 |
763 763 | x changeset: 2:0dec01379d3b
764 764 | | user: test
765 765 | | date: Thu Jan 01 00:00:00 1970 +0000
766 766 | | obsfate: rewritten as 3:eb5a0daa2192
767 767 | | summary: B0
768 768 | |
769 769 | x changeset: 1:471f378eab4c
770 770 |/ user: test
771 771 | date: Thu Jan 01 00:00:00 1970 +0000
772 772 | obsfate: rewritten as 3:eb5a0daa2192
773 773 | summary: A0
774 774 |
775 775 o changeset: 0:ea207398892e
776 776 user: test
777 777 date: Thu Jan 01 00:00:00 1970 +0000
778 778 summary: ROOT
779 779
780 780
781 781 Test templates with divergence
782 782 ==============================
783 783
784 784 Test setup
785 785 ----------
786 786
787 787 $ hg init $TESTTMP/templates-local-divergence
788 788 $ cd $TESTTMP/templates-local-divergence
789 789 $ mkcommit ROOT
790 790 $ mkcommit A0
791 791 $ hg commit --amend -m "A1"
792 792 $ hg log --hidden -G
793 793 @ changeset: 2:fdf9bde5129a
794 794 | tag: tip
795 795 | parent: 0:ea207398892e
796 796 | user: test
797 797 | date: Thu Jan 01 00:00:00 1970 +0000
798 798 | summary: A1
799 799 |
800 800 | x changeset: 1:471f378eab4c
801 801 |/ user: test
802 802 | date: Thu Jan 01 00:00:00 1970 +0000
803 | obsfate: rewritten using amend as 2:fdf9bde5129a
803 | obsolete: rewritten using amend as 2:fdf9bde5129a
804 804 | summary: A0
805 805 |
806 806 o changeset: 0:ea207398892e
807 807 user: test
808 808 date: Thu Jan 01 00:00:00 1970 +0000
809 809 summary: ROOT
810 810
811 811 $ hg update --hidden 'desc(A0)'
812 812 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
813 813 $ hg commit --amend -m "A2"
814 814 $ hg log --hidden -G
815 815 @ changeset: 3:65b757b745b9
816 816 | tag: tip
817 817 | parent: 0:ea207398892e
818 818 | user: test
819 819 | date: Thu Jan 01 00:00:00 1970 +0000
820 820 | instability: content-divergent
821 821 | summary: A2
822 822 |
823 823 | o changeset: 2:fdf9bde5129a
824 824 |/ parent: 0:ea207398892e
825 825 | user: test
826 826 | date: Thu Jan 01 00:00:00 1970 +0000
827 827 | instability: content-divergent
828 828 | summary: A1
829 829 |
830 830 | x changeset: 1:471f378eab4c
831 831 |/ user: test
832 832 | date: Thu Jan 01 00:00:00 1970 +0000
833 | obsfate: rewritten using amend as 2:fdf9bde5129a
834 | obsfate: rewritten using amend as 3:65b757b745b9
833 | obsolete: rewritten using amend as 2:fdf9bde5129a
834 | obsolete: rewritten using amend as 3:65b757b745b9
835 835 | summary: A0
836 836 |
837 837 o changeset: 0:ea207398892e
838 838 user: test
839 839 date: Thu Jan 01 00:00:00 1970 +0000
840 840 summary: ROOT
841 841
842 842 $ hg commit --amend -m 'A3'
843 843 $ hg log --hidden -G
844 844 @ changeset: 4:019fadeab383
845 845 | tag: tip
846 846 | parent: 0:ea207398892e
847 847 | user: test
848 848 | date: Thu Jan 01 00:00:00 1970 +0000
849 849 | instability: content-divergent
850 850 | summary: A3
851 851 |
852 852 | x changeset: 3:65b757b745b9
853 853 |/ parent: 0:ea207398892e
854 854 | user: test
855 855 | date: Thu Jan 01 00:00:00 1970 +0000
856 | obsfate: rewritten using amend as 4:019fadeab383
856 | obsolete: rewritten using amend as 4:019fadeab383
857 857 | summary: A2
858 858 |
859 859 | o changeset: 2:fdf9bde5129a
860 860 |/ parent: 0:ea207398892e
861 861 | user: test
862 862 | date: Thu Jan 01 00:00:00 1970 +0000
863 863 | instability: content-divergent
864 864 | summary: A1
865 865 |
866 866 | x changeset: 1:471f378eab4c
867 867 |/ user: test
868 868 | date: Thu Jan 01 00:00:00 1970 +0000
869 | obsfate: rewritten using amend as 2:fdf9bde5129a
870 | obsfate: rewritten using amend as 3:65b757b745b9
869 | obsolete: rewritten using amend as 2:fdf9bde5129a
870 | obsolete: rewritten using amend as 3:65b757b745b9
871 871 | summary: A0
872 872 |
873 873 o changeset: 0:ea207398892e
874 874 user: test
875 875 date: Thu Jan 01 00:00:00 1970 +0000
876 876 summary: ROOT
877 877
878 878
879 879 Check templates
880 880 ---------------
881 881
882 882 $ hg up 'desc(A0)' --hidden
883 883 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
884 884
885 885 Predecessors template should show current revision as it is the working copy
886 886 $ hg tlog
887 887 o 019fadeab383
888 888 | Predecessors: 1:471f378eab4c
889 889 | semi-colon: 1:471f378eab4c
890 890 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
891 891 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
892 892 | o fdf9bde5129a
893 893 |/ Predecessors: 1:471f378eab4c
894 894 | semi-colon: 1:471f378eab4c
895 895 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
896 896 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
897 897 | @ 471f378eab4c
898 898 |/ Successors: 2:fdf9bde5129a; 4:019fadeab383
899 899 | multi-line: 2:fdf9bde5129a
900 900 | multi-line: 4:019fadeab383
901 901 | json: [["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]]
902 902 o ea207398892e
903 903
904 904 $ hg fatelog
905 905 o 019fadeab383
906 906 |
907 907 | o fdf9bde5129a
908 908 |/
909 909 | @ 471f378eab4c
910 910 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000); rewritten using amend as 4:019fadeab383 by test (at 1970-01-01 00:00 +0000);
911 911 o ea207398892e
912 912
913 913 $ hg up 'desc(A1)'
914 914 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
915 915
916 916 Predecessors template should not show predecessors as they are not displayed in
917 917 the log
918 918 $ hg tlog
919 919 o 019fadeab383
920 920 |
921 921 | @ fdf9bde5129a
922 922 |/
923 923 o ea207398892e
924 924
925 925
926 926 $ hg fatelog
927 927 o 019fadeab383
928 928 |
929 929 | @ fdf9bde5129a
930 930 |/
931 931 o ea207398892e
932 932
933 933 Predecessors template should the predecessors as we force their display with
934 934 --hidden
935 935 $ hg tlog --hidden
936 936 o 019fadeab383
937 937 | Predecessors: 3:65b757b745b9
938 938 | semi-colon: 3:65b757b745b9
939 939 | json: ["65b757b745b935093c87a2bccd877521cccffcbd"]
940 940 | map: 3:65b757b745b935093c87a2bccd877521cccffcbd
941 941 | x 65b757b745b9
942 942 |/ Predecessors: 1:471f378eab4c
943 943 | semi-colon: 1:471f378eab4c
944 944 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
945 945 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
946 946 | Successors: 4:019fadeab383
947 947 | multi-line: 4:019fadeab383
948 948 | json: [["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]]
949 949 | @ fdf9bde5129a
950 950 |/ Predecessors: 1:471f378eab4c
951 951 | semi-colon: 1:471f378eab4c
952 952 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
953 953 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
954 954 | x 471f378eab4c
955 955 |/ Successors: 2:fdf9bde5129a; 3:65b757b745b9
956 956 | multi-line: 2:fdf9bde5129a
957 957 | multi-line: 3:65b757b745b9
958 958 | json: [["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], ["65b757b745b935093c87a2bccd877521cccffcbd"]]
959 959 o ea207398892e
960 960
961 961
962 962 $ hg fatelog --hidden
963 963 o 019fadeab383
964 964 |
965 965 | x 65b757b745b9
966 966 |/ Obsfate: rewritten using amend as 4:019fadeab383 by test (at 1970-01-01 00:00 +0000);
967 967 | @ fdf9bde5129a
968 968 |/
969 969 | x 471f378eab4c
970 970 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000); rewritten using amend as 3:65b757b745b9 by test (at 1970-01-01 00:00 +0000);
971 971 o ea207398892e
972 972
973 973
974 974 $ hg fatelogjson --hidden
975 975 o 019fadeab383
976 976 |
977 977 | x 65b757b745b9
978 978 |/ Obsfate: [{"markers": [["65b757b745b935093c87a2bccd877521cccffcbd", ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], 0, [["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]}]
979 979 | @ fdf9bde5129a
980 980 |/
981 981 | x 471f378eab4c
982 982 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"]}, {"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["65b757b745b935093c87a2bccd877521cccffcbd"], 0, [["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["65b757b745b935093c87a2bccd877521cccffcbd"]}]
983 983 o ea207398892e
984 984
985 985
986 986 Check other fatelog implementations
987 987 -----------------------------------
988 988
989 989 $ hg fatelogkw --hidden -q
990 990 o 019fadeab383
991 991 |
992 992 | x 65b757b745b9
993 993 |/ Obsfate: rewritten using amend as 4:019fadeab383
994 994 | @ fdf9bde5129a
995 995 |/
996 996 | x 471f378eab4c
997 997 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a
998 998 | Obsfate: rewritten using amend as 3:65b757b745b9
999 999 o ea207398892e
1000 1000
1001 1001 $ hg fatelogkw --hidden
1002 1002 o 019fadeab383
1003 1003 |
1004 1004 | x 65b757b745b9
1005 1005 |/ Obsfate: rewritten using amend as 4:019fadeab383
1006 1006 | @ fdf9bde5129a
1007 1007 |/
1008 1008 | x 471f378eab4c
1009 1009 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a
1010 1010 | Obsfate: rewritten using amend as 3:65b757b745b9
1011 1011 o ea207398892e
1012 1012
1013 1013 $ hg fatelogkw --hidden -v
1014 1014 o 019fadeab383
1015 1015 |
1016 1016 | x 65b757b745b9
1017 1017 |/ Obsfate: rewritten using amend as 4:019fadeab383 by test (at 1970-01-01 00:00 +0000)
1018 1018 | @ fdf9bde5129a
1019 1019 |/
1020 1020 | x 471f378eab4c
1021 1021 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000)
1022 1022 | Obsfate: rewritten using amend as 3:65b757b745b9 by test (at 1970-01-01 00:00 +0000)
1023 1023 o ea207398892e
1024 1024
1025 1025 $ hg log -G -T "default" --hidden
1026 1026 o changeset: 4:019fadeab383
1027 1027 | tag: tip
1028 1028 | parent: 0:ea207398892e
1029 1029 | user: test
1030 1030 | date: Thu Jan 01 00:00:00 1970 +0000
1031 1031 | instability: content-divergent
1032 1032 | summary: A3
1033 1033 |
1034 1034 | x changeset: 3:65b757b745b9
1035 1035 |/ parent: 0:ea207398892e
1036 1036 | user: test
1037 1037 | date: Thu Jan 01 00:00:00 1970 +0000
1038 1038 | obsfate: rewritten using amend as 4:019fadeab383
1039 1039 | summary: A2
1040 1040 |
1041 1041 | @ changeset: 2:fdf9bde5129a
1042 1042 |/ parent: 0:ea207398892e
1043 1043 | user: test
1044 1044 | date: Thu Jan 01 00:00:00 1970 +0000
1045 1045 | instability: content-divergent
1046 1046 | summary: A1
1047 1047 |
1048 1048 | x changeset: 1:471f378eab4c
1049 1049 |/ user: test
1050 1050 | date: Thu Jan 01 00:00:00 1970 +0000
1051 1051 | obsfate: rewritten using amend as 2:fdf9bde5129a
1052 1052 | obsfate: rewritten using amend as 3:65b757b745b9
1053 1053 | summary: A0
1054 1054 |
1055 1055 o changeset: 0:ea207398892e
1056 1056 user: test
1057 1057 date: Thu Jan 01 00:00:00 1970 +0000
1058 1058 summary: ROOT
1059 1059
1060 1060
1061 1061 Test templates with amended + folded commit
1062 1062 ===========================================
1063 1063
1064 1064 Test setup
1065 1065 ----------
1066 1066
1067 1067 $ hg init $TESTTMP/templates-local-amend-fold
1068 1068 $ cd $TESTTMP/templates-local-amend-fold
1069 1069 $ mkcommit ROOT
1070 1070 $ mkcommit A0
1071 1071 $ mkcommit B0
1072 1072 $ hg commit --amend -m "B1"
1073 1073 $ hg log --hidden -G
1074 1074 @ changeset: 3:b7ea6d14e664
1075 1075 | tag: tip
1076 1076 | parent: 1:471f378eab4c
1077 1077 | user: test
1078 1078 | date: Thu Jan 01 00:00:00 1970 +0000
1079 1079 | summary: B1
1080 1080 |
1081 1081 | x changeset: 2:0dec01379d3b
1082 1082 |/ user: test
1083 1083 | date: Thu Jan 01 00:00:00 1970 +0000
1084 | obsfate: rewritten using amend as 3:b7ea6d14e664
1084 | obsolete: rewritten using amend as 3:b7ea6d14e664
1085 1085 | summary: B0
1086 1086 |
1087 1087 o changeset: 1:471f378eab4c
1088 1088 | user: test
1089 1089 | date: Thu Jan 01 00:00:00 1970 +0000
1090 1090 | summary: A0
1091 1091 |
1092 1092 o changeset: 0:ea207398892e
1093 1093 user: test
1094 1094 date: Thu Jan 01 00:00:00 1970 +0000
1095 1095 summary: ROOT
1096 1096
1097 1097 # Simulate a fold
1098 1098 $ hg up -r "desc(ROOT)"
1099 1099 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1100 1100 $ echo "A0" > A0
1101 1101 $ echo "B0" > B0
1102 1102 $ hg commit -A -m "C0"
1103 1103 adding A0
1104 1104 adding B0
1105 1105 created new head
1106 1106 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(C0)"`
1107 1107 obsoleted 1 changesets
1108 1108 $ hg debugobsolete `getid "desc(B1)"` `getid "desc(C0)"`
1109 1109 obsoleted 1 changesets
1110 1110
1111 1111 $ hg log --hidden -G
1112 1112 @ changeset: 4:eb5a0daa2192
1113 1113 | tag: tip
1114 1114 | parent: 0:ea207398892e
1115 1115 | user: test
1116 1116 | date: Thu Jan 01 00:00:00 1970 +0000
1117 1117 | summary: C0
1118 1118 |
1119 1119 | x changeset: 3:b7ea6d14e664
1120 1120 | | parent: 1:471f378eab4c
1121 1121 | | user: test
1122 1122 | | date: Thu Jan 01 00:00:00 1970 +0000
1123 | | obsfate: rewritten as 4:eb5a0daa2192
1123 | | obsolete: rewritten as 4:eb5a0daa2192
1124 1124 | | summary: B1
1125 1125 | |
1126 1126 | | x changeset: 2:0dec01379d3b
1127 1127 | |/ user: test
1128 1128 | | date: Thu Jan 01 00:00:00 1970 +0000
1129 | | obsfate: rewritten using amend as 3:b7ea6d14e664
1129 | | obsolete: rewritten using amend as 3:b7ea6d14e664
1130 1130 | | summary: B0
1131 1131 | |
1132 1132 | x changeset: 1:471f378eab4c
1133 1133 |/ user: test
1134 1134 | date: Thu Jan 01 00:00:00 1970 +0000
1135 | obsfate: rewritten as 4:eb5a0daa2192
1135 | obsolete: rewritten as 4:eb5a0daa2192
1136 1136 | summary: A0
1137 1137 |
1138 1138 o changeset: 0:ea207398892e
1139 1139 user: test
1140 1140 date: Thu Jan 01 00:00:00 1970 +0000
1141 1141 summary: ROOT
1142 1142
1143 1143 Check templates
1144 1144 ---------------
1145 1145
1146 1146 $ hg up 'desc(A0)' --hidden
1147 1147 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1148 1148
1149 1149 Predecessors template should show current revision as it is the working copy
1150 1150 $ hg tlog
1151 1151 o eb5a0daa2192
1152 1152 | Predecessors: 1:471f378eab4c
1153 1153 | semi-colon: 1:471f378eab4c
1154 1154 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1155 1155 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1156 1156 | @ 471f378eab4c
1157 1157 |/ Successors: 4:eb5a0daa2192
1158 1158 | multi-line: 4:eb5a0daa2192
1159 1159 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1160 1160 o ea207398892e
1161 1161
1162 1162
1163 1163 $ hg fatelog
1164 1164 o eb5a0daa2192
1165 1165 |
1166 1166 | @ 471f378eab4c
1167 1167 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1168 1168 o ea207398892e
1169 1169
1170 1170 $ hg up 'desc(B0)' --hidden
1171 1171 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1172 1172
1173 1173 Predecessors template should both predecessors as they are visible
1174 1174 $ hg tlog
1175 1175 o eb5a0daa2192
1176 1176 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
1177 1177 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
1178 1178 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
1179 1179 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
1180 1180 | @ 0dec01379d3b
1181 1181 | | Successors: 4:eb5a0daa2192
1182 1182 | | multi-line: 4:eb5a0daa2192
1183 1183 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1184 1184 | x 471f378eab4c
1185 1185 |/ Successors: 4:eb5a0daa2192
1186 1186 | multi-line: 4:eb5a0daa2192
1187 1187 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1188 1188 o ea207398892e
1189 1189
1190 1190
1191 1191 $ hg fatelog
1192 1192 o eb5a0daa2192
1193 1193 |
1194 1194 | @ 0dec01379d3b
1195 1195 | | Obsfate: rewritten using amend as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1196 1196 | x 471f378eab4c
1197 1197 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1198 1198 o ea207398892e
1199 1199
1200 1200 $ hg up 'desc(B1)' --hidden
1201 1201 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1202 1202
1203 1203 Predecessors template should both predecessors as they are visible
1204 1204 $ hg tlog
1205 1205 o eb5a0daa2192
1206 1206 | Predecessors: 1:471f378eab4c 3:b7ea6d14e664
1207 1207 | semi-colon: 1:471f378eab4c; 3:b7ea6d14e664
1208 1208 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874", "b7ea6d14e664bdc8922221f7992631b50da3fb07"]
1209 1209 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874 3:b7ea6d14e664bdc8922221f7992631b50da3fb07
1210 1210 | @ b7ea6d14e664
1211 1211 | | Successors: 4:eb5a0daa2192
1212 1212 | | multi-line: 4:eb5a0daa2192
1213 1213 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1214 1214 | x 471f378eab4c
1215 1215 |/ Successors: 4:eb5a0daa2192
1216 1216 | multi-line: 4:eb5a0daa2192
1217 1217 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1218 1218 o ea207398892e
1219 1219
1220 1220
1221 1221 $ hg fatelog
1222 1222 o eb5a0daa2192
1223 1223 |
1224 1224 | @ b7ea6d14e664
1225 1225 | | Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1226 1226 | x 471f378eab4c
1227 1227 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1228 1228 o ea207398892e
1229 1229
1230 1230 $ hg up 'desc(C0)'
1231 1231 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1232 1232
1233 1233 Predecessors template should show no predecessors as they are both non visible
1234 1234 $ hg tlog
1235 1235 @ eb5a0daa2192
1236 1236 |
1237 1237 o ea207398892e
1238 1238
1239 1239
1240 1240 $ hg fatelog
1241 1241 @ eb5a0daa2192
1242 1242 |
1243 1243 o ea207398892e
1244 1244
1245 1245 Predecessors template should show all predecessors as we force their display
1246 1246 with --hidden
1247 1247 $ hg tlog --hidden
1248 1248 @ eb5a0daa2192
1249 1249 | Predecessors: 1:471f378eab4c 3:b7ea6d14e664
1250 1250 | semi-colon: 1:471f378eab4c; 3:b7ea6d14e664
1251 1251 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874", "b7ea6d14e664bdc8922221f7992631b50da3fb07"]
1252 1252 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874 3:b7ea6d14e664bdc8922221f7992631b50da3fb07
1253 1253 | x b7ea6d14e664
1254 1254 | | Predecessors: 2:0dec01379d3b
1255 1255 | | semi-colon: 2:0dec01379d3b
1256 1256 | | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1257 1257 | | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1258 1258 | | Successors: 4:eb5a0daa2192
1259 1259 | | multi-line: 4:eb5a0daa2192
1260 1260 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1261 1261 | | x 0dec01379d3b
1262 1262 | |/ Successors: 3:b7ea6d14e664
1263 1263 | | multi-line: 3:b7ea6d14e664
1264 1264 | | json: [["b7ea6d14e664bdc8922221f7992631b50da3fb07"]]
1265 1265 | x 471f378eab4c
1266 1266 |/ Successors: 4:eb5a0daa2192
1267 1267 | multi-line: 4:eb5a0daa2192
1268 1268 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1269 1269 o ea207398892e
1270 1270
1271 1271
1272 1272 $ hg fatelog --hidden
1273 1273 @ eb5a0daa2192
1274 1274 |
1275 1275 | x b7ea6d14e664
1276 1276 | | Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1277 1277 | | x 0dec01379d3b
1278 1278 | |/ Obsfate: rewritten using amend as 3:b7ea6d14e664 by test (at 1970-01-01 00:00 +0000);
1279 1279 | x 471f378eab4c
1280 1280 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1281 1281 o ea207398892e
1282 1282
1283 1283
1284 1284 $ hg fatelogjson --hidden
1285 1285 @ eb5a0daa2192
1286 1286 |
1287 1287 | x b7ea6d14e664
1288 1288 | | Obsfate: [{"markers": [["b7ea6d14e664bdc8922221f7992631b50da3fb07", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
1289 1289 | | x 0dec01379d3b
1290 1290 | |/ Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], 0, [["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["b7ea6d14e664bdc8922221f7992631b50da3fb07"]}]
1291 1291 | x 471f378eab4c
1292 1292 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
1293 1293 o ea207398892e
1294 1294
1295 1295
1296 1296 Check other fatelog implementations
1297 1297 -----------------------------------
1298 1298
1299 1299 $ hg fatelogkw --hidden -q
1300 1300 @ eb5a0daa2192
1301 1301 |
1302 1302 | x b7ea6d14e664
1303 1303 | | Obsfate: rewritten as 4:eb5a0daa2192
1304 1304 | | x 0dec01379d3b
1305 1305 | |/ Obsfate: rewritten using amend as 3:b7ea6d14e664
1306 1306 | x 471f378eab4c
1307 1307 |/ Obsfate: rewritten as 4:eb5a0daa2192
1308 1308 o ea207398892e
1309 1309
1310 1310 $ hg fatelogkw --hidden
1311 1311 @ eb5a0daa2192
1312 1312 |
1313 1313 | x b7ea6d14e664
1314 1314 | | Obsfate: rewritten as 4:eb5a0daa2192
1315 1315 | | x 0dec01379d3b
1316 1316 | |/ Obsfate: rewritten using amend as 3:b7ea6d14e664
1317 1317 | x 471f378eab4c
1318 1318 |/ Obsfate: rewritten as 4:eb5a0daa2192
1319 1319 o ea207398892e
1320 1320
1321 1321 $ hg fatelogkw --hidden -v
1322 1322 @ eb5a0daa2192
1323 1323 |
1324 1324 | x b7ea6d14e664
1325 1325 | | Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000)
1326 1326 | | x 0dec01379d3b
1327 1327 | |/ Obsfate: rewritten using amend as 3:b7ea6d14e664 by test (at 1970-01-01 00:00 +0000)
1328 1328 | x 471f378eab4c
1329 1329 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000)
1330 1330 o ea207398892e
1331 1331
1332 1332 $ hg log -G -T "default" --hidden
1333 1333 @ changeset: 4:eb5a0daa2192
1334 1334 | tag: tip
1335 1335 | parent: 0:ea207398892e
1336 1336 | user: test
1337 1337 | date: Thu Jan 01 00:00:00 1970 +0000
1338 1338 | summary: C0
1339 1339 |
1340 1340 | x changeset: 3:b7ea6d14e664
1341 1341 | | parent: 1:471f378eab4c
1342 1342 | | user: test
1343 1343 | | date: Thu Jan 01 00:00:00 1970 +0000
1344 1344 | | obsfate: rewritten as 4:eb5a0daa2192
1345 1345 | | summary: B1
1346 1346 | |
1347 1347 | | x changeset: 2:0dec01379d3b
1348 1348 | |/ user: test
1349 1349 | | date: Thu Jan 01 00:00:00 1970 +0000
1350 1350 | | obsfate: rewritten using amend as 3:b7ea6d14e664
1351 1351 | | summary: B0
1352 1352 | |
1353 1353 | x changeset: 1:471f378eab4c
1354 1354 |/ user: test
1355 1355 | date: Thu Jan 01 00:00:00 1970 +0000
1356 1356 | obsfate: rewritten as 4:eb5a0daa2192
1357 1357 | summary: A0
1358 1358 |
1359 1359 o changeset: 0:ea207398892e
1360 1360 user: test
1361 1361 date: Thu Jan 01 00:00:00 1970 +0000
1362 1362 summary: ROOT
1363 1363
1364 1364
1365 1365 Test template with pushed and pulled obs markers
1366 1366 ================================================
1367 1367
1368 1368 Test setup
1369 1369 ----------
1370 1370
1371 1371 $ hg init $TESTTMP/templates-local-remote-markers-1
1372 1372 $ cd $TESTTMP/templates-local-remote-markers-1
1373 1373 $ mkcommit ROOT
1374 1374 $ mkcommit A0
1375 1375 $ hg clone $TESTTMP/templates-local-remote-markers-1 $TESTTMP/templates-local-remote-markers-2
1376 1376 updating to branch default
1377 1377 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1378 1378 $ cd $TESTTMP/templates-local-remote-markers-2
1379 1379 $ hg log --hidden -G
1380 1380 @ changeset: 1:471f378eab4c
1381 1381 | tag: tip
1382 1382 | user: test
1383 1383 | date: Thu Jan 01 00:00:00 1970 +0000
1384 1384 | summary: A0
1385 1385 |
1386 1386 o changeset: 0:ea207398892e
1387 1387 user: test
1388 1388 date: Thu Jan 01 00:00:00 1970 +0000
1389 1389 summary: ROOT
1390 1390
1391 1391 $ cd $TESTTMP/templates-local-remote-markers-1
1392 1392 $ hg commit --amend -m "A1"
1393 1393 $ hg commit --amend -m "A2"
1394 1394 $ hg log --hidden -G
1395 1395 @ changeset: 3:7a230b46bf61
1396 1396 | tag: tip
1397 1397 | parent: 0:ea207398892e
1398 1398 | user: test
1399 1399 | date: Thu Jan 01 00:00:00 1970 +0000
1400 1400 | summary: A2
1401 1401 |
1402 1402 | x changeset: 2:fdf9bde5129a
1403 1403 |/ parent: 0:ea207398892e
1404 1404 | user: test
1405 1405 | date: Thu Jan 01 00:00:00 1970 +0000
1406 | obsfate: rewritten using amend as 3:7a230b46bf61
1406 | obsolete: rewritten using amend as 3:7a230b46bf61
1407 1407 | summary: A1
1408 1408 |
1409 1409 | x changeset: 1:471f378eab4c
1410 1410 |/ user: test
1411 1411 | date: Thu Jan 01 00:00:00 1970 +0000
1412 | obsfate: rewritten using amend as 2:fdf9bde5129a
1412 | obsolete: rewritten using amend as 2:fdf9bde5129a
1413 1413 | summary: A0
1414 1414 |
1415 1415 o changeset: 0:ea207398892e
1416 1416 user: test
1417 1417 date: Thu Jan 01 00:00:00 1970 +0000
1418 1418 summary: ROOT
1419 1419
1420 1420 $ cd $TESTTMP/templates-local-remote-markers-2
1421 1421 $ hg pull
1422 1422 pulling from $TESTTMP/templates-local-remote-markers-1 (glob)
1423 1423 searching for changes
1424 1424 adding changesets
1425 1425 adding manifests
1426 1426 adding file changes
1427 1427 added 1 changesets with 0 changes to 1 files (+1 heads)
1428 1428 2 new obsolescence markers
1429 1429 obsoleted 1 changesets
1430 1430 new changesets 7a230b46bf61
1431 1431 (run 'hg heads' to see heads, 'hg merge' to merge)
1432 1432 $ hg log --hidden -G
1433 1433 o changeset: 2:7a230b46bf61
1434 1434 | tag: tip
1435 1435 | parent: 0:ea207398892e
1436 1436 | user: test
1437 1437 | date: Thu Jan 01 00:00:00 1970 +0000
1438 1438 | summary: A2
1439 1439 |
1440 1440 | @ changeset: 1:471f378eab4c
1441 1441 |/ user: test
1442 1442 | date: Thu Jan 01 00:00:00 1970 +0000
1443 | obsfate: rewritten using amend as 2:7a230b46bf61
1443 | obsolete: rewritten using amend as 2:7a230b46bf61
1444 1444 | summary: A0
1445 1445 |
1446 1446 o changeset: 0:ea207398892e
1447 1447 user: test
1448 1448 date: Thu Jan 01 00:00:00 1970 +0000
1449 1449 summary: ROOT
1450 1450
1451 1451
1452 1452 $ hg debugobsolete
1453 1453 471f378eab4c5e25f6c77f785b27c936efb22874 fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1454 1454 fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e 7a230b46bf61e50b30308c6cfd7bd1269ef54702 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1455 1455
1456 1456 Check templates
1457 1457 ---------------
1458 1458
1459 1459 Predecessors template should show current revision as it is the working copy
1460 1460 $ hg tlog
1461 1461 o 7a230b46bf61
1462 1462 | Predecessors: 1:471f378eab4c
1463 1463 | semi-colon: 1:471f378eab4c
1464 1464 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1465 1465 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1466 1466 | @ 471f378eab4c
1467 1467 |/ Successors: 2:7a230b46bf61
1468 1468 | multi-line: 2:7a230b46bf61
1469 1469 | json: [["7a230b46bf61e50b30308c6cfd7bd1269ef54702"]]
1470 1470 o ea207398892e
1471 1471
1472 1472
1473 1473 $ hg fatelog
1474 1474 o 7a230b46bf61
1475 1475 |
1476 1476 | @ 471f378eab4c
1477 1477 |/ Obsfate: rewritten using amend as 2:7a230b46bf61 by test (at 1970-01-01 00:00 +0000);
1478 1478 o ea207398892e
1479 1479
1480 1480 $ hg up 'desc(A2)'
1481 1481 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1482 1482
1483 1483 Predecessors template should show no predecessors as they are non visible
1484 1484 $ hg tlog
1485 1485 @ 7a230b46bf61
1486 1486 |
1487 1487 o ea207398892e
1488 1488
1489 1489
1490 1490 $ hg fatelog
1491 1491 @ 7a230b46bf61
1492 1492 |
1493 1493 o ea207398892e
1494 1494
1495 1495 Predecessors template should show all predecessors as we force their display
1496 1496 with --hidden
1497 1497 $ hg tlog --hidden
1498 1498 @ 7a230b46bf61
1499 1499 | Predecessors: 1:471f378eab4c
1500 1500 | semi-colon: 1:471f378eab4c
1501 1501 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1502 1502 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1503 1503 | x 471f378eab4c
1504 1504 |/ Successors: 2:7a230b46bf61
1505 1505 | multi-line: 2:7a230b46bf61
1506 1506 | json: [["7a230b46bf61e50b30308c6cfd7bd1269ef54702"]]
1507 1507 o ea207398892e
1508 1508
1509 1509
1510 1510 $ hg fatelog --hidden
1511 1511 @ 7a230b46bf61
1512 1512 |
1513 1513 | x 471f378eab4c
1514 1514 |/ Obsfate: rewritten using amend as 2:7a230b46bf61 by test (at 1970-01-01 00:00 +0000);
1515 1515 o ea207398892e
1516 1516
1517 1517
1518 1518 Check other fatelog implementations
1519 1519 -----------------------------------
1520 1520
1521 1521 $ hg fatelogkw --hidden -q
1522 1522 @ 7a230b46bf61
1523 1523 |
1524 1524 | x 471f378eab4c
1525 1525 |/ Obsfate: rewritten using amend as 2:7a230b46bf61
1526 1526 o ea207398892e
1527 1527
1528 1528 $ hg fatelogkw --hidden
1529 1529 @ 7a230b46bf61
1530 1530 |
1531 1531 | x 471f378eab4c
1532 1532 |/ Obsfate: rewritten using amend as 2:7a230b46bf61
1533 1533 o ea207398892e
1534 1534
1535 1535 $ hg fatelogkw --hidden -v
1536 1536 @ 7a230b46bf61
1537 1537 |
1538 1538 | x 471f378eab4c
1539 1539 |/ Obsfate: rewritten using amend as 2:7a230b46bf61 by test (at 1970-01-01 00:00 +0000)
1540 1540 o ea207398892e
1541 1541
1542 1542 $ hg log -G -T "default" --hidden
1543 1543 @ changeset: 2:7a230b46bf61
1544 1544 | tag: tip
1545 1545 | parent: 0:ea207398892e
1546 1546 | user: test
1547 1547 | date: Thu Jan 01 00:00:00 1970 +0000
1548 1548 | summary: A2
1549 1549 |
1550 1550 | x changeset: 1:471f378eab4c
1551 1551 |/ user: test
1552 1552 | date: Thu Jan 01 00:00:00 1970 +0000
1553 1553 | obsfate: rewritten using amend as 2:7a230b46bf61
1554 1554 | summary: A0
1555 1555 |
1556 1556 o changeset: 0:ea207398892e
1557 1557 user: test
1558 1558 date: Thu Jan 01 00:00:00 1970 +0000
1559 1559 summary: ROOT
1560 1560
1561 1561
1562 1562 Test template with obsmarkers cycle
1563 1563 ===================================
1564 1564
1565 1565 Test setup
1566 1566 ----------
1567 1567
1568 1568 $ hg init $TESTTMP/templates-local-cycle
1569 1569 $ cd $TESTTMP/templates-local-cycle
1570 1570 $ mkcommit ROOT
1571 1571 $ mkcommit A0
1572 1572 $ mkcommit B0
1573 1573 $ hg up -r 0
1574 1574 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1575 1575 $ mkcommit C0
1576 1576 created new head
1577 1577
1578 1578 Create the cycle
1579 1579
1580 1580 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(B0)"`
1581 1581 obsoleted 1 changesets
1582 1582 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(C0)"`
1583 1583 obsoleted 1 changesets
1584 1584 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(A0)"`
1585 1585
1586 1586 Check templates
1587 1587 ---------------
1588 1588
1589 1589 $ hg tlog
1590 1590 @ f897c6137566
1591 1591 |
1592 1592 o ea207398892e
1593 1593
1594 1594
1595 1595 $ hg fatelog
1596 1596 @ f897c6137566
1597 1597 |
1598 1598 o ea207398892e
1599 1599
1600 1600
1601 1601 $ hg up -r "desc(B0)" --hidden
1602 1602 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1603 1603 $ hg tlog
1604 1604 o f897c6137566
1605 1605 | Predecessors: 2:0dec01379d3b
1606 1606 | semi-colon: 2:0dec01379d3b
1607 1607 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1608 1608 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1609 1609 | @ 0dec01379d3b
1610 1610 | | Predecessors: 1:471f378eab4c
1611 1611 | | semi-colon: 1:471f378eab4c
1612 1612 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1613 1613 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1614 1614 | | Successors: 3:f897c6137566; 1:471f378eab4c
1615 1615 | | multi-line: 3:f897c6137566
1616 1616 | | multi-line: 1:471f378eab4c
1617 1617 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
1618 1618 | x 471f378eab4c
1619 1619 |/ Predecessors: 2:0dec01379d3b
1620 1620 | semi-colon: 2:0dec01379d3b
1621 1621 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1622 1622 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1623 1623 | Successors: 2:0dec01379d3b
1624 1624 | multi-line: 2:0dec01379d3b
1625 1625 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
1626 1626 o ea207398892e
1627 1627
1628 1628
1629 1629 $ hg fatelog
1630 1630 o f897c6137566
1631 1631 |
1632 1632 | @ 0dec01379d3b
1633 1633 | | Obsfate: rewritten as 3:f897c6137566 by test (at 1970-01-01 00:00 +0000); rewritten as 1:471f378eab4c by test (at 1970-01-01 00:00 +0000);
1634 1634 | x 471f378eab4c
1635 1635 |/ Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000);
1636 1636 o ea207398892e
1637 1637
1638 1638
1639 1639 $ hg up -r "desc(A0)" --hidden
1640 1640 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1641 1641 $ hg tlog
1642 1642 o f897c6137566
1643 1643 | Predecessors: 1:471f378eab4c
1644 1644 | semi-colon: 1:471f378eab4c
1645 1645 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1646 1646 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1647 1647 | @ 471f378eab4c
1648 1648 |/
1649 1649 o ea207398892e
1650 1650
1651 1651
1652 1652 $ hg fatelog
1653 1653 o f897c6137566
1654 1654 |
1655 1655 | @ 471f378eab4c
1656 1656 |/ Obsfate: pruned;
1657 1657 o ea207398892e
1658 1658
1659 1659
1660 1660 $ hg up -r "desc(ROOT)" --hidden
1661 1661 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1662 1662 $ hg tlog
1663 1663 o f897c6137566
1664 1664 |
1665 1665 @ ea207398892e
1666 1666
1667 1667
1668 1668 $ hg fatelog
1669 1669 o f897c6137566
1670 1670 |
1671 1671 @ ea207398892e
1672 1672
1673 1673
1674 1674 $ hg tlog --hidden
1675 1675 o f897c6137566
1676 1676 | Predecessors: 2:0dec01379d3b
1677 1677 | semi-colon: 2:0dec01379d3b
1678 1678 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1679 1679 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1680 1680 | x 0dec01379d3b
1681 1681 | | Predecessors: 1:471f378eab4c
1682 1682 | | semi-colon: 1:471f378eab4c
1683 1683 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1684 1684 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1685 1685 | | Successors: 3:f897c6137566; 1:471f378eab4c
1686 1686 | | multi-line: 3:f897c6137566
1687 1687 | | multi-line: 1:471f378eab4c
1688 1688 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
1689 1689 | x 471f378eab4c
1690 1690 |/ Predecessors: 2:0dec01379d3b
1691 1691 | semi-colon: 2:0dec01379d3b
1692 1692 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1693 1693 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1694 1694 | Successors: 2:0dec01379d3b
1695 1695 | multi-line: 2:0dec01379d3b
1696 1696 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
1697 1697 @ ea207398892e
1698 1698
1699 1699
1700 1700 Check other fatelog implementations
1701 1701 -----------------------------------
1702 1702
1703 1703 $ hg fatelogkw --hidden -q
1704 1704 o f897c6137566
1705 1705 |
1706 1706 | x 0dec01379d3b
1707 1707 | | Obsfate: rewritten as 3:f897c6137566
1708 1708 | | Obsfate: rewritten as 1:471f378eab4c
1709 1709 | x 471f378eab4c
1710 1710 |/ Obsfate: rewritten as 2:0dec01379d3b
1711 1711 @ ea207398892e
1712 1712
1713 1713 $ hg fatelogkw --hidden
1714 1714 o f897c6137566
1715 1715 |
1716 1716 | x 0dec01379d3b
1717 1717 | | Obsfate: rewritten as 3:f897c6137566
1718 1718 | | Obsfate: rewritten as 1:471f378eab4c
1719 1719 | x 471f378eab4c
1720 1720 |/ Obsfate: rewritten as 2:0dec01379d3b
1721 1721 @ ea207398892e
1722 1722
1723 1723 $ hg fatelogkw --hidden -v
1724 1724 o f897c6137566
1725 1725 |
1726 1726 | x 0dec01379d3b
1727 1727 | | Obsfate: rewritten as 3:f897c6137566 by test (at 1970-01-01 00:00 +0000)
1728 1728 | | Obsfate: rewritten as 1:471f378eab4c by test (at 1970-01-01 00:00 +0000)
1729 1729 | x 471f378eab4c
1730 1730 |/ Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000)
1731 1731 @ ea207398892e
1732 1732
1733 1733 $ hg log -G -T "default" --hidden
1734 1734 o changeset: 3:f897c6137566
1735 1735 | tag: tip
1736 1736 | parent: 0:ea207398892e
1737 1737 | user: test
1738 1738 | date: Thu Jan 01 00:00:00 1970 +0000
1739 1739 | summary: C0
1740 1740 |
1741 1741 | x changeset: 2:0dec01379d3b
1742 1742 | | user: test
1743 1743 | | date: Thu Jan 01 00:00:00 1970 +0000
1744 1744 | | obsfate: rewritten as 3:f897c6137566
1745 1745 | | obsfate: rewritten as 1:471f378eab4c
1746 1746 | | summary: B0
1747 1747 | |
1748 1748 | x changeset: 1:471f378eab4c
1749 1749 |/ user: test
1750 1750 | date: Thu Jan 01 00:00:00 1970 +0000
1751 1751 | obsfate: rewritten as 2:0dec01379d3b
1752 1752 | summary: A0
1753 1753 |
1754 1754 @ changeset: 0:ea207398892e
1755 1755 user: test
1756 1756 date: Thu Jan 01 00:00:00 1970 +0000
1757 1757 summary: ROOT
1758 1758
1759 1759
1760 1760 Test template with split + divergence with cycles
1761 1761 =================================================
1762 1762
1763 1763 $ hg log -G
1764 1764 o changeset: 3:f897c6137566
1765 1765 | tag: tip
1766 1766 | parent: 0:ea207398892e
1767 1767 | user: test
1768 1768 | date: Thu Jan 01 00:00:00 1970 +0000
1769 1769 | summary: C0
1770 1770 |
1771 1771 @ changeset: 0:ea207398892e
1772 1772 user: test
1773 1773 date: Thu Jan 01 00:00:00 1970 +0000
1774 1774 summary: ROOT
1775 1775
1776 1776 $ hg up
1777 1777 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1778 1778
1779 1779 Create a commit with three files
1780 1780 $ touch A B C
1781 1781 $ hg commit -A -m "Add A,B,C" A B C
1782 1782
1783 1783 Split it
1784 1784 $ hg up 3
1785 1785 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
1786 1786 $ touch A
1787 1787 $ hg commit -A -m "Add A,B,C" A
1788 1788 created new head
1789 1789
1790 1790 $ touch B
1791 1791 $ hg commit -A -m "Add A,B,C" B
1792 1792
1793 1793 $ touch C
1794 1794 $ hg commit -A -m "Add A,B,C" C
1795 1795
1796 1796 $ hg log -G
1797 1797 @ changeset: 7:ba2ed02b0c9a
1798 1798 | tag: tip
1799 1799 | user: test
1800 1800 | date: Thu Jan 01 00:00:00 1970 +0000
1801 1801 | summary: Add A,B,C
1802 1802 |
1803 1803 o changeset: 6:4a004186e638
1804 1804 | user: test
1805 1805 | date: Thu Jan 01 00:00:00 1970 +0000
1806 1806 | summary: Add A,B,C
1807 1807 |
1808 1808 o changeset: 5:dd800401bd8c
1809 1809 | parent: 3:f897c6137566
1810 1810 | user: test
1811 1811 | date: Thu Jan 01 00:00:00 1970 +0000
1812 1812 | summary: Add A,B,C
1813 1813 |
1814 1814 | o changeset: 4:9bd10a0775e4
1815 1815 |/ user: test
1816 1816 | date: Thu Jan 01 00:00:00 1970 +0000
1817 1817 | summary: Add A,B,C
1818 1818 |
1819 1819 o changeset: 3:f897c6137566
1820 1820 | parent: 0:ea207398892e
1821 1821 | user: test
1822 1822 | date: Thu Jan 01 00:00:00 1970 +0000
1823 1823 | summary: C0
1824 1824 |
1825 1825 o changeset: 0:ea207398892e
1826 1826 user: test
1827 1827 date: Thu Jan 01 00:00:00 1970 +0000
1828 1828 summary: ROOT
1829 1829
1830 1830 $ hg debugobsolete `getid "4"` `getid "5"` `getid "6"` `getid "7"`
1831 1831 obsoleted 1 changesets
1832 1832 $ hg log -G
1833 1833 @ changeset: 7:ba2ed02b0c9a
1834 1834 | tag: tip
1835 1835 | user: test
1836 1836 | date: Thu Jan 01 00:00:00 1970 +0000
1837 1837 | summary: Add A,B,C
1838 1838 |
1839 1839 o changeset: 6:4a004186e638
1840 1840 | user: test
1841 1841 | date: Thu Jan 01 00:00:00 1970 +0000
1842 1842 | summary: Add A,B,C
1843 1843 |
1844 1844 o changeset: 5:dd800401bd8c
1845 1845 | parent: 3:f897c6137566
1846 1846 | user: test
1847 1847 | date: Thu Jan 01 00:00:00 1970 +0000
1848 1848 | summary: Add A,B,C
1849 1849 |
1850 1850 o changeset: 3:f897c6137566
1851 1851 | parent: 0:ea207398892e
1852 1852 | user: test
1853 1853 | date: Thu Jan 01 00:00:00 1970 +0000
1854 1854 | summary: C0
1855 1855 |
1856 1856 o changeset: 0:ea207398892e
1857 1857 user: test
1858 1858 date: Thu Jan 01 00:00:00 1970 +0000
1859 1859 summary: ROOT
1860 1860
1861 1861 Diverge one of the splitted commit
1862 1862
1863 1863 $ hg up 6
1864 1864 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1865 1865 $ hg commit --amend -m "Add only B"
1866 1866
1867 1867 $ hg up 6 --hidden
1868 1868 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1869 1869 $ hg commit --amend -m "Add B only"
1870 1870
1871 1871 $ hg log -G
1872 1872 @ changeset: 9:0b997eb7ceee
1873 1873 | tag: tip
1874 1874 | parent: 5:dd800401bd8c
1875 1875 | user: test
1876 1876 | date: Thu Jan 01 00:00:00 1970 +0000
1877 1877 | instability: content-divergent
1878 1878 | summary: Add B only
1879 1879 |
1880 1880 | o changeset: 8:b18bc8331526
1881 1881 |/ parent: 5:dd800401bd8c
1882 1882 | user: test
1883 1883 | date: Thu Jan 01 00:00:00 1970 +0000
1884 1884 | instability: content-divergent
1885 1885 | summary: Add only B
1886 1886 |
1887 1887 | o changeset: 7:ba2ed02b0c9a
1888 1888 | | user: test
1889 1889 | | date: Thu Jan 01 00:00:00 1970 +0000
1890 1890 | | instability: orphan, content-divergent
1891 1891 | | summary: Add A,B,C
1892 1892 | |
1893 1893 | x changeset: 6:4a004186e638
1894 1894 |/ user: test
1895 1895 | date: Thu Jan 01 00:00:00 1970 +0000
1896 | obsfate: rewritten using amend as 8:b18bc8331526
1897 | obsfate: rewritten using amend as 9:0b997eb7ceee
1896 | obsolete: rewritten using amend as 8:b18bc8331526
1897 | obsolete: rewritten using amend as 9:0b997eb7ceee
1898 1898 | summary: Add A,B,C
1899 1899 |
1900 1900 o changeset: 5:dd800401bd8c
1901 1901 | parent: 3:f897c6137566
1902 1902 | user: test
1903 1903 | date: Thu Jan 01 00:00:00 1970 +0000
1904 1904 | instability: content-divergent
1905 1905 | summary: Add A,B,C
1906 1906 |
1907 1907 o changeset: 3:f897c6137566
1908 1908 | parent: 0:ea207398892e
1909 1909 | user: test
1910 1910 | date: Thu Jan 01 00:00:00 1970 +0000
1911 1911 | summary: C0
1912 1912 |
1913 1913 o changeset: 0:ea207398892e
1914 1914 user: test
1915 1915 date: Thu Jan 01 00:00:00 1970 +0000
1916 1916 summary: ROOT
1917 1917
1918 1918
1919 1919 Check templates
1920 1920 ---------------
1921 1921
1922 1922 $ hg tlog
1923 1923 @ 0b997eb7ceee
1924 1924 | Predecessors: 6:4a004186e638
1925 1925 | semi-colon: 6:4a004186e638
1926 1926 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1927 1927 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1928 1928 | o b18bc8331526
1929 1929 |/ Predecessors: 6:4a004186e638
1930 1930 | semi-colon: 6:4a004186e638
1931 1931 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1932 1932 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1933 1933 | o ba2ed02b0c9a
1934 1934 | |
1935 1935 | x 4a004186e638
1936 1936 |/ Successors: 8:b18bc8331526; 9:0b997eb7ceee
1937 1937 | multi-line: 8:b18bc8331526
1938 1938 | multi-line: 9:0b997eb7ceee
1939 1939 | json: [["b18bc8331526a22cbb1801022bd1555bf291c48b"], ["0b997eb7ceeee06200a02f8aab185979092d514e"]]
1940 1940 o dd800401bd8c
1941 1941 |
1942 1942 o f897c6137566
1943 1943 |
1944 1944 o ea207398892e
1945 1945
1946 1946 $ hg fatelog
1947 1947 @ 0b997eb7ceee
1948 1948 |
1949 1949 | o b18bc8331526
1950 1950 |/
1951 1951 | o ba2ed02b0c9a
1952 1952 | |
1953 1953 | x 4a004186e638
1954 1954 |/ Obsfate: rewritten using amend as 8:b18bc8331526 by test (at 1970-01-01 00:00 +0000); rewritten using amend as 9:0b997eb7ceee by test (at 1970-01-01 00:00 +0000);
1955 1955 o dd800401bd8c
1956 1956 |
1957 1957 o f897c6137566
1958 1958 |
1959 1959 o ea207398892e
1960 1960
1961 1961 $ hg tlog --hidden
1962 1962 @ 0b997eb7ceee
1963 1963 | Predecessors: 6:4a004186e638
1964 1964 | semi-colon: 6:4a004186e638
1965 1965 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1966 1966 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1967 1967 | o b18bc8331526
1968 1968 |/ Predecessors: 6:4a004186e638
1969 1969 | semi-colon: 6:4a004186e638
1970 1970 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1971 1971 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1972 1972 | o ba2ed02b0c9a
1973 1973 | | Predecessors: 4:9bd10a0775e4
1974 1974 | | semi-colon: 4:9bd10a0775e4
1975 1975 | | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1976 1976 | | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1977 1977 | x 4a004186e638
1978 1978 |/ Predecessors: 4:9bd10a0775e4
1979 1979 | semi-colon: 4:9bd10a0775e4
1980 1980 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1981 1981 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1982 1982 | Successors: 8:b18bc8331526; 9:0b997eb7ceee
1983 1983 | multi-line: 8:b18bc8331526
1984 1984 | multi-line: 9:0b997eb7ceee
1985 1985 | json: [["b18bc8331526a22cbb1801022bd1555bf291c48b"], ["0b997eb7ceeee06200a02f8aab185979092d514e"]]
1986 1986 o dd800401bd8c
1987 1987 | Predecessors: 4:9bd10a0775e4
1988 1988 | semi-colon: 4:9bd10a0775e4
1989 1989 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1990 1990 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1991 1991 | x 9bd10a0775e4
1992 1992 |/ Successors: 5:dd800401bd8c 6:4a004186e638 7:ba2ed02b0c9a
1993 1993 | multi-line: 5:dd800401bd8c 6:4a004186e638 7:ba2ed02b0c9a
1994 1994 | json: [["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"]]
1995 1995 o f897c6137566
1996 1996 | Predecessors: 2:0dec01379d3b
1997 1997 | semi-colon: 2:0dec01379d3b
1998 1998 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1999 1999 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
2000 2000 | x 0dec01379d3b
2001 2001 | | Predecessors: 1:471f378eab4c
2002 2002 | | semi-colon: 1:471f378eab4c
2003 2003 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
2004 2004 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
2005 2005 | | Successors: 3:f897c6137566; 1:471f378eab4c
2006 2006 | | multi-line: 3:f897c6137566
2007 2007 | | multi-line: 1:471f378eab4c
2008 2008 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
2009 2009 | x 471f378eab4c
2010 2010 |/ Predecessors: 2:0dec01379d3b
2011 2011 | semi-colon: 2:0dec01379d3b
2012 2012 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
2013 2013 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
2014 2014 | Successors: 2:0dec01379d3b
2015 2015 | multi-line: 2:0dec01379d3b
2016 2016 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
2017 2017 o ea207398892e
2018 2018
2019 2019 $ hg fatelog --hidden
2020 2020 @ 0b997eb7ceee
2021 2021 |
2022 2022 | o b18bc8331526
2023 2023 |/
2024 2024 | o ba2ed02b0c9a
2025 2025 | |
2026 2026 | x 4a004186e638
2027 2027 |/ Obsfate: rewritten using amend as 8:b18bc8331526 by test (at 1970-01-01 00:00 +0000); rewritten using amend as 9:0b997eb7ceee by test (at 1970-01-01 00:00 +0000);
2028 2028 o dd800401bd8c
2029 2029 |
2030 2030 | x 9bd10a0775e4
2031 2031 |/ Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a by test (at 1970-01-01 00:00 +0000);
2032 2032 o f897c6137566
2033 2033 |
2034 2034 | x 0dec01379d3b
2035 2035 | | Obsfate: rewritten as 3:f897c6137566 by test (at 1970-01-01 00:00 +0000); rewritten as 1:471f378eab4c by test (at 1970-01-01 00:00 +0000);
2036 2036 | x 471f378eab4c
2037 2037 |/ Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000);
2038 2038 o ea207398892e
2039 2039
2040 2040 $ hg fatelogjson --hidden
2041 2041 @ 0b997eb7ceee
2042 2042 |
2043 2043 | o b18bc8331526
2044 2044 |/
2045 2045 | o ba2ed02b0c9a
2046 2046 | |
2047 2047 | x 4a004186e638
2048 2048 |/ Obsfate: [{"markers": [["4a004186e63889f20cb16434fcbd72220bd1eace", ["b18bc8331526a22cbb1801022bd1555bf291c48b"], 0, [["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["b18bc8331526a22cbb1801022bd1555bf291c48b"]}, {"markers": [["4a004186e63889f20cb16434fcbd72220bd1eace", ["0b997eb7ceeee06200a02f8aab185979092d514e"], 0, [["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["0b997eb7ceeee06200a02f8aab185979092d514e"]}]
2049 2049 o dd800401bd8c
2050 2050 |
2051 2051 | x 9bd10a0775e4
2052 2052 |/ Obsfate: [{"markers": [["9bd10a0775e478708cada5f176ec6de654359ce7", ["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"]}]
2053 2053 o f897c6137566
2054 2054 |
2055 2055 | x 0dec01379d3b
2056 2056 | | Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["f897c6137566320b081514b4c7227ecc3d384b39"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["f897c6137566320b081514b4c7227ecc3d384b39"]}, {"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["471f378eab4c5e25f6c77f785b27c936efb22874"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["471f378eab4c5e25f6c77f785b27c936efb22874"]}]
2057 2057 | x 471f378eab4c
2058 2058 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]}]
2059 2059 o ea207398892e
2060 2060
2061 2061 $ hg up --hidden 4
2062 2062 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2063 2063 $ hg rebase -r 7 -d 8 --config extensions.rebase=
2064 2064 rebasing 7:ba2ed02b0c9a "Add A,B,C"
2065 2065 $ hg tlog
2066 2066 o eceed8f98ffc
2067 2067 | Predecessors: 4:9bd10a0775e4
2068 2068 | semi-colon: 4:9bd10a0775e4
2069 2069 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
2070 2070 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
2071 2071 | o 0b997eb7ceee
2072 2072 | | Predecessors: 4:9bd10a0775e4
2073 2073 | | semi-colon: 4:9bd10a0775e4
2074 2074 | | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
2075 2075 | | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
2076 2076 o | b18bc8331526
2077 2077 |/ Predecessors: 4:9bd10a0775e4
2078 2078 | semi-colon: 4:9bd10a0775e4
2079 2079 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
2080 2080 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
2081 2081 o dd800401bd8c
2082 2082 | Predecessors: 4:9bd10a0775e4
2083 2083 | semi-colon: 4:9bd10a0775e4
2084 2084 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
2085 2085 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
2086 2086 | @ 9bd10a0775e4
2087 2087 |/ Successors: 5:dd800401bd8c 9:0b997eb7ceee 10:eceed8f98ffc; 5:dd800401bd8c 8:b18bc8331526 10:eceed8f98ffc
2088 2088 | multi-line: 5:dd800401bd8c 9:0b997eb7ceee 10:eceed8f98ffc
2089 2089 | multi-line: 5:dd800401bd8c 8:b18bc8331526 10:eceed8f98ffc
2090 2090 | json: [["dd800401bd8c79d815329277739e433e883f784e", "0b997eb7ceeee06200a02f8aab185979092d514e", "eceed8f98ffc4186032e29a6542ab98888ebf68d"], ["dd800401bd8c79d815329277739e433e883f784e", "b18bc8331526a22cbb1801022bd1555bf291c48b", "eceed8f98ffc4186032e29a6542ab98888ebf68d"]]
2091 2091 o f897c6137566
2092 2092 |
2093 2093 o ea207398892e
2094 2094
2095 2095
2096 2096 $ hg fatelog
2097 2097 o eceed8f98ffc
2098 2098 |
2099 2099 | o 0b997eb7ceee
2100 2100 | |
2101 2101 o | b18bc8331526
2102 2102 |/
2103 2103 o dd800401bd8c
2104 2104 |
2105 2105 | @ 9bd10a0775e4
2106 2106 |/ Obsfate: split using amend, rebase as 5:dd800401bd8c, 9:0b997eb7ceee, 10:eceed8f98ffc by test (at 1970-01-01 00:00 +0000); split using amend, rebase as 5:dd800401bd8c, 8:b18bc8331526, 10:eceed8f98ffc by test (at 1970-01-01 00:00 +0000);
2107 2107 o f897c6137566
2108 2108 |
2109 2109 o ea207398892e
2110 2110
2111 2111 Check other fatelog implementations
2112 2112 -----------------------------------
2113 2113
2114 2114 $ hg fatelogkw --hidden -q
2115 2115 o eceed8f98ffc
2116 2116 |
2117 2117 | o 0b997eb7ceee
2118 2118 | |
2119 2119 o | b18bc8331526
2120 2120 |/
2121 2121 | x ba2ed02b0c9a
2122 2122 | | Obsfate: rewritten using rebase as 10:eceed8f98ffc
2123 2123 | x 4a004186e638
2124 2124 |/ Obsfate: rewritten using amend as 8:b18bc8331526
2125 2125 | Obsfate: rewritten using amend as 9:0b997eb7ceee
2126 2126 o dd800401bd8c
2127 2127 |
2128 2128 | @ 9bd10a0775e4
2129 2129 |/ Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a
2130 2130 o f897c6137566
2131 2131 |
2132 2132 | x 0dec01379d3b
2133 2133 | | Obsfate: rewritten as 3:f897c6137566
2134 2134 | | Obsfate: rewritten as 1:471f378eab4c
2135 2135 | x 471f378eab4c
2136 2136 |/ Obsfate: rewritten as 2:0dec01379d3b
2137 2137 o ea207398892e
2138 2138
2139 2139 $ hg fatelogkw --hidden
2140 2140 o eceed8f98ffc
2141 2141 |
2142 2142 | o 0b997eb7ceee
2143 2143 | |
2144 2144 o | b18bc8331526
2145 2145 |/
2146 2146 | x ba2ed02b0c9a
2147 2147 | | Obsfate: rewritten using rebase as 10:eceed8f98ffc
2148 2148 | x 4a004186e638
2149 2149 |/ Obsfate: rewritten using amend as 8:b18bc8331526
2150 2150 | Obsfate: rewritten using amend as 9:0b997eb7ceee
2151 2151 o dd800401bd8c
2152 2152 |
2153 2153 | @ 9bd10a0775e4
2154 2154 |/ Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a
2155 2155 o f897c6137566
2156 2156 |
2157 2157 | x 0dec01379d3b
2158 2158 | | Obsfate: rewritten as 3:f897c6137566
2159 2159 | | Obsfate: rewritten as 1:471f378eab4c
2160 2160 | x 471f378eab4c
2161 2161 |/ Obsfate: rewritten as 2:0dec01379d3b
2162 2162 o ea207398892e
2163 2163
2164 2164 $ hg fatelogkw --hidden -v
2165 2165 o eceed8f98ffc
2166 2166 |
2167 2167 | o 0b997eb7ceee
2168 2168 | |
2169 2169 o | b18bc8331526
2170 2170 |/
2171 2171 | x ba2ed02b0c9a
2172 2172 | | Obsfate: rewritten using rebase as 10:eceed8f98ffc by test (at 1970-01-01 00:00 +0000)
2173 2173 | x 4a004186e638
2174 2174 |/ Obsfate: rewritten using amend as 8:b18bc8331526 by test (at 1970-01-01 00:00 +0000)
2175 2175 | Obsfate: rewritten using amend as 9:0b997eb7ceee by test (at 1970-01-01 00:00 +0000)
2176 2176 o dd800401bd8c
2177 2177 |
2178 2178 | @ 9bd10a0775e4
2179 2179 |/ Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a by test (at 1970-01-01 00:00 +0000)
2180 2180 o f897c6137566
2181 2181 |
2182 2182 | x 0dec01379d3b
2183 2183 | | Obsfate: rewritten as 3:f897c6137566 by test (at 1970-01-01 00:00 +0000)
2184 2184 | | Obsfate: rewritten as 1:471f378eab4c by test (at 1970-01-01 00:00 +0000)
2185 2185 | x 471f378eab4c
2186 2186 |/ Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000)
2187 2187 o ea207398892e
2188 2188
2189 2189 $ hg log -G -T "default" --hidden
2190 2190 o changeset: 10:eceed8f98ffc
2191 2191 | tag: tip
2192 2192 | parent: 8:b18bc8331526
2193 2193 | user: test
2194 2194 | date: Thu Jan 01 00:00:00 1970 +0000
2195 2195 | instability: content-divergent
2196 2196 | summary: Add A,B,C
2197 2197 |
2198 2198 | o changeset: 9:0b997eb7ceee
2199 2199 | | parent: 5:dd800401bd8c
2200 2200 | | user: test
2201 2201 | | date: Thu Jan 01 00:00:00 1970 +0000
2202 2202 | | instability: content-divergent
2203 2203 | | summary: Add B only
2204 2204 | |
2205 2205 o | changeset: 8:b18bc8331526
2206 2206 |/ parent: 5:dd800401bd8c
2207 2207 | user: test
2208 2208 | date: Thu Jan 01 00:00:00 1970 +0000
2209 2209 | instability: content-divergent
2210 2210 | summary: Add only B
2211 2211 |
2212 2212 | x changeset: 7:ba2ed02b0c9a
2213 2213 | | user: test
2214 2214 | | date: Thu Jan 01 00:00:00 1970 +0000
2215 2215 | | obsfate: rewritten using rebase as 10:eceed8f98ffc
2216 2216 | | summary: Add A,B,C
2217 2217 | |
2218 2218 | x changeset: 6:4a004186e638
2219 2219 |/ user: test
2220 2220 | date: Thu Jan 01 00:00:00 1970 +0000
2221 2221 | obsfate: rewritten using amend as 8:b18bc8331526
2222 2222 | obsfate: rewritten using amend as 9:0b997eb7ceee
2223 2223 | summary: Add A,B,C
2224 2224 |
2225 2225 o changeset: 5:dd800401bd8c
2226 2226 | parent: 3:f897c6137566
2227 2227 | user: test
2228 2228 | date: Thu Jan 01 00:00:00 1970 +0000
2229 2229 | instability: content-divergent
2230 2230 | summary: Add A,B,C
2231 2231 |
2232 2232 | @ changeset: 4:9bd10a0775e4
2233 2233 |/ user: test
2234 2234 | date: Thu Jan 01 00:00:00 1970 +0000
2235 2235 | obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a
2236 2236 | summary: Add A,B,C
2237 2237 |
2238 2238 o changeset: 3:f897c6137566
2239 2239 | parent: 0:ea207398892e
2240 2240 | user: test
2241 2241 | date: Thu Jan 01 00:00:00 1970 +0000
2242 2242 | summary: C0
2243 2243 |
2244 2244 | x changeset: 2:0dec01379d3b
2245 2245 | | user: test
2246 2246 | | date: Thu Jan 01 00:00:00 1970 +0000
2247 2247 | | obsfate: rewritten as 3:f897c6137566
2248 2248 | | obsfate: rewritten as 1:471f378eab4c
2249 2249 | | summary: B0
2250 2250 | |
2251 2251 | x changeset: 1:471f378eab4c
2252 2252 |/ user: test
2253 2253 | date: Thu Jan 01 00:00:00 1970 +0000
2254 2254 | obsfate: rewritten as 2:0dec01379d3b
2255 2255 | summary: A0
2256 2256 |
2257 2257 o changeset: 0:ea207398892e
2258 2258 user: test
2259 2259 date: Thu Jan 01 00:00:00 1970 +0000
2260 2260 summary: ROOT
2261 2261
2262 2262
2263 2263 Test templates with pruned commits
2264 2264 ==================================
2265 2265
2266 2266 Test setup
2267 2267 ----------
2268 2268
2269 2269 $ hg init $TESTTMP/templates-local-prune
2270 2270 $ cd $TESTTMP/templates-local-prune
2271 2271 $ mkcommit ROOT
2272 2272 $ mkcommit A0
2273 2273 $ hg debugobsolete --record-parent `getid "."`
2274 2274 obsoleted 1 changesets
2275 2275
2276 2276 Check output
2277 2277 ------------
2278 2278
2279 2279 $ hg up "desc(A0)" --hidden
2280 2280 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2281 2281 $ hg tlog
2282 2282 @ 471f378eab4c
2283 2283 |
2284 2284 o ea207398892e
2285 2285
2286 2286 $ hg fatelog
2287 2287 @ 471f378eab4c
2288 2288 | Obsfate: pruned by test (at 1970-01-01 00:00 +0000);
2289 2289 o ea207398892e
2290 2290
2291 2291 Test templates with multiple pruned commits
2292 2292 ===========================================
2293 2293
2294 2294 Test setup
2295 2295 ----------
2296 2296
2297 2297 $ hg init $TESTTMP/multiple-local-prune
2298 2298 $ cd $TESTTMP/multiple-local-prune
2299 2299 $ mkcommit ROOT
2300 2300 $ mkcommit A0
2301 2301 $ hg commit --amend -m "A1"
2302 2302 $ hg debugobsolete --record-parent `getid "."`
2303 2303 obsoleted 1 changesets
2304 2304
2305 2305 $ hg up -r "desc(A0)" --hidden
2306 2306 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2307 2307 $ hg commit --amend -m "A2"
2308 2308 $ hg debugobsolete --record-parent `getid "."`
2309 2309 obsoleted 1 changesets
2310 2310
2311 2311 Check output
2312 2312 ------------
2313 2313
2314 2314 $ hg up "desc(A0)" --hidden
2315 2315 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2316 2316 $ hg tlog
2317 2317 @ 471f378eab4c
2318 2318 |
2319 2319 o ea207398892e
2320 2320
2321 2321 # todo: the obsfate output is not ideal
2322 2322 $ hg fatelog
2323 2323 @ 471f378eab4c
2324 2324 | Obsfate: pruned;
2325 2325 o ea207398892e
2326 2326
2327 2327 $ hg fatelog --hidden
2328 2328 x 65b757b745b9
2329 2329 | Obsfate: pruned by test (at 1970-01-01 00:00 +0000);
2330 2330 | x fdf9bde5129a
2331 2331 |/ Obsfate: pruned by test (at 1970-01-01 00:00 +0000);
2332 2332 | @ 471f378eab4c
2333 2333 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000); rewritten using amend as 3:65b757b745b9 by test (at 1970-01-01 00:00 +0000);
2334 2334 o ea207398892e
2335 2335
2336 2336 Check other fatelog implementations
2337 2337 -----------------------------------
2338 2338
2339 2339 $ hg fatelogkw --hidden -q
2340 2340 x 65b757b745b9
2341 2341 | Obsfate: pruned
2342 2342 | x fdf9bde5129a
2343 2343 |/ Obsfate: pruned
2344 2344 | @ 471f378eab4c
2345 2345 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a
2346 2346 | Obsfate: rewritten using amend as 3:65b757b745b9
2347 2347 o ea207398892e
2348 2348
2349 2349 $ hg fatelogkw --hidden
2350 2350 x 65b757b745b9
2351 2351 | Obsfate: pruned
2352 2352 | x fdf9bde5129a
2353 2353 |/ Obsfate: pruned
2354 2354 | @ 471f378eab4c
2355 2355 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a
2356 2356 | Obsfate: rewritten using amend as 3:65b757b745b9
2357 2357 o ea207398892e
2358 2358
2359 2359 $ hg fatelogkw --hidden -v
2360 2360 x 65b757b745b9
2361 2361 | Obsfate: pruned by test (at 1970-01-01 00:00 +0000)
2362 2362 | x fdf9bde5129a
2363 2363 |/ Obsfate: pruned by test (at 1970-01-01 00:00 +0000)
2364 2364 | @ 471f378eab4c
2365 2365 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000)
2366 2366 | Obsfate: rewritten using amend as 3:65b757b745b9 by test (at 1970-01-01 00:00 +0000)
2367 2367 o ea207398892e
2368 2368
2369 2369
2370 2370 $ hg log -G -T "default" --hidden
2371 2371 x changeset: 3:65b757b745b9
2372 2372 | tag: tip
2373 2373 | parent: 0:ea207398892e
2374 2374 | user: test
2375 2375 | date: Thu Jan 01 00:00:00 1970 +0000
2376 2376 | obsfate: pruned
2377 2377 | summary: A2
2378 2378 |
2379 2379 | x changeset: 2:fdf9bde5129a
2380 2380 |/ parent: 0:ea207398892e
2381 2381 | user: test
2382 2382 | date: Thu Jan 01 00:00:00 1970 +0000
2383 2383 | obsfate: pruned
2384 2384 | summary: A1
2385 2385 |
2386 2386 | @ changeset: 1:471f378eab4c
2387 2387 |/ user: test
2388 2388 | date: Thu Jan 01 00:00:00 1970 +0000
2389 2389 | obsfate: rewritten using amend as 2:fdf9bde5129a
2390 2390 | obsfate: rewritten using amend as 3:65b757b745b9
2391 2391 | summary: A0
2392 2392 |
2393 2393 o changeset: 0:ea207398892e
2394 2394 user: test
2395 2395 date: Thu Jan 01 00:00:00 1970 +0000
2396 2396 summary: ROOT
2397 2397
2398 2398
2399 2399 Test templates with splitted and pruned commit
2400 2400 ==============================================
2401 2401
2402 2402 $ hg init $TESTTMP/templates-local-split-prune
2403 2403 $ cd $TESTTMP/templates-local-split-prune
2404 2404 $ mkcommit ROOT
2405 2405 $ echo 42 >> a
2406 2406 $ echo 43 >> b
2407 2407 $ hg commit -A -m "A0"
2408 2408 adding a
2409 2409 adding b
2410 2410 $ hg log --hidden -G
2411 2411 @ changeset: 1:471597cad322
2412 2412 | tag: tip
2413 2413 | user: test
2414 2414 | date: Thu Jan 01 00:00:00 1970 +0000
2415 2415 | summary: A0
2416 2416 |
2417 2417 o changeset: 0:ea207398892e
2418 2418 user: test
2419 2419 date: Thu Jan 01 00:00:00 1970 +0000
2420 2420 summary: ROOT
2421 2421
2422 2422 # Simulate split
2423 2423 $ hg up -r "desc(ROOT)"
2424 2424 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
2425 2425 $ echo 42 >> a
2426 2426 $ hg commit -A -m "A1"
2427 2427 adding a
2428 2428 created new head
2429 2429 $ echo 43 >> b
2430 2430 $ hg commit -A -m "A2"
2431 2431 adding b
2432 2432 $ hg debugobsolete `getid "1"` `getid "2"` `getid "3"`
2433 2433 obsoleted 1 changesets
2434 2434
2435 2435 # Simulate prune
2436 2436 $ hg debugobsolete --record-parent `getid "."`
2437 2437 obsoleted 1 changesets
2438 2438
2439 2439 $ hg log --hidden -G
2440 2440 @ changeset: 3:0d0ef4bdf70e
2441 2441 | tag: tip
2442 2442 | user: test
2443 2443 | date: Thu Jan 01 00:00:00 1970 +0000
2444 | obsfate: pruned
2444 | obsolete: pruned
2445 2445 | summary: A2
2446 2446 |
2447 2447 o changeset: 2:617adc3a144c
2448 2448 | parent: 0:ea207398892e
2449 2449 | user: test
2450 2450 | date: Thu Jan 01 00:00:00 1970 +0000
2451 2451 | summary: A1
2452 2452 |
2453 2453 | x changeset: 1:471597cad322
2454 2454 |/ user: test
2455 2455 | date: Thu Jan 01 00:00:00 1970 +0000
2456 | obsfate: split as 2:617adc3a144c, 3:0d0ef4bdf70e
2456 | obsolete: split as 2:617adc3a144c, 3:0d0ef4bdf70e
2457 2457 | summary: A0
2458 2458 |
2459 2459 o changeset: 0:ea207398892e
2460 2460 user: test
2461 2461 date: Thu Jan 01 00:00:00 1970 +0000
2462 2462 summary: ROOT
2463 2463
2464 2464 Check templates
2465 2465 ---------------
2466 2466
2467 2467 $ hg up 'desc("A0")' --hidden
2468 2468 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2469 2469
2470 2470 # todo: the obsfate output is not ideal
2471 2471 $ hg fatelog
2472 2472 o 617adc3a144c
2473 2473 |
2474 2474 | @ 471597cad322
2475 2475 |/ Obsfate: pruned;
2476 2476 o ea207398892e
2477 2477
2478 2478 $ hg up -r 'desc("A2")' --hidden
2479 2479 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2480 2480
2481 2481 $ hg fatelog --hidden
2482 2482 @ 0d0ef4bdf70e
2483 2483 | Obsfate: pruned by test (at 1970-01-01 00:00 +0000);
2484 2484 o 617adc3a144c
2485 2485 |
2486 2486 | x 471597cad322
2487 2487 |/ Obsfate: split as 2:617adc3a144c, 3:0d0ef4bdf70e by test (at 1970-01-01 00:00 +0000);
2488 2488 o ea207398892e
2489 2489
2490 2490
2491 2491 Check other fatelog implementations
2492 2492 -----------------------------------
2493 2493
2494 2494 $ hg fatelogkw --hidden -q
2495 2495 @ 0d0ef4bdf70e
2496 2496 | Obsfate: pruned
2497 2497 o 617adc3a144c
2498 2498 |
2499 2499 | x 471597cad322
2500 2500 |/ Obsfate: split as 2:617adc3a144c, 3:0d0ef4bdf70e
2501 2501 o ea207398892e
2502 2502
2503 2503 $ hg fatelogkw --hidden
2504 2504 @ 0d0ef4bdf70e
2505 2505 | Obsfate: pruned
2506 2506 o 617adc3a144c
2507 2507 |
2508 2508 | x 471597cad322
2509 2509 |/ Obsfate: split as 2:617adc3a144c, 3:0d0ef4bdf70e
2510 2510 o ea207398892e
2511 2511
2512 2512 $ hg fatelogkw --hidden -v
2513 2513 @ 0d0ef4bdf70e
2514 2514 | Obsfate: pruned by test (at 1970-01-01 00:00 +0000)
2515 2515 o 617adc3a144c
2516 2516 |
2517 2517 | x 471597cad322
2518 2518 |/ Obsfate: split as 2:617adc3a144c, 3:0d0ef4bdf70e by test (at 1970-01-01 00:00 +0000)
2519 2519 o ea207398892e
2520 2520
2521 2521 $ hg log -G -T "default" --hidden
2522 2522 @ changeset: 3:0d0ef4bdf70e
2523 2523 | tag: tip
2524 2524 | user: test
2525 2525 | date: Thu Jan 01 00:00:00 1970 +0000
2526 2526 | obsfate: pruned
2527 2527 | summary: A2
2528 2528 |
2529 2529 o changeset: 2:617adc3a144c
2530 2530 | parent: 0:ea207398892e
2531 2531 | user: test
2532 2532 | date: Thu Jan 01 00:00:00 1970 +0000
2533 2533 | summary: A1
2534 2534 |
2535 2535 | x changeset: 1:471597cad322
2536 2536 |/ user: test
2537 2537 | date: Thu Jan 01 00:00:00 1970 +0000
2538 2538 | obsfate: split as 2:617adc3a144c, 3:0d0ef4bdf70e
2539 2539 | summary: A0
2540 2540 |
2541 2541 o changeset: 0:ea207398892e
2542 2542 user: test
2543 2543 date: Thu Jan 01 00:00:00 1970 +0000
2544 2544 summary: ROOT
2545 2545
@@ -1,1512 +1,1512 b''
1 1 $ cat >> $HGRCPATH << EOF
2 2 > [phases]
3 3 > # public changeset are not obsolete
4 4 > publish=false
5 5 > [ui]
6 6 > logtemplate="{rev}:{node|short} ({phase}{if(obsolete, ' *{obsolete}*')}{if(instabilities, ' {instabilities}')}) [{tags} {bookmarks}] {desc|firstline}{if(obsfate, " [{join(obsfate, "; ")}]")}\n"
7 7 > EOF
8 8 $ mkcommit() {
9 9 > echo "$1" > "$1"
10 10 > hg add "$1"
11 11 > hg ci -m "add $1"
12 12 > }
13 13 $ getid() {
14 14 > hg log -T "{node}\n" --hidden -r "desc('$1')"
15 15 > }
16 16
17 17 $ cat > debugkeys.py <<EOF
18 18 > def reposetup(ui, repo):
19 19 > class debugkeysrepo(repo.__class__):
20 20 > def listkeys(self, namespace):
21 21 > ui.write('listkeys %s\n' % (namespace,))
22 22 > return super(debugkeysrepo, self).listkeys(namespace)
23 23 >
24 24 > if repo.local():
25 25 > repo.__class__ = debugkeysrepo
26 26 > EOF
27 27
28 28 $ hg init tmpa
29 29 $ cd tmpa
30 30 $ mkcommit kill_me
31 31
32 32 Checking that the feature is properly disabled
33 33
34 34 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
35 35 abort: creating obsolete markers is not enabled on this repo
36 36 [255]
37 37
38 38 Enabling it
39 39
40 40 $ cat >> $HGRCPATH << EOF
41 41 > [experimental]
42 42 > evolution=exchange
43 43 > evolution.createmarkers=True
44 44 > EOF
45 45
46 46 Killing a single changeset without replacement
47 47
48 48 $ hg debugobsolete 0
49 49 abort: changeset references must be full hexadecimal node identifiers
50 50 [255]
51 51 $ hg debugobsolete '00'
52 52 abort: changeset references must be full hexadecimal node identifiers
53 53 [255]
54 54 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
55 55 obsoleted 1 changesets
56 56 $ hg debugobsolete
57 57 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'babar'}
58 58
59 59 (test that mercurial is not confused)
60 60
61 61 $ hg up null --quiet # having 0 as parent prevents it to be hidden
62 62 $ hg tip
63 63 -1:000000000000 (public) [tip ]
64 64 $ hg up --hidden tip --quiet
65 65
66 66 Killing a single changeset with itself should fail
67 67 (simple local safeguard)
68 68
69 69 $ hg debugobsolete `getid kill_me` `getid kill_me`
70 70 abort: bad obsmarker input: in-marker cycle with 97b7c2d76b1845ed3eb988cd612611e72406cef0
71 71 [255]
72 72
73 73 $ cd ..
74 74
75 75 Killing a single changeset with replacement
76 76 (and testing the format option)
77 77
78 78 $ hg init tmpb
79 79 $ cd tmpb
80 80 $ mkcommit a
81 81 $ mkcommit b
82 82 $ mkcommit original_c
83 83 $ hg up "desc('b')"
84 84 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
85 85 $ mkcommit new_c
86 86 created new head
87 87 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
88 88 $ hg debugobsolete --config format.obsstore-version=0 --flag 12 `getid original_c` `getid new_c` -d '121 120'
89 89 obsoleted 1 changesets
90 90 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
91 91 2:245bde4270cd add original_c
92 92 $ hg debugrevlog -cd
93 93 # rev p1rev p2rev start end deltastart base p1 p2 rawsize totalsize compression heads chainlen
94 94 0 -1 -1 0 59 0 0 0 0 58 58 0 1 0
95 95 1 0 -1 59 118 59 59 0 0 58 116 0 1 0
96 96 2 1 -1 118 193 118 118 59 0 76 192 0 1 0
97 97 3 1 -1 193 260 193 193 59 0 66 258 0 2 0
98 98 $ hg debugobsolete
99 99 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
100 100
101 101 (check for version number of the obsstore)
102 102
103 103 $ dd bs=1 count=1 if=.hg/store/obsstore 2>/dev/null
104 104 \x00 (no-eol) (esc)
105 105
106 106 do it again (it read the obsstore before adding new changeset)
107 107
108 108 $ hg up '.^'
109 109 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
110 110 $ mkcommit new_2_c
111 111 created new head
112 112 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
113 113 obsoleted 1 changesets
114 114 $ hg debugobsolete
115 115 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
116 116 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
117 117
118 118 Register two markers with a missing node
119 119
120 120 $ hg up '.^'
121 121 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
122 122 $ mkcommit new_3_c
123 123 created new head
124 124 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
125 125 obsoleted 1 changesets
126 126 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
127 127 $ hg debugobsolete
128 128 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
129 129 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
130 130 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
131 131 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
132 132
133 133 Test the --index option of debugobsolete command
134 134 $ hg debugobsolete --index
135 135 0 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
136 136 1 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
137 137 2 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
138 138 3 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
139 139
140 140 Refuse pathological nullid successors
141 141 $ hg debugobsolete -d '9001 0' 1337133713371337133713371337133713371337 0000000000000000000000000000000000000000
142 142 transaction abort!
143 143 rollback completed
144 144 abort: bad obsolescence marker detected: invalid successors nullid
145 145 [255]
146 146
147 147 Check that graphlog detect that a changeset is obsolete:
148 148
149 149 $ hg log -G
150 150 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
151 151 |
152 152 o 1:7c3bad9141dc (draft) [ ] add b
153 153 |
154 154 o 0:1f0dee641bb7 (draft) [ ] add a
155 155
156 156
157 157 check that heads does not report them
158 158
159 159 $ hg heads
160 160 5:5601fb93a350 (draft) [tip ] add new_3_c
161 161 $ hg heads --hidden
162 162 5:5601fb93a350 (draft) [tip ] add new_3_c
163 163 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c [rewritten as 5:5601fb93a350]
164 164 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c [rewritten as 4:ca819180edb9]
165 165 2:245bde4270cd (draft *obsolete*) [ ] add original_c [rewritten as 3:cdbce2fbb163]
166 166
167 167
168 168 check that summary does not report them
169 169
170 170 $ hg init ../sink
171 171 $ echo '[paths]' >> .hg/hgrc
172 172 $ echo 'default=../sink' >> .hg/hgrc
173 173 $ hg summary --remote
174 174 parent: 5:5601fb93a350 tip
175 175 add new_3_c
176 176 branch: default
177 177 commit: (clean)
178 178 update: (current)
179 179 phases: 3 draft
180 180 remote: 3 outgoing
181 181
182 182 $ hg summary --remote --hidden
183 183 parent: 5:5601fb93a350 tip
184 184 add new_3_c
185 185 branch: default
186 186 commit: (clean)
187 187 update: 3 new changesets, 4 branch heads (merge)
188 188 phases: 6 draft
189 189 remote: 3 outgoing
190 190
191 191 check that various commands work well with filtering
192 192
193 193 $ hg tip
194 194 5:5601fb93a350 (draft) [tip ] add new_3_c
195 195 $ hg log -r 6
196 196 abort: unknown revision '6'!
197 197 [255]
198 198 $ hg log -r 4
199 199 abort: hidden revision '4'!
200 200 (use --hidden to access hidden revisions)
201 201 [255]
202 202 $ hg debugrevspec 'rev(6)'
203 203 $ hg debugrevspec 'rev(4)'
204 204 $ hg debugrevspec 'null'
205 205 -1
206 206
207 207 Check that public changeset are not accounted as obsolete:
208 208
209 209 $ hg --hidden phase --public 2
210 210 $ hg log -G
211 211 @ 5:5601fb93a350 (draft phase-divergent) [tip ] add new_3_c
212 212 |
213 213 | o 2:245bde4270cd (public) [ ] add original_c
214 214 |/
215 215 o 1:7c3bad9141dc (public) [ ] add b
216 216 |
217 217 o 0:1f0dee641bb7 (public) [ ] add a
218 218
219 219
220 220 And that bumped changeset are detected
221 221 --------------------------------------
222 222
223 223 If we didn't filtered obsolete changesets out, 3 and 4 would show up too. Also
224 224 note that the bumped changeset (5:5601fb93a350) is not a direct successor of
225 225 the public changeset
226 226
227 227 $ hg log --hidden -r 'phasedivergent()'
228 228 5:5601fb93a350 (draft phase-divergent) [tip ] add new_3_c
229 229
230 230 And that we can't push bumped changeset
231 231
232 232 $ hg push ../tmpa -r 0 --force #(make repo related)
233 233 pushing to ../tmpa
234 234 searching for changes
235 235 warning: repository is unrelated
236 236 adding changesets
237 237 adding manifests
238 238 adding file changes
239 239 added 1 changesets with 1 changes to 1 files (+1 heads)
240 240 $ hg push ../tmpa
241 241 pushing to ../tmpa
242 242 searching for changes
243 243 abort: push includes phase-divergent changeset: 5601fb93a350!
244 244 [255]
245 245
246 246 Fixing "bumped" situation
247 247 We need to create a clone of 5 and add a special marker with a flag
248 248
249 249 $ hg summary
250 250 parent: 5:5601fb93a350 tip (phase-divergent)
251 251 add new_3_c
252 252 branch: default
253 253 commit: (clean)
254 254 update: 1 new changesets, 2 branch heads (merge)
255 255 phases: 1 draft
256 256 phase-divergent: 1 changesets
257 257 $ hg up '5^'
258 258 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
259 259 $ hg revert -ar 5
260 260 adding new_3_c
261 261 $ hg ci -m 'add n3w_3_c'
262 262 created new head
263 263 $ hg debugobsolete -d '1338 0' --flags 1 `getid new_3_c` `getid n3w_3_c`
264 264 obsoleted 1 changesets
265 265 $ hg log -r 'phasedivergent()'
266 266 $ hg log -G
267 267 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
268 268 |
269 269 | o 2:245bde4270cd (public) [ ] add original_c
270 270 |/
271 271 o 1:7c3bad9141dc (public) [ ] add b
272 272 |
273 273 o 0:1f0dee641bb7 (public) [ ] add a
274 274
275 275
276 276 Basic exclusive testing
277 277
278 278 $ hg log -G --hidden
279 279 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
280 280 |
281 281 | x 5:5601fb93a350 (draft *obsolete*) [ ] add new_3_c [rewritten as 6:6f9641995072]
282 282 |/
283 283 | x 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c [rewritten as 5:5601fb93a350]
284 284 |/
285 285 | x 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c [rewritten as 4:ca819180edb9]
286 286 |/
287 287 | o 2:245bde4270cd (public) [ ] add original_c
288 288 |/
289 289 o 1:7c3bad9141dc (public) [ ] add b
290 290 |
291 291 o 0:1f0dee641bb7 (public) [ ] add a
292 292
293 293 $ hg debugobsolete --rev 6f9641995072
294 294 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
295 295 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
296 296 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
297 297 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
298 298 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
299 299 $ hg debugobsolete --rev 6f9641995072 --exclusive
300 300 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
301 301 $ hg debugobsolete --rev 5601fb93a350 --hidden
302 302 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
303 303 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
304 304 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
305 305 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
306 306 $ hg debugobsolete --rev 5601fb93a350 --hidden --exclusive
307 307 $ hg debugobsolete --rev 5601fb93a350+6f9641995072 --hidden --exclusive
308 308 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
309 309 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
310 310 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
311 311
312 312 $ cd ..
313 313
314 314 Revision 0 is hidden
315 315 --------------------
316 316
317 317 $ hg init rev0hidden
318 318 $ cd rev0hidden
319 319
320 320 $ mkcommit kill0
321 321 $ hg up -q null
322 322 $ hg debugobsolete `getid kill0`
323 323 obsoleted 1 changesets
324 324 $ mkcommit a
325 325 $ mkcommit b
326 326
327 327 Should pick the first visible revision as "repo" node
328 328
329 329 $ hg archive ../archive-null
330 330 $ cat ../archive-null/.hg_archival.txt
331 331 repo: 1f0dee641bb7258c56bd60e93edfa2405381c41e
332 332 node: 7c3bad9141dcb46ff89abf5f61856facd56e476c
333 333 branch: default
334 334 latesttag: null
335 335 latesttagdistance: 2
336 336 changessincelatesttag: 2
337 337
338 338
339 339 $ cd ..
340 340
341 341 Exchange Test
342 342 ============================
343 343
344 344 Destination repo does not have any data
345 345 ---------------------------------------
346 346
347 347 Simple incoming test
348 348
349 349 $ hg init tmpc
350 350 $ cd tmpc
351 351 $ hg incoming ../tmpb
352 352 comparing with ../tmpb
353 353 0:1f0dee641bb7 (public) [ ] add a
354 354 1:7c3bad9141dc (public) [ ] add b
355 355 2:245bde4270cd (public) [ ] add original_c
356 356 6:6f9641995072 (draft) [tip ] add n3w_3_c
357 357
358 358 Try to pull markers
359 359 (extinct changeset are excluded but marker are pushed)
360 360
361 361 $ hg pull ../tmpb
362 362 pulling from ../tmpb
363 363 requesting all changes
364 364 adding changesets
365 365 adding manifests
366 366 adding file changes
367 367 added 4 changesets with 4 changes to 4 files (+1 heads)
368 368 5 new obsolescence markers
369 369 new changesets 1f0dee641bb7:6f9641995072
370 370 (run 'hg heads' to see heads, 'hg merge' to merge)
371 371 $ hg debugobsolete
372 372 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
373 373 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
374 374 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
375 375 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
376 376 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
377 377
378 378 Rollback//Transaction support
379 379
380 380 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
381 381 $ hg debugobsolete
382 382 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
383 383 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
384 384 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
385 385 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
386 386 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
387 387 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 (Thu Jan 01 00:22:20 1970 +0000) {'user': 'test'}
388 388 $ hg rollback -n
389 389 repository tip rolled back to revision 3 (undo debugobsolete)
390 390 $ hg rollback
391 391 repository tip rolled back to revision 3 (undo debugobsolete)
392 392 $ hg debugobsolete
393 393 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
394 394 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
395 395 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
396 396 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
397 397 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
398 398
399 399 $ cd ..
400 400
401 401 Try to push markers
402 402
403 403 $ hg init tmpd
404 404 $ hg -R tmpb push tmpd
405 405 pushing to tmpd
406 406 searching for changes
407 407 adding changesets
408 408 adding manifests
409 409 adding file changes
410 410 added 4 changesets with 4 changes to 4 files (+1 heads)
411 411 5 new obsolescence markers
412 412 $ hg -R tmpd debugobsolete | sort
413 413 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
414 414 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
415 415 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
416 416 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
417 417 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
418 418
419 419 Check obsolete keys are exchanged only if source has an obsolete store
420 420
421 421 $ hg init empty
422 422 $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
423 423 pushing to tmpd
424 424 listkeys phases
425 425 listkeys bookmarks
426 426 no changes found
427 427 listkeys phases
428 428 [1]
429 429
430 430 clone support
431 431 (markers are copied and extinct changesets are included to allow hardlinks)
432 432
433 433 $ hg clone tmpb clone-dest
434 434 updating to branch default
435 435 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 436 $ hg -R clone-dest log -G --hidden
437 437 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
438 438 |
439 439 | x 5:5601fb93a350 (draft *obsolete*) [ ] add new_3_c [rewritten as 6:6f9641995072]
440 440 |/
441 441 | x 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c [rewritten as 5:5601fb93a350]
442 442 |/
443 443 | x 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c [rewritten as 4:ca819180edb9]
444 444 |/
445 445 | o 2:245bde4270cd (public) [ ] add original_c
446 446 |/
447 447 o 1:7c3bad9141dc (public) [ ] add b
448 448 |
449 449 o 0:1f0dee641bb7 (public) [ ] add a
450 450
451 451 $ hg -R clone-dest debugobsolete
452 452 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
453 453 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
454 454 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
455 455 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
456 456 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
457 457
458 458
459 459 Destination repo have existing data
460 460 ---------------------------------------
461 461
462 462 On pull
463 463
464 464 $ hg init tmpe
465 465 $ cd tmpe
466 466 $ hg debugobsolete -d '1339 0' 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00
467 467 $ hg pull ../tmpb
468 468 pulling from ../tmpb
469 469 requesting all changes
470 470 adding changesets
471 471 adding manifests
472 472 adding file changes
473 473 added 4 changesets with 4 changes to 4 files (+1 heads)
474 474 5 new obsolescence markers
475 475 new changesets 1f0dee641bb7:6f9641995072
476 476 (run 'hg heads' to see heads, 'hg merge' to merge)
477 477 $ hg debugobsolete
478 478 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
479 479 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
480 480 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
481 481 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
482 482 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
483 483 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
484 484
485 485
486 486 On push
487 487
488 488 $ hg push ../tmpc
489 489 pushing to ../tmpc
490 490 searching for changes
491 491 no changes found
492 492 1 new obsolescence markers
493 493 [1]
494 494 $ hg -R ../tmpc debugobsolete
495 495 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
496 496 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
497 497 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
498 498 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
499 499 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
500 500 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
501 501
502 502 detect outgoing obsolete and unstable
503 503 ---------------------------------------
504 504
505 505
506 506 $ hg log -G
507 507 o 3:6f9641995072 (draft) [tip ] add n3w_3_c
508 508 |
509 509 | o 2:245bde4270cd (public) [ ] add original_c
510 510 |/
511 511 o 1:7c3bad9141dc (public) [ ] add b
512 512 |
513 513 o 0:1f0dee641bb7 (public) [ ] add a
514 514
515 515 $ hg up 'desc("n3w_3_c")'
516 516 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
517 517 $ mkcommit original_d
518 518 $ mkcommit original_e
519 519 $ hg debugobsolete --record-parents `getid original_d` -d '0 0'
520 520 obsoleted 1 changesets
521 521 $ hg debugobsolete | grep `getid original_d`
522 522 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
523 523 $ hg log -r 'obsolete()'
524 524 4:94b33453f93b (draft *obsolete*) [ ] add original_d [pruned]
525 525 $ hg summary
526 526 parent: 5:cda648ca50f5 tip (orphan)
527 527 add original_e
528 528 branch: default
529 529 commit: (clean)
530 530 update: 1 new changesets, 2 branch heads (merge)
531 531 phases: 3 draft
532 532 orphan: 1 changesets
533 533 $ hg log -G -r '::orphan()'
534 534 @ 5:cda648ca50f5 (draft orphan) [tip ] add original_e
535 535 |
536 536 x 4:94b33453f93b (draft *obsolete*) [ ] add original_d [pruned]
537 537 |
538 538 o 3:6f9641995072 (draft) [ ] add n3w_3_c
539 539 |
540 540 o 1:7c3bad9141dc (public) [ ] add b
541 541 |
542 542 o 0:1f0dee641bb7 (public) [ ] add a
543 543
544 544
545 545 refuse to push obsolete changeset
546 546
547 547 $ hg push ../tmpc/ -r 'desc("original_d")'
548 548 pushing to ../tmpc/
549 549 searching for changes
550 550 abort: push includes obsolete changeset: 94b33453f93b!
551 551 [255]
552 552
553 553 refuse to push unstable changeset
554 554
555 555 $ hg push ../tmpc/
556 556 pushing to ../tmpc/
557 557 searching for changes
558 558 abort: push includes orphan changeset: cda648ca50f5!
559 559 [255]
560 560
561 561 Test that extinct changeset are properly detected
562 562
563 563 $ hg log -r 'extinct()'
564 564
565 565 Don't try to push extinct changeset
566 566
567 567 $ hg init ../tmpf
568 568 $ hg out ../tmpf
569 569 comparing with ../tmpf
570 570 searching for changes
571 571 0:1f0dee641bb7 (public) [ ] add a
572 572 1:7c3bad9141dc (public) [ ] add b
573 573 2:245bde4270cd (public) [ ] add original_c
574 574 3:6f9641995072 (draft) [ ] add n3w_3_c
575 575 4:94b33453f93b (draft *obsolete*) [ ] add original_d [pruned]
576 576 5:cda648ca50f5 (draft orphan) [tip ] add original_e
577 577 $ hg push ../tmpf -f # -f because be push unstable too
578 578 pushing to ../tmpf
579 579 searching for changes
580 580 adding changesets
581 581 adding manifests
582 582 adding file changes
583 583 added 6 changesets with 6 changes to 6 files (+1 heads)
584 584 7 new obsolescence markers
585 585
586 586 no warning displayed
587 587
588 588 $ hg push ../tmpf
589 589 pushing to ../tmpf
590 590 searching for changes
591 591 no changes found
592 592 [1]
593 593
594 594 Do not warn about new head when the new head is a successors of a remote one
595 595
596 596 $ hg log -G
597 597 @ 5:cda648ca50f5 (draft orphan) [tip ] add original_e
598 598 |
599 599 x 4:94b33453f93b (draft *obsolete*) [ ] add original_d [pruned]
600 600 |
601 601 o 3:6f9641995072 (draft) [ ] add n3w_3_c
602 602 |
603 603 | o 2:245bde4270cd (public) [ ] add original_c
604 604 |/
605 605 o 1:7c3bad9141dc (public) [ ] add b
606 606 |
607 607 o 0:1f0dee641bb7 (public) [ ] add a
608 608
609 609 $ hg up -q 'desc(n3w_3_c)'
610 610 $ mkcommit obsolete_e
611 611 created new head
612 612 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'` \
613 613 > -u 'test <test@example.net>'
614 614 obsoleted 1 changesets
615 615 $ hg outgoing ../tmpf # parasite hg outgoing testin
616 616 comparing with ../tmpf
617 617 searching for changes
618 618 6:3de5eca88c00 (draft) [tip ] add obsolete_e
619 619 $ hg push ../tmpf
620 620 pushing to ../tmpf
621 621 searching for changes
622 622 adding changesets
623 623 adding manifests
624 624 adding file changes
625 625 added 1 changesets with 1 changes to 1 files (+1 heads)
626 626 1 new obsolescence markers
627 627 obsoleted 1 changesets
628 628
629 629 test relevance computation
630 630 ---------------------------------------
631 631
632 632 Checking simple case of "marker relevance".
633 633
634 634
635 635 Reminder of the repo situation
636 636
637 637 $ hg log --hidden --graph
638 638 @ 6:3de5eca88c00 (draft) [tip ] add obsolete_e
639 639 |
640 640 | x 5:cda648ca50f5 (draft *obsolete*) [ ] add original_e [rewritten as 6:3de5eca88c00 by test <test@example.net>]
641 641 | |
642 642 | x 4:94b33453f93b (draft *obsolete*) [ ] add original_d [pruned]
643 643 |/
644 644 o 3:6f9641995072 (draft) [ ] add n3w_3_c
645 645 |
646 646 | o 2:245bde4270cd (public) [ ] add original_c
647 647 |/
648 648 o 1:7c3bad9141dc (public) [ ] add b
649 649 |
650 650 o 0:1f0dee641bb7 (public) [ ] add a
651 651
652 652
653 653 List of all markers
654 654
655 655 $ hg debugobsolete
656 656 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
657 657 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
658 658 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
659 659 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
660 660 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
661 661 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
662 662 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
663 663 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test <test@example.net>'} (glob)
664 664
665 665 List of changesets with no chain
666 666
667 667 $ hg debugobsolete --hidden --rev ::2
668 668
669 669 List of changesets that are included on marker chain
670 670
671 671 $ hg debugobsolete --hidden --rev 6
672 672 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test <test@example.net>'} (glob)
673 673
674 674 List of changesets with a longer chain, (including a pruned children)
675 675
676 676 $ hg debugobsolete --hidden --rev 3
677 677 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
678 678 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
679 679 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
680 680 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
681 681 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
682 682 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
683 683 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
684 684
685 685 List of both
686 686
687 687 $ hg debugobsolete --hidden --rev 3::6
688 688 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
689 689 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
690 690 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
691 691 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
692 692 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
693 693 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
694 694 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test <test@example.net>'} (glob)
695 695 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
696 696
697 697 List of all markers in JSON
698 698
699 699 $ hg debugobsolete -Tjson
700 700 [
701 701 {
702 702 "date": [1339.0, 0],
703 703 "flag": 0,
704 704 "metadata": {"user": "test"},
705 705 "prednode": "1339133913391339133913391339133913391339",
706 706 "succnodes": ["ca819180edb99ed25ceafb3e9584ac287e240b00"]
707 707 },
708 708 {
709 709 "date": [1339.0, 0],
710 710 "flag": 0,
711 711 "metadata": {"user": "test"},
712 712 "prednode": "1337133713371337133713371337133713371337",
713 713 "succnodes": ["5601fb93a350734d935195fee37f4054c529ff39"]
714 714 },
715 715 {
716 716 "date": [121.0, 120],
717 717 "flag": 12,
718 718 "metadata": {"user": "test"},
719 719 "prednode": "245bde4270cd1072a27757984f9cda8ba26f08ca",
720 720 "succnodes": ["cdbce2fbb16313928851e97e0d85413f3f7eb77f"]
721 721 },
722 722 {
723 723 "date": [1338.0, 0],
724 724 "flag": 1,
725 725 "metadata": {"user": "test"},
726 726 "prednode": "5601fb93a350734d935195fee37f4054c529ff39",
727 727 "succnodes": ["6f96419950729f3671185b847352890f074f7557"]
728 728 },
729 729 {
730 730 "date": [1338.0, 0],
731 731 "flag": 0,
732 732 "metadata": {"user": "test"},
733 733 "prednode": "ca819180edb99ed25ceafb3e9584ac287e240b00",
734 734 "succnodes": ["1337133713371337133713371337133713371337"]
735 735 },
736 736 {
737 737 "date": [1337.0, 0],
738 738 "flag": 0,
739 739 "metadata": {"user": "test"},
740 740 "prednode": "cdbce2fbb16313928851e97e0d85413f3f7eb77f",
741 741 "succnodes": ["ca819180edb99ed25ceafb3e9584ac287e240b00"]
742 742 },
743 743 {
744 744 "date": [0.0, 0],
745 745 "flag": 0,
746 746 "metadata": {"user": "test"},
747 747 "parentnodes": ["6f96419950729f3671185b847352890f074f7557"],
748 748 "prednode": "94b33453f93bdb8d457ef9b770851a618bf413e1",
749 749 "succnodes": []
750 750 },
751 751 {
752 752 "date": *, (glob)
753 753 "flag": 0,
754 754 "metadata": {"user": "test <test@example.net>"},
755 755 "prednode": "cda648ca50f50482b7055c0b0c4c117bba6733d9",
756 756 "succnodes": ["3de5eca88c00aa039da7399a220f4a5221faa585"]
757 757 }
758 758 ]
759 759
760 760 Template keywords
761 761
762 762 $ hg debugobsolete -r6 -T '{succnodes % "{node|short}"} {date|shortdate}\n'
763 763 3de5eca88c00 ????-??-?? (glob)
764 764 $ hg debugobsolete -r6 -T '{join(metadata % "{key}={value}", " ")}\n'
765 765 user=test <test@example.net>
766 766 $ hg debugobsolete -r6 -T '{metadata}\n{metadata}\n'
767 767 'user': 'test <test@example.net>'
768 768 'user': 'test <test@example.net>'
769 769 $ hg debugobsolete -r6 -T '{succnodes}\n{succnodes}\n'
770 770 3de5eca88c00aa039da7399a220f4a5221faa585
771 771 3de5eca88c00aa039da7399a220f4a5221faa585
772 772 $ hg debugobsolete -r6 -T '{flag} {get(metadata, "user")}\n'
773 773 0 test <test@example.net>
774 774
775 775 Test the debug output for exchange
776 776 ----------------------------------
777 777
778 778 $ hg pull ../tmpb --config 'experimental.obsmarkers-exchange-debug=True' # bundle2
779 779 pulling from ../tmpb
780 780 searching for changes
781 781 no changes found
782 782 obsmarker-exchange: 346 bytes received
783 783
784 784 check hgweb does not explode
785 785 ====================================
786 786
787 787 $ hg unbundle $TESTDIR/bundles/hgweb+obs.hg
788 788 adding changesets
789 789 adding manifests
790 790 adding file changes
791 791 added 62 changesets with 63 changes to 9 files (+60 heads)
792 792 new changesets 50c51b361e60:c15e9edfca13
793 793 (run 'hg heads .' to see heads, 'hg merge' to merge)
794 794 $ for node in `hg log -r 'desc(babar_)' --template '{node}\n'`;
795 795 > do
796 796 > hg debugobsolete $node
797 797 > done
798 798 obsoleted 1 changesets
799 799 obsoleted 1 changesets
800 800 obsoleted 1 changesets
801 801 obsoleted 1 changesets
802 802 obsoleted 1 changesets
803 803 obsoleted 1 changesets
804 804 obsoleted 1 changesets
805 805 obsoleted 1 changesets
806 806 obsoleted 1 changesets
807 807 obsoleted 1 changesets
808 808 obsoleted 1 changesets
809 809 obsoleted 1 changesets
810 810 obsoleted 1 changesets
811 811 obsoleted 1 changesets
812 812 obsoleted 1 changesets
813 813 obsoleted 1 changesets
814 814 obsoleted 1 changesets
815 815 obsoleted 1 changesets
816 816 obsoleted 1 changesets
817 817 obsoleted 1 changesets
818 818 obsoleted 1 changesets
819 819 obsoleted 1 changesets
820 820 obsoleted 1 changesets
821 821 obsoleted 1 changesets
822 822 obsoleted 1 changesets
823 823 obsoleted 1 changesets
824 824 obsoleted 1 changesets
825 825 obsoleted 1 changesets
826 826 obsoleted 1 changesets
827 827 obsoleted 1 changesets
828 828 obsoleted 1 changesets
829 829 obsoleted 1 changesets
830 830 obsoleted 1 changesets
831 831 obsoleted 1 changesets
832 832 obsoleted 1 changesets
833 833 obsoleted 1 changesets
834 834 obsoleted 1 changesets
835 835 obsoleted 1 changesets
836 836 obsoleted 1 changesets
837 837 obsoleted 1 changesets
838 838 obsoleted 1 changesets
839 839 obsoleted 1 changesets
840 840 obsoleted 1 changesets
841 841 obsoleted 1 changesets
842 842 obsoleted 1 changesets
843 843 obsoleted 1 changesets
844 844 obsoleted 1 changesets
845 845 obsoleted 1 changesets
846 846 obsoleted 1 changesets
847 847 obsoleted 1 changesets
848 848 obsoleted 1 changesets
849 849 obsoleted 1 changesets
850 850 obsoleted 1 changesets
851 851 obsoleted 1 changesets
852 852 obsoleted 1 changesets
853 853 obsoleted 1 changesets
854 854 obsoleted 1 changesets
855 855 obsoleted 1 changesets
856 856 obsoleted 1 changesets
857 857 obsoleted 1 changesets
858 858 $ hg up tip
859 859 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
860 860
861 861 #if serve
862 862
863 863 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
864 864 $ cat hg.pid >> $DAEMON_PIDS
865 865
866 866 check changelog view
867 867
868 868 $ get-with-headers.py --headeronly localhost:$HGPORT 'shortlog/'
869 869 200 Script output follows
870 870
871 871 check graph view
872 872
873 873 $ get-with-headers.py --headeronly localhost:$HGPORT 'graph'
874 874 200 Script output follows
875 875
876 876 check filelog view
877 877
878 878 $ get-with-headers.py --headeronly localhost:$HGPORT 'log/'`hg log -r . -T "{node}"`/'babar'
879 879 200 Script output follows
880 880
881 881 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/68'
882 882 200 Script output follows
883 883 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/67'
884 884 404 Not Found
885 885 [1]
886 886
887 887 check that web.view config option:
888 888
889 889 $ killdaemons.py hg.pid
890 890 $ cat >> .hg/hgrc << EOF
891 891 > [web]
892 892 > view=all
893 893 > EOF
894 894 $ wait
895 895 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
896 896 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/67'
897 897 200 Script output follows
898 898 $ killdaemons.py hg.pid
899 899
900 900 Checking _enable=False warning if obsolete marker exists
901 901
902 902 $ echo '[experimental]' >> $HGRCPATH
903 903 $ echo "evolution=" >> $HGRCPATH
904 904 $ hg log -r tip
905 905 68:c15e9edfca13 (draft) [tip ] add celestine
906 906
907 907 reenable for later test
908 908
909 909 $ echo '[experimental]' >> $HGRCPATH
910 910 $ echo "evolution.exchange=True" >> $HGRCPATH
911 911 $ echo "evolution.createmarkers=True" >> $HGRCPATH
912 912
913 913 $ rm hg.pid access.log errors.log
914 914 #endif
915 915
916 916 Several troubles on the same changeset (create an unstable and bumped changeset)
917 917
918 918 $ hg debugobsolete `getid obsolete_e`
919 919 obsoleted 1 changesets
920 920 $ hg debugobsolete `getid original_c` `getid babar`
921 921 $ hg log --config ui.logtemplate= -r 'phasedivergent() and orphan()'
922 922 changeset: 7:50c51b361e60
923 923 user: test
924 924 date: Thu Jan 01 00:00:00 1970 +0000
925 925 instability: orphan, phase-divergent
926 926 summary: add babar
927 927
928 928
929 929 test the "obsolete" templatekw
930 930
931 931 $ hg log -r 'obsolete()'
932 932 6:3de5eca88c00 (draft *obsolete*) [ ] add obsolete_e [pruned]
933 933
934 934 test the "troubles" templatekw
935 935
936 936 $ hg log -r 'phasedivergent() and orphan()'
937 937 7:50c51b361e60 (draft orphan phase-divergent) [ ] add babar
938 938
939 939 test the default cmdline template
940 940
941 941 $ hg log -T default -r 'phasedivergent()'
942 942 changeset: 7:50c51b361e60
943 943 user: test
944 944 date: Thu Jan 01 00:00:00 1970 +0000
945 945 instability: orphan, phase-divergent
946 946 summary: add babar
947 947
948 948 $ hg log -T default -r 'obsolete()'
949 949 changeset: 6:3de5eca88c00
950 950 parent: 3:6f9641995072
951 951 user: test
952 952 date: Thu Jan 01 00:00:00 1970 +0000
953 953 obsfate: pruned
954 954 summary: add obsolete_e
955 955
956 956
957 957 test the obsolete labels
958 958
959 959 $ hg log --config ui.logtemplate= --color=debug -r 'phasedivergent()'
960 960 [log.changeset changeset.draft changeset.unstable instability.orphan instability.phase-divergent|changeset: 7:50c51b361e60]
961 961 [log.user|user: test]
962 962 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
963 963 [log.instability|instability: orphan, phase-divergent]
964 964 [log.summary|summary: add babar]
965 965
966 966
967 967 $ hg log -T default -r 'phasedivergent()' --color=debug
968 968 [log.changeset changeset.draft changeset.unstable instability.orphan instability.phase-divergent|changeset: 7:50c51b361e60]
969 969 [log.user|user: test]
970 970 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
971 971 [log.instability|instability: orphan, phase-divergent]
972 972 [log.summary|summary: add babar]
973 973
974 974
975 975 $ hg log --config ui.logtemplate= --color=debug -r "obsolete()"
976 976 [log.changeset changeset.draft changeset.obsolete|changeset: 6:3de5eca88c00]
977 977 [log.parent changeset.draft|parent: 3:6f9641995072]
978 978 [log.user|user: test]
979 979 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
980 [log.obsfate|obsfate: pruned]
980 [log.obsfate|obsolete: pruned]
981 981 [log.summary|summary: add obsolete_e]
982 982
983 983
984 984 $ hg log -T default -r 'obsolete()' --color=debug
985 985 [log.changeset changeset.draft changeset.obsolete|changeset: 6:3de5eca88c00]
986 986 [log.parent changeset.draft|parent: 3:6f9641995072]
987 987 [log.user|user: test]
988 988 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
989 989 [log.obsfate|obsfate: pruned]
990 990 [log.summary|summary: add obsolete_e]
991 991
992 992
993 993 test summary output
994 994
995 995 $ hg up -r 'phasedivergent() and orphan()'
996 996 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
997 997 $ hg summary
998 998 parent: 7:50c51b361e60 (orphan, phase-divergent)
999 999 add babar
1000 1000 branch: default
1001 1001 commit: (clean)
1002 1002 update: 2 new changesets (update)
1003 1003 phases: 4 draft
1004 1004 orphan: 2 changesets
1005 1005 phase-divergent: 1 changesets
1006 1006 $ hg up -r 'obsolete()'
1007 1007 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1008 1008 $ hg summary
1009 1009 parent: 6:3de5eca88c00 (obsolete)
1010 1010 add obsolete_e
1011 1011 branch: default
1012 1012 commit: (clean)
1013 1013 update: 3 new changesets (update)
1014 1014 phases: 4 draft
1015 1015 orphan: 2 changesets
1016 1016 phase-divergent: 1 changesets
1017 1017
1018 1018 Test incoming/outcoming with changesets obsoleted remotely, known locally
1019 1019 ===============================================================================
1020 1020
1021 1021 This test issue 3805
1022 1022
1023 1023 $ hg init repo-issue3805
1024 1024 $ cd repo-issue3805
1025 1025 $ echo "base" > base
1026 1026 $ hg ci -Am "base"
1027 1027 adding base
1028 1028 $ echo "foo" > foo
1029 1029 $ hg ci -Am "A"
1030 1030 adding foo
1031 1031 $ hg clone . ../other-issue3805
1032 1032 updating to branch default
1033 1033 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1034 1034 $ echo "bar" >> foo
1035 1035 $ hg ci --amend
1036 1036 $ cd ../other-issue3805
1037 1037 $ hg log -G
1038 1038 @ 1:29f0c6921ddd (draft) [tip ] A
1039 1039 |
1040 1040 o 0:d20a80d4def3 (draft) [ ] base
1041 1041
1042 1042 $ hg log -G -R ../repo-issue3805
1043 1043 @ 2:323a9c3ddd91 (draft) [tip ] A
1044 1044 |
1045 1045 o 0:d20a80d4def3 (draft) [ ] base
1046 1046
1047 1047 $ hg incoming
1048 1048 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
1049 1049 searching for changes
1050 1050 2:323a9c3ddd91 (draft) [tip ] A
1051 1051 $ hg incoming --bundle ../issue3805.hg
1052 1052 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
1053 1053 searching for changes
1054 1054 2:323a9c3ddd91 (draft) [tip ] A
1055 1055 $ hg outgoing
1056 1056 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
1057 1057 searching for changes
1058 1058 1:29f0c6921ddd (draft) [tip ] A
1059 1059
1060 1060 #if serve
1061 1061
1062 1062 $ hg serve -R ../repo-issue3805 -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
1063 1063 $ cat hg.pid >> $DAEMON_PIDS
1064 1064
1065 1065 $ hg incoming http://localhost:$HGPORT
1066 1066 comparing with http://localhost:$HGPORT/
1067 1067 searching for changes
1068 1068 2:323a9c3ddd91 (draft) [tip ] A
1069 1069 $ hg outgoing http://localhost:$HGPORT
1070 1070 comparing with http://localhost:$HGPORT/
1071 1071 searching for changes
1072 1072 1:29f0c6921ddd (draft) [tip ] A
1073 1073
1074 1074 $ killdaemons.py
1075 1075
1076 1076 #endif
1077 1077
1078 1078 This test issue 3814
1079 1079
1080 1080 (nothing to push but locally hidden changeset)
1081 1081
1082 1082 $ cd ..
1083 1083 $ hg init repo-issue3814
1084 1084 $ cd repo-issue3805
1085 1085 $ hg push -r 323a9c3ddd91 ../repo-issue3814
1086 1086 pushing to ../repo-issue3814
1087 1087 searching for changes
1088 1088 adding changesets
1089 1089 adding manifests
1090 1090 adding file changes
1091 1091 added 2 changesets with 2 changes to 2 files
1092 1092 1 new obsolescence markers
1093 1093 $ hg out ../repo-issue3814
1094 1094 comparing with ../repo-issue3814
1095 1095 searching for changes
1096 1096 no changes found
1097 1097 [1]
1098 1098
1099 1099 Test that a local tag blocks a changeset from being hidden
1100 1100
1101 1101 $ hg tag -l visible -r 1 --hidden
1102 1102 $ hg log -G
1103 1103 @ 2:323a9c3ddd91 (draft) [tip ] A
1104 1104 |
1105 1105 | x 1:29f0c6921ddd (draft *obsolete*) [visible ] A [rewritten using amend as 2:323a9c3ddd91]
1106 1106 |/
1107 1107 o 0:d20a80d4def3 (draft) [ ] base
1108 1108
1109 1109 Test that removing a local tag does not cause some commands to fail
1110 1110
1111 1111 $ hg tag -l -r tip tiptag
1112 1112 $ hg tags
1113 1113 tiptag 2:323a9c3ddd91
1114 1114 tip 2:323a9c3ddd91
1115 1115 visible 1:29f0c6921ddd
1116 1116 $ hg --config extensions.strip= strip -r tip --no-backup
1117 1117 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1118 1118 $ hg tags
1119 1119 visible 1:29f0c6921ddd
1120 1120 tip 1:29f0c6921ddd
1121 1121
1122 1122 Test bundle overlay onto hidden revision
1123 1123
1124 1124 $ cd ..
1125 1125 $ hg init repo-bundleoverlay
1126 1126 $ cd repo-bundleoverlay
1127 1127 $ echo "A" > foo
1128 1128 $ hg ci -Am "A"
1129 1129 adding foo
1130 1130 $ echo "B" >> foo
1131 1131 $ hg ci -m "B"
1132 1132 $ hg up 0
1133 1133 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1134 1134 $ echo "C" >> foo
1135 1135 $ hg ci -m "C"
1136 1136 created new head
1137 1137 $ hg log -G
1138 1138 @ 2:c186d7714947 (draft) [tip ] C
1139 1139 |
1140 1140 | o 1:44526ebb0f98 (draft) [ ] B
1141 1141 |/
1142 1142 o 0:4b34ecfb0d56 (draft) [ ] A
1143 1143
1144 1144
1145 1145 $ hg clone -r1 . ../other-bundleoverlay
1146 1146 adding changesets
1147 1147 adding manifests
1148 1148 adding file changes
1149 1149 added 2 changesets with 2 changes to 1 files
1150 1150 new changesets 4b34ecfb0d56:44526ebb0f98
1151 1151 updating to branch default
1152 1152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1153 1153 $ cd ../other-bundleoverlay
1154 1154 $ echo "B+" >> foo
1155 1155 $ hg ci --amend -m "B+"
1156 1156 $ hg log -G --hidden
1157 1157 @ 2:b7d587542d40 (draft) [tip ] B+
1158 1158 |
1159 1159 | x 1:44526ebb0f98 (draft *obsolete*) [ ] B [rewritten using amend as 2:b7d587542d40]
1160 1160 |/
1161 1161 o 0:4b34ecfb0d56 (draft) [ ] A
1162 1162
1163 1163
1164 1164 $ hg incoming ../repo-bundleoverlay --bundle ../bundleoverlay.hg
1165 1165 comparing with ../repo-bundleoverlay
1166 1166 searching for changes
1167 1167 1:44526ebb0f98 (draft) [ ] B
1168 1168 2:c186d7714947 (draft) [tip ] C
1169 1169 $ hg log -G -R ../bundleoverlay.hg
1170 1170 o 3:c186d7714947 (draft) [tip ] C
1171 1171 |
1172 1172 | @ 2:b7d587542d40 (draft) [ ] B+
1173 1173 |/
1174 1174 o 0:4b34ecfb0d56 (draft) [ ] A
1175 1175
1176 1176
1177 1177 #if serve
1178 1178
1179 1179 Test issue 4506
1180 1180
1181 1181 $ cd ..
1182 1182 $ hg init repo-issue4506
1183 1183 $ cd repo-issue4506
1184 1184 $ echo "0" > foo
1185 1185 $ hg add foo
1186 1186 $ hg ci -m "content-0"
1187 1187
1188 1188 $ hg up null
1189 1189 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1190 1190 $ echo "1" > bar
1191 1191 $ hg add bar
1192 1192 $ hg ci -m "content-1"
1193 1193 created new head
1194 1194 $ hg up 0
1195 1195 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1196 1196 $ hg graft 1
1197 1197 grafting 1:1c9eddb02162 "content-1" (tip)
1198 1198
1199 1199 $ hg debugobsolete `hg log -r1 -T'{node}'` `hg log -r2 -T'{node}'`
1200 1200 obsoleted 1 changesets
1201 1201
1202 1202 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
1203 1203 $ cat hg.pid >> $DAEMON_PIDS
1204 1204
1205 1205 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/1'
1206 1206 404 Not Found
1207 1207 [1]
1208 1208 $ get-with-headers.py --headeronly localhost:$HGPORT 'file/tip/bar'
1209 1209 200 Script output follows
1210 1210 $ get-with-headers.py --headeronly localhost:$HGPORT 'annotate/tip/bar'
1211 1211 200 Script output follows
1212 1212
1213 1213 $ killdaemons.py
1214 1214
1215 1215 #endif
1216 1216
1217 1217 Test heads computation on pending index changes with obsolescence markers
1218 1218 $ cd ..
1219 1219 $ cat >$TESTTMP/test_extension.py << EOF
1220 1220 > from __future__ import absolute_import
1221 1221 > from mercurial.i18n import _
1222 1222 > from mercurial import cmdutil, registrar
1223 1223 >
1224 1224 > cmdtable = {}
1225 1225 > command = registrar.command(cmdtable)
1226 1226 > @command(b"amendtransient",[], _('hg amendtransient [rev]'))
1227 1227 > def amend(ui, repo, *pats, **opts):
1228 1228 > opts['message'] = 'Test'
1229 1229 > opts['logfile'] = None
1230 1230 > cmdutil.amend(ui, repo, repo['.'], {}, pats, opts)
1231 1231 > ui.write('%s\n' % repo.changelog.headrevs())
1232 1232 > EOF
1233 1233 $ cat >> $HGRCPATH << EOF
1234 1234 > [extensions]
1235 1235 > testextension=$TESTTMP/test_extension.py
1236 1236 > EOF
1237 1237 $ hg init repo-issue-nativerevs-pending-changes
1238 1238 $ cd repo-issue-nativerevs-pending-changes
1239 1239 $ mkcommit a
1240 1240 $ mkcommit b
1241 1241 $ hg up ".^"
1242 1242 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1243 1243 $ echo aa > a
1244 1244 $ hg amendtransient
1245 1245 [1, 2]
1246 1246
1247 1247 Test cache consistency for the visible filter
1248 1248 1) We want to make sure that the cached filtered revs are invalidated when
1249 1249 bookmarks change
1250 1250 $ cd ..
1251 1251 $ cat >$TESTTMP/test_extension.py << EOF
1252 1252 > from __future__ import absolute_import, print_function
1253 1253 > import weakref
1254 1254 > from mercurial import (
1255 1255 > bookmarks,
1256 1256 > cmdutil,
1257 1257 > extensions,
1258 1258 > repoview,
1259 1259 > )
1260 1260 > def _bookmarkchanged(orig, bkmstoreinst, *args, **kwargs):
1261 1261 > reporef = weakref.ref(bkmstoreinst._repo)
1262 1262 > def trhook(tr):
1263 1263 > repo = reporef()
1264 1264 > hidden1 = repoview.computehidden(repo)
1265 1265 > hidden = repoview.filterrevs(repo, 'visible')
1266 1266 > if sorted(hidden1) != sorted(hidden):
1267 1267 > print("cache inconsistency")
1268 1268 > bkmstoreinst._repo.currenttransaction().addpostclose('test_extension', trhook)
1269 1269 > orig(bkmstoreinst, *args, **kwargs)
1270 1270 > def extsetup(ui):
1271 1271 > extensions.wrapfunction(bookmarks.bmstore, '_recordchange',
1272 1272 > _bookmarkchanged)
1273 1273 > EOF
1274 1274
1275 1275 $ hg init repo-cache-inconsistency
1276 1276 $ cd repo-issue-nativerevs-pending-changes
1277 1277 $ mkcommit a
1278 1278 a already tracked!
1279 1279 $ mkcommit b
1280 1280 $ hg id
1281 1281 13bedc178fce tip
1282 1282 $ echo "hello" > b
1283 1283 $ hg commit --amend -m "message"
1284 1284 $ hg book bookb -r 13bedc178fce --hidden
1285 1285 $ hg log -r 13bedc178fce
1286 1286 4:13bedc178fce (draft *obsolete*) [ bookb] add b [rewritten using amend as 5:a9b1f8652753]
1287 1287 $ hg book -d bookb
1288 1288 $ hg log -r 13bedc178fce
1289 1289 abort: hidden revision '13bedc178fce'!
1290 1290 (use --hidden to access hidden revisions)
1291 1291 [255]
1292 1292
1293 1293 Empty out the test extension, as it isn't compatible with later parts
1294 1294 of the test.
1295 1295 $ echo > $TESTTMP/test_extension.py
1296 1296
1297 1297 Test ability to pull changeset with locally applying obsolescence markers
1298 1298 (issue4945)
1299 1299
1300 1300 $ cd ..
1301 1301 $ hg init issue4845
1302 1302 $ cd issue4845
1303 1303
1304 1304 $ echo foo > f0
1305 1305 $ hg add f0
1306 1306 $ hg ci -m '0'
1307 1307 $ echo foo > f1
1308 1308 $ hg add f1
1309 1309 $ hg ci -m '1'
1310 1310 $ echo foo > f2
1311 1311 $ hg add f2
1312 1312 $ hg ci -m '2'
1313 1313
1314 1314 $ echo bar > f2
1315 1315 $ hg commit --amend --config experimental.evolution.createmarkers=True
1316 1316 $ hg log -G
1317 1317 @ 3:b0551702f918 (draft) [tip ] 2
1318 1318 |
1319 1319 o 1:e016b03fd86f (draft) [ ] 1
1320 1320 |
1321 1321 o 0:a78f55e5508c (draft) [ ] 0
1322 1322
1323 1323 $ hg log -G --hidden
1324 1324 @ 3:b0551702f918 (draft) [tip ] 2
1325 1325 |
1326 1326 | x 2:e008cf283490 (draft *obsolete*) [ ] 2 [rewritten using amend as 3:b0551702f918]
1327 1327 |/
1328 1328 o 1:e016b03fd86f (draft) [ ] 1
1329 1329 |
1330 1330 o 0:a78f55e5508c (draft) [ ] 0
1331 1331
1332 1332
1333 1333 $ hg strip --hidden -r 2 --config extensions.strip= --config devel.strip-obsmarkers=no
1334 1334 saved backup bundle to $TESTTMP/tmpe/issue4845/.hg/strip-backup/e008cf283490-ede36964-backup.hg (glob)
1335 1335 $ hg debugobsolete
1336 1336 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1337 1337 $ hg log -G
1338 1338 @ 2:b0551702f918 (draft) [tip ] 2
1339 1339 |
1340 1340 o 1:e016b03fd86f (draft) [ ] 1
1341 1341 |
1342 1342 o 0:a78f55e5508c (draft) [ ] 0
1343 1343
1344 1344 $ hg log -G --hidden
1345 1345 @ 2:b0551702f918 (draft) [tip ] 2
1346 1346 |
1347 1347 o 1:e016b03fd86f (draft) [ ] 1
1348 1348 |
1349 1349 o 0:a78f55e5508c (draft) [ ] 0
1350 1350
1351 1351 $ hg debugbundle .hg/strip-backup/e008cf283490-*-backup.hg
1352 1352 Stream params: {Compression: BZ}
1353 1353 changegroup -- {nbchanges: 1, version: 02}
1354 1354 e008cf2834908e5d6b0f792a9d4b0e2272260fb8
1355 1355 phase-heads -- {}
1356 1356 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 draft
1357 1357
1358 1358 $ hg pull .hg/strip-backup/e008cf283490-*-backup.hg
1359 1359 pulling from .hg/strip-backup/e008cf283490-ede36964-backup.hg
1360 1360 searching for changes
1361 1361 no changes found
1362 1362 $ hg debugobsolete
1363 1363 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1364 1364 $ hg log -G
1365 1365 @ 2:b0551702f918 (draft) [tip ] 2
1366 1366 |
1367 1367 o 1:e016b03fd86f (draft) [ ] 1
1368 1368 |
1369 1369 o 0:a78f55e5508c (draft) [ ] 0
1370 1370
1371 1371 $ hg log -G --hidden
1372 1372 @ 2:b0551702f918 (draft) [tip ] 2
1373 1373 |
1374 1374 o 1:e016b03fd86f (draft) [ ] 1
1375 1375 |
1376 1376 o 0:a78f55e5508c (draft) [ ] 0
1377 1377
1378 1378
1379 1379 Testing that strip remove markers:
1380 1380
1381 1381 $ hg strip -r 1 --config extensions.strip=
1382 1382 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1383 1383 saved backup bundle to $TESTTMP/tmpe/issue4845/.hg/strip-backup/e016b03fd86f-65ede734-backup.hg (glob)
1384 1384 $ hg debugobsolete
1385 1385 $ hg log -G
1386 1386 @ 0:a78f55e5508c (draft) [tip ] 0
1387 1387
1388 1388 $ hg log -G --hidden
1389 1389 @ 0:a78f55e5508c (draft) [tip ] 0
1390 1390
1391 1391 $ hg debugbundle .hg/strip-backup/e016b03fd86f-*-backup.hg
1392 1392 Stream params: {Compression: BZ}
1393 1393 changegroup -- {nbchanges: 2, version: 02}
1394 1394 e016b03fd86fcccc54817d120b90b751aaf367d6
1395 1395 b0551702f918510f01ae838ab03a463054c67b46
1396 1396 obsmarkers -- {}
1397 1397 version: 1 (86 bytes)
1398 1398 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1399 1399 phase-heads -- {}
1400 1400 b0551702f918510f01ae838ab03a463054c67b46 draft
1401 1401
1402 1402 $ hg unbundle .hg/strip-backup/e016b03fd86f-*-backup.hg
1403 1403 adding changesets
1404 1404 adding manifests
1405 1405 adding file changes
1406 1406 added 2 changesets with 2 changes to 2 files
1407 1407 1 new obsolescence markers
1408 1408 new changesets e016b03fd86f:b0551702f918
1409 1409 (run 'hg update' to get a working copy)
1410 1410 $ hg debugobsolete | sort
1411 1411 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1412 1412 $ hg log -G
1413 1413 o 2:b0551702f918 (draft) [tip ] 2
1414 1414 |
1415 1415 o 1:e016b03fd86f (draft) [ ] 1
1416 1416 |
1417 1417 @ 0:a78f55e5508c (draft) [ ] 0
1418 1418
1419 1419 $ hg log -G --hidden
1420 1420 o 2:b0551702f918 (draft) [tip ] 2
1421 1421 |
1422 1422 o 1:e016b03fd86f (draft) [ ] 1
1423 1423 |
1424 1424 @ 0:a78f55e5508c (draft) [ ] 0
1425 1425
1426 1426 Test that 'hg debugobsolete --index --rev' can show indices of obsmarkers when
1427 1427 only a subset of those are displayed (because of --rev option)
1428 1428 $ hg init doindexrev
1429 1429 $ cd doindexrev
1430 1430 $ echo a > a
1431 1431 $ hg ci -Am a
1432 1432 adding a
1433 1433 $ hg ci --amend -m aa
1434 1434 $ echo b > b
1435 1435 $ hg ci -Am b
1436 1436 adding b
1437 1437 $ hg ci --amend -m bb
1438 1438 $ echo c > c
1439 1439 $ hg ci -Am c
1440 1440 adding c
1441 1441 $ hg ci --amend -m cc
1442 1442 $ echo d > d
1443 1443 $ hg ci -Am d
1444 1444 adding d
1445 1445 $ hg ci --amend -m dd --config experimental.evolution.track-operation=1
1446 1446 $ hg debugobsolete --index --rev "3+7"
1447 1447 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1448 1448 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 \(.*\) {'operation': 'amend', 'user': 'test'} (re)
1449 1449 $ hg debugobsolete --index --rev "3+7" -Tjson
1450 1450 [
1451 1451 {
1452 1452 "date": [0.0, 0],
1453 1453 "flag": 0,
1454 1454 "index": 1,
1455 1455 "metadata": {"operation": "amend", "user": "test"},
1456 1456 "prednode": "6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1",
1457 1457 "succnodes": ["d27fb9b066076fd921277a4b9e8b9cb48c95bc6a"]
1458 1458 },
1459 1459 {
1460 1460 "date": [0.0, 0],
1461 1461 "flag": 0,
1462 1462 "index": 3,
1463 1463 "metadata": {"operation": "amend", "user": "test"},
1464 1464 "prednode": "4715cf767440ed891755448016c2b8cf70760c30",
1465 1465 "succnodes": ["7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d"]
1466 1466 }
1467 1467 ]
1468 1468
1469 1469 Test the --delete option of debugobsolete command
1470 1470 $ hg debugobsolete --index
1471 1471 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1472 1472 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1473 1473 2 1ab51af8f9b41ef8c7f6f3312d4706d870b1fb74 29346082e4a9e27042b62d2da0e2de211c027621 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1474 1474 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1475 1475 $ hg debugobsolete --delete 1 --delete 3
1476 1476 deleted 2 obsolescence markers
1477 1477 $ hg debugobsolete
1478 1478 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1479 1479 1ab51af8f9b41ef8c7f6f3312d4706d870b1fb74 29346082e4a9e27042b62d2da0e2de211c027621 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1480 1480
1481 1481 Test adding changeset after obsmarkers affecting it
1482 1482 (eg: during pull, or unbundle)
1483 1483
1484 1484 $ mkcommit e
1485 1485 $ hg bundle -r . --base .~1 ../bundle-2.hg
1486 1486 1 changesets found
1487 1487 $ getid .
1488 1488 $ hg --config extensions.strip= strip -r .
1489 1489 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1490 1490 saved backup bundle to $TESTTMP/tmpe/issue4845/doindexrev/.hg/strip-backup/9bc153528424-ee80edd4-backup.hg (glob)
1491 1491 $ hg debugobsolete 9bc153528424ea266d13e57f9ff0d799dfe61e4b
1492 1492 $ hg unbundle ../bundle-2.hg
1493 1493 adding changesets
1494 1494 adding manifests
1495 1495 adding file changes
1496 1496 added 1 changesets with 1 changes to 1 files
1497 1497 (run 'hg update' to get a working copy)
1498 1498 $ hg log -G
1499 1499 @ 7:7ae79c5d60f0 (draft) [tip ] dd
1500 1500 |
1501 1501 | o 6:4715cf767440 (draft) [ ] d
1502 1502 |/
1503 1503 o 5:29346082e4a9 (draft) [ ] cc
1504 1504 |
1505 1505 o 3:d27fb9b06607 (draft) [ ] bb
1506 1506 |
1507 1507 | o 2:6fdef60fcbab (draft) [ ] b
1508 1508 |/
1509 1509 o 1:f9bd49731b0b (draft) [ ] aa
1510 1510
1511 1511
1512 1512 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now