##// END OF EJS Templates
context: possibly yield initial fctx in blockdescendants()...
Denis Laxalde -
r31992:3e47a40d default
parent child Browse files
Show More
@@ -1,2150 +1,2160
1 1 # context.py - changeset and file context objects for mercurial
2 2 #
3 3 # Copyright 2006, 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 os
12 12 import re
13 13 import stat
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 addednodeid,
18 18 bin,
19 19 hex,
20 20 modifiednodeid,
21 21 nullid,
22 22 nullrev,
23 23 short,
24 24 wdirid,
25 25 wdirnodes,
26 26 )
27 27 from . import (
28 28 encoding,
29 29 error,
30 30 fileset,
31 31 match as matchmod,
32 32 mdiff,
33 33 obsolete as obsmod,
34 34 patch,
35 35 phases,
36 36 pycompat,
37 37 repoview,
38 38 revlog,
39 39 scmutil,
40 40 subrepo,
41 41 util,
42 42 )
43 43
44 44 propertycache = util.propertycache
45 45
46 46 nonascii = re.compile(r'[^\x21-\x7f]').search
47 47
48 48 class basectx(object):
49 49 """A basectx object represents the common logic for its children:
50 50 changectx: read-only context that is already present in the repo,
51 51 workingctx: a context that represents the working directory and can
52 52 be committed,
53 53 memctx: a context that represents changes in-memory and can also
54 54 be committed."""
55 55 def __new__(cls, repo, changeid='', *args, **kwargs):
56 56 if isinstance(changeid, basectx):
57 57 return changeid
58 58
59 59 o = super(basectx, cls).__new__(cls)
60 60
61 61 o._repo = repo
62 62 o._rev = nullrev
63 63 o._node = nullid
64 64
65 65 return o
66 66
67 67 def __str__(self):
68 68 r = short(self.node())
69 69 if pycompat.ispy3:
70 70 return r.decode('ascii')
71 71 return r
72 72
73 73 def __bytes__(self):
74 74 return short(self.node())
75 75
76 76 def __int__(self):
77 77 return self.rev()
78 78
79 79 def __repr__(self):
80 80 return "<%s %s>" % (type(self).__name__, str(self))
81 81
82 82 def __eq__(self, other):
83 83 try:
84 84 return type(self) == type(other) and self._rev == other._rev
85 85 except AttributeError:
86 86 return False
87 87
88 88 def __ne__(self, other):
89 89 return not (self == other)
90 90
91 91 def __contains__(self, key):
92 92 return key in self._manifest
93 93
94 94 def __getitem__(self, key):
95 95 return self.filectx(key)
96 96
97 97 def __iter__(self):
98 98 return iter(self._manifest)
99 99
100 100 def _buildstatusmanifest(self, status):
101 101 """Builds a manifest that includes the given status results, if this is
102 102 a working copy context. For non-working copy contexts, it just returns
103 103 the normal manifest."""
104 104 return self.manifest()
105 105
106 106 def _matchstatus(self, other, match):
107 107 """return match.always if match is none
108 108
109 109 This internal method provides a way for child objects to override the
110 110 match operator.
111 111 """
112 112 return match or matchmod.always(self._repo.root, self._repo.getcwd())
113 113
114 114 def _buildstatus(self, other, s, match, listignored, listclean,
115 115 listunknown):
116 116 """build a status with respect to another context"""
117 117 # Load earliest manifest first for caching reasons. More specifically,
118 118 # if you have revisions 1000 and 1001, 1001 is probably stored as a
119 119 # delta against 1000. Thus, if you read 1000 first, we'll reconstruct
120 120 # 1000 and cache it so that when you read 1001, we just need to apply a
121 121 # delta to what's in the cache. So that's one full reconstruction + one
122 122 # delta application.
123 123 mf2 = None
124 124 if self.rev() is not None and self.rev() < other.rev():
125 125 mf2 = self._buildstatusmanifest(s)
126 126 mf1 = other._buildstatusmanifest(s)
127 127 if mf2 is None:
128 128 mf2 = self._buildstatusmanifest(s)
129 129
130 130 modified, added = [], []
131 131 removed = []
132 132 clean = []
133 133 deleted, unknown, ignored = s.deleted, s.unknown, s.ignored
134 134 deletedset = set(deleted)
135 135 d = mf1.diff(mf2, match=match, clean=listclean)
136 136 for fn, value in d.iteritems():
137 137 if fn in deletedset:
138 138 continue
139 139 if value is None:
140 140 clean.append(fn)
141 141 continue
142 142 (node1, flag1), (node2, flag2) = value
143 143 if node1 is None:
144 144 added.append(fn)
145 145 elif node2 is None:
146 146 removed.append(fn)
147 147 elif flag1 != flag2:
148 148 modified.append(fn)
149 149 elif node2 not in wdirnodes:
150 150 # When comparing files between two commits, we save time by
151 151 # not comparing the file contents when the nodeids differ.
152 152 # Note that this means we incorrectly report a reverted change
153 153 # to a file as a modification.
154 154 modified.append(fn)
155 155 elif self[fn].cmp(other[fn]):
156 156 modified.append(fn)
157 157 else:
158 158 clean.append(fn)
159 159
160 160 if removed:
161 161 # need to filter files if they are already reported as removed
162 162 unknown = [fn for fn in unknown if fn not in mf1 and
163 163 (not match or match(fn))]
164 164 ignored = [fn for fn in ignored if fn not in mf1 and
165 165 (not match or match(fn))]
166 166 # if they're deleted, don't report them as removed
167 167 removed = [fn for fn in removed if fn not in deletedset]
168 168
169 169 return scmutil.status(modified, added, removed, deleted, unknown,
170 170 ignored, clean)
171 171
172 172 @propertycache
173 173 def substate(self):
174 174 return subrepo.state(self, self._repo.ui)
175 175
176 176 def subrev(self, subpath):
177 177 return self.substate[subpath][1]
178 178
179 179 def rev(self):
180 180 return self._rev
181 181 def node(self):
182 182 return self._node
183 183 def hex(self):
184 184 return hex(self.node())
185 185 def manifest(self):
186 186 return self._manifest
187 187 def manifestctx(self):
188 188 return self._manifestctx
189 189 def repo(self):
190 190 return self._repo
191 191 def phasestr(self):
192 192 return phases.phasenames[self.phase()]
193 193 def mutable(self):
194 194 return self.phase() > phases.public
195 195
196 196 def getfileset(self, expr):
197 197 return fileset.getfileset(self, expr)
198 198
199 199 def obsolete(self):
200 200 """True if the changeset is obsolete"""
201 201 return self.rev() in obsmod.getrevs(self._repo, 'obsolete')
202 202
203 203 def extinct(self):
204 204 """True if the changeset is extinct"""
205 205 return self.rev() in obsmod.getrevs(self._repo, 'extinct')
206 206
207 207 def unstable(self):
208 208 """True if the changeset is not obsolete but it's ancestor are"""
209 209 return self.rev() in obsmod.getrevs(self._repo, 'unstable')
210 210
211 211 def bumped(self):
212 212 """True if the changeset try to be a successor of a public changeset
213 213
214 214 Only non-public and non-obsolete changesets may be bumped.
215 215 """
216 216 return self.rev() in obsmod.getrevs(self._repo, 'bumped')
217 217
218 218 def divergent(self):
219 219 """Is a successors of a changeset with multiple possible successors set
220 220
221 221 Only non-public and non-obsolete changesets may be divergent.
222 222 """
223 223 return self.rev() in obsmod.getrevs(self._repo, 'divergent')
224 224
225 225 def troubled(self):
226 226 """True if the changeset is either unstable, bumped or divergent"""
227 227 return self.unstable() or self.bumped() or self.divergent()
228 228
229 229 def troubles(self):
230 230 """return the list of troubles affecting this changesets.
231 231
232 232 Troubles are returned as strings. possible values are:
233 233 - unstable,
234 234 - bumped,
235 235 - divergent.
236 236 """
237 237 troubles = []
238 238 if self.unstable():
239 239 troubles.append('unstable')
240 240 if self.bumped():
241 241 troubles.append('bumped')
242 242 if self.divergent():
243 243 troubles.append('divergent')
244 244 return troubles
245 245
246 246 def parents(self):
247 247 """return contexts for each parent changeset"""
248 248 return self._parents
249 249
250 250 def p1(self):
251 251 return self._parents[0]
252 252
253 253 def p2(self):
254 254 parents = self._parents
255 255 if len(parents) == 2:
256 256 return parents[1]
257 257 return changectx(self._repo, nullrev)
258 258
259 259 def _fileinfo(self, path):
260 260 if '_manifest' in self.__dict__:
261 261 try:
262 262 return self._manifest[path], self._manifest.flags(path)
263 263 except KeyError:
264 264 raise error.ManifestLookupError(self._node, path,
265 265 _('not found in manifest'))
266 266 if '_manifestdelta' in self.__dict__ or path in self.files():
267 267 if path in self._manifestdelta:
268 268 return (self._manifestdelta[path],
269 269 self._manifestdelta.flags(path))
270 270 mfl = self._repo.manifestlog
271 271 try:
272 272 node, flag = mfl[self._changeset.manifest].find(path)
273 273 except KeyError:
274 274 raise error.ManifestLookupError(self._node, path,
275 275 _('not found in manifest'))
276 276
277 277 return node, flag
278 278
279 279 def filenode(self, path):
280 280 return self._fileinfo(path)[0]
281 281
282 282 def flags(self, path):
283 283 try:
284 284 return self._fileinfo(path)[1]
285 285 except error.LookupError:
286 286 return ''
287 287
288 288 def sub(self, path, allowcreate=True):
289 289 '''return a subrepo for the stored revision of path, never wdir()'''
290 290 return subrepo.subrepo(self, path, allowcreate=allowcreate)
291 291
292 292 def nullsub(self, path, pctx):
293 293 return subrepo.nullsubrepo(self, path, pctx)
294 294
295 295 def workingsub(self, path):
296 296 '''return a subrepo for the stored revision, or wdir if this is a wdir
297 297 context.
298 298 '''
299 299 return subrepo.subrepo(self, path, allowwdir=True)
300 300
301 301 def match(self, pats=None, include=None, exclude=None, default='glob',
302 302 listsubrepos=False, badfn=None):
303 303 if pats is None:
304 304 pats = []
305 305 r = self._repo
306 306 return matchmod.match(r.root, r.getcwd(), pats,
307 307 include, exclude, default,
308 308 auditor=r.nofsauditor, ctx=self,
309 309 listsubrepos=listsubrepos, badfn=badfn)
310 310
311 311 def diff(self, ctx2=None, match=None, **opts):
312 312 """Returns a diff generator for the given contexts and matcher"""
313 313 if ctx2 is None:
314 314 ctx2 = self.p1()
315 315 if ctx2 is not None:
316 316 ctx2 = self._repo[ctx2]
317 317 diffopts = patch.diffopts(self._repo.ui, opts)
318 318 return patch.diff(self._repo, ctx2, self, match=match, opts=diffopts)
319 319
320 320 def dirs(self):
321 321 return self._manifest.dirs()
322 322
323 323 def hasdir(self, dir):
324 324 return self._manifest.hasdir(dir)
325 325
326 326 def dirty(self, missing=False, merge=True, branch=True):
327 327 return False
328 328
329 329 def status(self, other=None, match=None, listignored=False,
330 330 listclean=False, listunknown=False, listsubrepos=False):
331 331 """return status of files between two nodes or node and working
332 332 directory.
333 333
334 334 If other is None, compare this node with working directory.
335 335
336 336 returns (modified, added, removed, deleted, unknown, ignored, clean)
337 337 """
338 338
339 339 ctx1 = self
340 340 ctx2 = self._repo[other]
341 341
342 342 # This next code block is, admittedly, fragile logic that tests for
343 343 # reversing the contexts and wouldn't need to exist if it weren't for
344 344 # the fast (and common) code path of comparing the working directory
345 345 # with its first parent.
346 346 #
347 347 # What we're aiming for here is the ability to call:
348 348 #
349 349 # workingctx.status(parentctx)
350 350 #
351 351 # If we always built the manifest for each context and compared those,
352 352 # then we'd be done. But the special case of the above call means we
353 353 # just copy the manifest of the parent.
354 354 reversed = False
355 355 if (not isinstance(ctx1, changectx)
356 356 and isinstance(ctx2, changectx)):
357 357 reversed = True
358 358 ctx1, ctx2 = ctx2, ctx1
359 359
360 360 match = ctx2._matchstatus(ctx1, match)
361 361 r = scmutil.status([], [], [], [], [], [], [])
362 362 r = ctx2._buildstatus(ctx1, r, match, listignored, listclean,
363 363 listunknown)
364 364
365 365 if reversed:
366 366 # Reverse added and removed. Clear deleted, unknown and ignored as
367 367 # these make no sense to reverse.
368 368 r = scmutil.status(r.modified, r.removed, r.added, [], [], [],
369 369 r.clean)
370 370
371 371 if listsubrepos:
372 372 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
373 373 try:
374 374 rev2 = ctx2.subrev(subpath)
375 375 except KeyError:
376 376 # A subrepo that existed in node1 was deleted between
377 377 # node1 and node2 (inclusive). Thus, ctx2's substate
378 378 # won't contain that subpath. The best we can do ignore it.
379 379 rev2 = None
380 380 submatch = matchmod.subdirmatcher(subpath, match)
381 381 s = sub.status(rev2, match=submatch, ignored=listignored,
382 382 clean=listclean, unknown=listunknown,
383 383 listsubrepos=True)
384 384 for rfiles, sfiles in zip(r, s):
385 385 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
386 386
387 387 for l in r:
388 388 l.sort()
389 389
390 390 return r
391 391
392 392
393 393 def makememctx(repo, parents, text, user, date, branch, files, store,
394 394 editor=None, extra=None):
395 395 def getfilectx(repo, memctx, path):
396 396 data, mode, copied = store.getfile(path)
397 397 if data is None:
398 398 return None
399 399 islink, isexec = mode
400 400 return memfilectx(repo, path, data, islink=islink, isexec=isexec,
401 401 copied=copied, memctx=memctx)
402 402 if extra is None:
403 403 extra = {}
404 404 if branch:
405 405 extra['branch'] = encoding.fromlocal(branch)
406 406 ctx = memctx(repo, parents, text, files, getfilectx, user,
407 407 date, extra, editor)
408 408 return ctx
409 409
410 410 class changectx(basectx):
411 411 """A changecontext object makes access to data related to a particular
412 412 changeset convenient. It represents a read-only context already present in
413 413 the repo."""
414 414 def __init__(self, repo, changeid=''):
415 415 """changeid is a revision number, node, or tag"""
416 416
417 417 # since basectx.__new__ already took care of copying the object, we
418 418 # don't need to do anything in __init__, so we just exit here
419 419 if isinstance(changeid, basectx):
420 420 return
421 421
422 422 if changeid == '':
423 423 changeid = '.'
424 424 self._repo = repo
425 425
426 426 try:
427 427 if isinstance(changeid, int):
428 428 self._node = repo.changelog.node(changeid)
429 429 self._rev = changeid
430 430 return
431 431 if not pycompat.ispy3 and isinstance(changeid, long):
432 432 changeid = str(changeid)
433 433 if changeid == 'null':
434 434 self._node = nullid
435 435 self._rev = nullrev
436 436 return
437 437 if changeid == 'tip':
438 438 self._node = repo.changelog.tip()
439 439 self._rev = repo.changelog.rev(self._node)
440 440 return
441 441 if changeid == '.' or changeid == repo.dirstate.p1():
442 442 # this is a hack to delay/avoid loading obsmarkers
443 443 # when we know that '.' won't be hidden
444 444 self._node = repo.dirstate.p1()
445 445 self._rev = repo.unfiltered().changelog.rev(self._node)
446 446 return
447 447 if len(changeid) == 20:
448 448 try:
449 449 self._node = changeid
450 450 self._rev = repo.changelog.rev(changeid)
451 451 return
452 452 except error.FilteredRepoLookupError:
453 453 raise
454 454 except LookupError:
455 455 pass
456 456
457 457 try:
458 458 r = int(changeid)
459 459 if '%d' % r != changeid:
460 460 raise ValueError
461 461 l = len(repo.changelog)
462 462 if r < 0:
463 463 r += l
464 464 if r < 0 or r >= l:
465 465 raise ValueError
466 466 self._rev = r
467 467 self._node = repo.changelog.node(r)
468 468 return
469 469 except error.FilteredIndexError:
470 470 raise
471 471 except (ValueError, OverflowError, IndexError):
472 472 pass
473 473
474 474 if len(changeid) == 40:
475 475 try:
476 476 self._node = bin(changeid)
477 477 self._rev = repo.changelog.rev(self._node)
478 478 return
479 479 except error.FilteredLookupError:
480 480 raise
481 481 except (TypeError, LookupError):
482 482 pass
483 483
484 484 # lookup bookmarks through the name interface
485 485 try:
486 486 self._node = repo.names.singlenode(repo, changeid)
487 487 self._rev = repo.changelog.rev(self._node)
488 488 return
489 489 except KeyError:
490 490 pass
491 491 except error.FilteredRepoLookupError:
492 492 raise
493 493 except error.RepoLookupError:
494 494 pass
495 495
496 496 self._node = repo.unfiltered().changelog._partialmatch(changeid)
497 497 if self._node is not None:
498 498 self._rev = repo.changelog.rev(self._node)
499 499 return
500 500
501 501 # lookup failed
502 502 # check if it might have come from damaged dirstate
503 503 #
504 504 # XXX we could avoid the unfiltered if we had a recognizable
505 505 # exception for filtered changeset access
506 506 if changeid in repo.unfiltered().dirstate.parents():
507 507 msg = _("working directory has unknown parent '%s'!")
508 508 raise error.Abort(msg % short(changeid))
509 509 try:
510 510 if len(changeid) == 20 and nonascii(changeid):
511 511 changeid = hex(changeid)
512 512 except TypeError:
513 513 pass
514 514 except (error.FilteredIndexError, error.FilteredLookupError,
515 515 error.FilteredRepoLookupError):
516 516 if repo.filtername.startswith('visible'):
517 517 msg = _("hidden revision '%s'") % changeid
518 518 hint = _('use --hidden to access hidden revisions')
519 519 raise error.FilteredRepoLookupError(msg, hint=hint)
520 520 msg = _("filtered revision '%s' (not in '%s' subset)")
521 521 msg %= (changeid, repo.filtername)
522 522 raise error.FilteredRepoLookupError(msg)
523 523 except IndexError:
524 524 pass
525 525 raise error.RepoLookupError(
526 526 _("unknown revision '%s'") % changeid)
527 527
528 528 def __hash__(self):
529 529 try:
530 530 return hash(self._rev)
531 531 except AttributeError:
532 532 return id(self)
533 533
534 534 def __nonzero__(self):
535 535 return self._rev != nullrev
536 536
537 537 __bool__ = __nonzero__
538 538
539 539 @propertycache
540 540 def _changeset(self):
541 541 return self._repo.changelog.changelogrevision(self.rev())
542 542
543 543 @propertycache
544 544 def _manifest(self):
545 545 return self._manifestctx.read()
546 546
547 547 @propertycache
548 548 def _manifestctx(self):
549 549 return self._repo.manifestlog[self._changeset.manifest]
550 550
551 551 @propertycache
552 552 def _manifestdelta(self):
553 553 return self._manifestctx.readdelta()
554 554
555 555 @propertycache
556 556 def _parents(self):
557 557 repo = self._repo
558 558 p1, p2 = repo.changelog.parentrevs(self._rev)
559 559 if p2 == nullrev:
560 560 return [changectx(repo, p1)]
561 561 return [changectx(repo, p1), changectx(repo, p2)]
562 562
563 563 def changeset(self):
564 564 c = self._changeset
565 565 return (
566 566 c.manifest,
567 567 c.user,
568 568 c.date,
569 569 c.files,
570 570 c.description,
571 571 c.extra,
572 572 )
573 573 def manifestnode(self):
574 574 return self._changeset.manifest
575 575
576 576 def user(self):
577 577 return self._changeset.user
578 578 def date(self):
579 579 return self._changeset.date
580 580 def files(self):
581 581 return self._changeset.files
582 582 def description(self):
583 583 return self._changeset.description
584 584 def branch(self):
585 585 return encoding.tolocal(self._changeset.extra.get("branch"))
586 586 def closesbranch(self):
587 587 return 'close' in self._changeset.extra
588 588 def extra(self):
589 589 return self._changeset.extra
590 590 def tags(self):
591 591 return self._repo.nodetags(self._node)
592 592 def bookmarks(self):
593 593 return self._repo.nodebookmarks(self._node)
594 594 def phase(self):
595 595 return self._repo._phasecache.phase(self._repo, self._rev)
596 596 def hidden(self):
597 597 return self._rev in repoview.filterrevs(self._repo, 'visible')
598 598
599 599 def children(self):
600 600 """return contexts for each child changeset"""
601 601 c = self._repo.changelog.children(self._node)
602 602 return [changectx(self._repo, x) for x in c]
603 603
604 604 def ancestors(self):
605 605 for a in self._repo.changelog.ancestors([self._rev]):
606 606 yield changectx(self._repo, a)
607 607
608 608 def descendants(self):
609 609 for d in self._repo.changelog.descendants([self._rev]):
610 610 yield changectx(self._repo, d)
611 611
612 612 def filectx(self, path, fileid=None, filelog=None):
613 613 """get a file context from this changeset"""
614 614 if fileid is None:
615 615 fileid = self.filenode(path)
616 616 return filectx(self._repo, path, fileid=fileid,
617 617 changectx=self, filelog=filelog)
618 618
619 619 def ancestor(self, c2, warn=False):
620 620 """return the "best" ancestor context of self and c2
621 621
622 622 If there are multiple candidates, it will show a message and check
623 623 merge.preferancestor configuration before falling back to the
624 624 revlog ancestor."""
625 625 # deal with workingctxs
626 626 n2 = c2._node
627 627 if n2 is None:
628 628 n2 = c2._parents[0]._node
629 629 cahs = self._repo.changelog.commonancestorsheads(self._node, n2)
630 630 if not cahs:
631 631 anc = nullid
632 632 elif len(cahs) == 1:
633 633 anc = cahs[0]
634 634 else:
635 635 # experimental config: merge.preferancestor
636 636 for r in self._repo.ui.configlist('merge', 'preferancestor', ['*']):
637 637 try:
638 638 ctx = changectx(self._repo, r)
639 639 except error.RepoLookupError:
640 640 continue
641 641 anc = ctx.node()
642 642 if anc in cahs:
643 643 break
644 644 else:
645 645 anc = self._repo.changelog.ancestor(self._node, n2)
646 646 if warn:
647 647 self._repo.ui.status(
648 648 (_("note: using %s as ancestor of %s and %s\n") %
649 649 (short(anc), short(self._node), short(n2))) +
650 650 ''.join(_(" alternatively, use --config "
651 651 "merge.preferancestor=%s\n") %
652 652 short(n) for n in sorted(cahs) if n != anc))
653 653 return changectx(self._repo, anc)
654 654
655 655 def descendant(self, other):
656 656 """True if other is descendant of this changeset"""
657 657 return self._repo.changelog.descendant(self._rev, other._rev)
658 658
659 659 def walk(self, match):
660 660 '''Generates matching file names.'''
661 661
662 662 # Wrap match.bad method to have message with nodeid
663 663 def bad(fn, msg):
664 664 # The manifest doesn't know about subrepos, so don't complain about
665 665 # paths into valid subrepos.
666 666 if any(fn == s or fn.startswith(s + '/')
667 667 for s in self.substate):
668 668 return
669 669 match.bad(fn, _('no such file in rev %s') % self)
670 670
671 671 m = matchmod.badmatch(match, bad)
672 672 return self._manifest.walk(m)
673 673
674 674 def matches(self, match):
675 675 return self.walk(match)
676 676
677 677 class basefilectx(object):
678 678 """A filecontext object represents the common logic for its children:
679 679 filectx: read-only access to a filerevision that is already present
680 680 in the repo,
681 681 workingfilectx: a filecontext that represents files from the working
682 682 directory,
683 683 memfilectx: a filecontext that represents files in-memory."""
684 684 def __new__(cls, repo, path, *args, **kwargs):
685 685 return super(basefilectx, cls).__new__(cls)
686 686
687 687 @propertycache
688 688 def _filelog(self):
689 689 return self._repo.file(self._path)
690 690
691 691 @propertycache
692 692 def _changeid(self):
693 693 if '_changeid' in self.__dict__:
694 694 return self._changeid
695 695 elif '_changectx' in self.__dict__:
696 696 return self._changectx.rev()
697 697 elif '_descendantrev' in self.__dict__:
698 698 # this file context was created from a revision with a known
699 699 # descendant, we can (lazily) correct for linkrev aliases
700 700 return self._adjustlinkrev(self._descendantrev)
701 701 else:
702 702 return self._filelog.linkrev(self._filerev)
703 703
704 704 @propertycache
705 705 def _filenode(self):
706 706 if '_fileid' in self.__dict__:
707 707 return self._filelog.lookup(self._fileid)
708 708 else:
709 709 return self._changectx.filenode(self._path)
710 710
711 711 @propertycache
712 712 def _filerev(self):
713 713 return self._filelog.rev(self._filenode)
714 714
715 715 @propertycache
716 716 def _repopath(self):
717 717 return self._path
718 718
719 719 def __nonzero__(self):
720 720 try:
721 721 self._filenode
722 722 return True
723 723 except error.LookupError:
724 724 # file is missing
725 725 return False
726 726
727 727 __bool__ = __nonzero__
728 728
729 729 def __str__(self):
730 730 try:
731 731 return "%s@%s" % (self.path(), self._changectx)
732 732 except error.LookupError:
733 733 return "%s@???" % self.path()
734 734
735 735 def __repr__(self):
736 736 return "<%s %s>" % (type(self).__name__, str(self))
737 737
738 738 def __hash__(self):
739 739 try:
740 740 return hash((self._path, self._filenode))
741 741 except AttributeError:
742 742 return id(self)
743 743
744 744 def __eq__(self, other):
745 745 try:
746 746 return (type(self) == type(other) and self._path == other._path
747 747 and self._filenode == other._filenode)
748 748 except AttributeError:
749 749 return False
750 750
751 751 def __ne__(self, other):
752 752 return not (self == other)
753 753
754 754 def filerev(self):
755 755 return self._filerev
756 756 def filenode(self):
757 757 return self._filenode
758 758 def flags(self):
759 759 return self._changectx.flags(self._path)
760 760 def filelog(self):
761 761 return self._filelog
762 762 def rev(self):
763 763 return self._changeid
764 764 def linkrev(self):
765 765 return self._filelog.linkrev(self._filerev)
766 766 def node(self):
767 767 return self._changectx.node()
768 768 def hex(self):
769 769 return self._changectx.hex()
770 770 def user(self):
771 771 return self._changectx.user()
772 772 def date(self):
773 773 return self._changectx.date()
774 774 def files(self):
775 775 return self._changectx.files()
776 776 def description(self):
777 777 return self._changectx.description()
778 778 def branch(self):
779 779 return self._changectx.branch()
780 780 def extra(self):
781 781 return self._changectx.extra()
782 782 def phase(self):
783 783 return self._changectx.phase()
784 784 def phasestr(self):
785 785 return self._changectx.phasestr()
786 786 def manifest(self):
787 787 return self._changectx.manifest()
788 788 def changectx(self):
789 789 return self._changectx
790 790 def repo(self):
791 791 return self._repo
792 792
793 793 def path(self):
794 794 return self._path
795 795
796 796 def isbinary(self):
797 797 try:
798 798 return util.binary(self.data())
799 799 except IOError:
800 800 return False
801 801 def isexec(self):
802 802 return 'x' in self.flags()
803 803 def islink(self):
804 804 return 'l' in self.flags()
805 805
806 806 def isabsent(self):
807 807 """whether this filectx represents a file not in self._changectx
808 808
809 809 This is mainly for merge code to detect change/delete conflicts. This is
810 810 expected to be True for all subclasses of basectx."""
811 811 return False
812 812
813 813 _customcmp = False
814 814 def cmp(self, fctx):
815 815 """compare with other file context
816 816
817 817 returns True if different than fctx.
818 818 """
819 819 if fctx._customcmp:
820 820 return fctx.cmp(self)
821 821
822 822 if (fctx._filenode is None
823 823 and (self._repo._encodefilterpats
824 824 # if file data starts with '\1\n', empty metadata block is
825 825 # prepended, which adds 4 bytes to filelog.size().
826 826 or self.size() - 4 == fctx.size())
827 827 or self.size() == fctx.size()):
828 828 return self._filelog.cmp(self._filenode, fctx.data())
829 829
830 830 return True
831 831
832 832 def _adjustlinkrev(self, srcrev, inclusive=False):
833 833 """return the first ancestor of <srcrev> introducing <fnode>
834 834
835 835 If the linkrev of the file revision does not point to an ancestor of
836 836 srcrev, we'll walk down the ancestors until we find one introducing
837 837 this file revision.
838 838
839 839 :srcrev: the changeset revision we search ancestors from
840 840 :inclusive: if true, the src revision will also be checked
841 841 """
842 842 repo = self._repo
843 843 cl = repo.unfiltered().changelog
844 844 mfl = repo.manifestlog
845 845 # fetch the linkrev
846 846 lkr = self.linkrev()
847 847 # hack to reuse ancestor computation when searching for renames
848 848 memberanc = getattr(self, '_ancestrycontext', None)
849 849 iteranc = None
850 850 if srcrev is None:
851 851 # wctx case, used by workingfilectx during mergecopy
852 852 revs = [p.rev() for p in self._repo[None].parents()]
853 853 inclusive = True # we skipped the real (revless) source
854 854 else:
855 855 revs = [srcrev]
856 856 if memberanc is None:
857 857 memberanc = iteranc = cl.ancestors(revs, lkr,
858 858 inclusive=inclusive)
859 859 # check if this linkrev is an ancestor of srcrev
860 860 if lkr not in memberanc:
861 861 if iteranc is None:
862 862 iteranc = cl.ancestors(revs, lkr, inclusive=inclusive)
863 863 fnode = self._filenode
864 864 path = self._path
865 865 for a in iteranc:
866 866 ac = cl.read(a) # get changeset data (we avoid object creation)
867 867 if path in ac[3]: # checking the 'files' field.
868 868 # The file has been touched, check if the content is
869 869 # similar to the one we search for.
870 870 if fnode == mfl[ac[0]].readfast().get(path):
871 871 return a
872 872 # In theory, we should never get out of that loop without a result.
873 873 # But if manifest uses a buggy file revision (not children of the
874 874 # one it replaces) we could. Such a buggy situation will likely
875 875 # result is crash somewhere else at to some point.
876 876 return lkr
877 877
878 878 def introrev(self):
879 879 """return the rev of the changeset which introduced this file revision
880 880
881 881 This method is different from linkrev because it take into account the
882 882 changeset the filectx was created from. It ensures the returned
883 883 revision is one of its ancestors. This prevents bugs from
884 884 'linkrev-shadowing' when a file revision is used by multiple
885 885 changesets.
886 886 """
887 887 lkr = self.linkrev()
888 888 attrs = vars(self)
889 889 noctx = not ('_changeid' in attrs or '_changectx' in attrs)
890 890 if noctx or self.rev() == lkr:
891 891 return self.linkrev()
892 892 return self._adjustlinkrev(self.rev(), inclusive=True)
893 893
894 894 def _parentfilectx(self, path, fileid, filelog):
895 895 """create parent filectx keeping ancestry info for _adjustlinkrev()"""
896 896 fctx = filectx(self._repo, path, fileid=fileid, filelog=filelog)
897 897 if '_changeid' in vars(self) or '_changectx' in vars(self):
898 898 # If self is associated with a changeset (probably explicitly
899 899 # fed), ensure the created filectx is associated with a
900 900 # changeset that is an ancestor of self.changectx.
901 901 # This lets us later use _adjustlinkrev to get a correct link.
902 902 fctx._descendantrev = self.rev()
903 903 fctx._ancestrycontext = getattr(self, '_ancestrycontext', None)
904 904 elif '_descendantrev' in vars(self):
905 905 # Otherwise propagate _descendantrev if we have one associated.
906 906 fctx._descendantrev = self._descendantrev
907 907 fctx._ancestrycontext = getattr(self, '_ancestrycontext', None)
908 908 return fctx
909 909
910 910 def parents(self):
911 911 _path = self._path
912 912 fl = self._filelog
913 913 parents = self._filelog.parents(self._filenode)
914 914 pl = [(_path, node, fl) for node in parents if node != nullid]
915 915
916 916 r = fl.renamed(self._filenode)
917 917 if r:
918 918 # - In the simple rename case, both parent are nullid, pl is empty.
919 919 # - In case of merge, only one of the parent is null id and should
920 920 # be replaced with the rename information. This parent is -always-
921 921 # the first one.
922 922 #
923 923 # As null id have always been filtered out in the previous list
924 924 # comprehension, inserting to 0 will always result in "replacing
925 925 # first nullid parent with rename information.
926 926 pl.insert(0, (r[0], r[1], self._repo.file(r[0])))
927 927
928 928 return [self._parentfilectx(path, fnode, l) for path, fnode, l in pl]
929 929
930 930 def p1(self):
931 931 return self.parents()[0]
932 932
933 933 def p2(self):
934 934 p = self.parents()
935 935 if len(p) == 2:
936 936 return p[1]
937 937 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
938 938
939 939 def annotate(self, follow=False, linenumber=False, diffopts=None):
940 940 '''returns a list of tuples of ((ctx, number), line) for each line
941 941 in the file, where ctx is the filectx of the node where
942 942 that line was last changed; if linenumber parameter is true, number is
943 943 the line number at the first appearance in the managed file, otherwise,
944 944 number has a fixed value of False.
945 945 '''
946 946
947 947 def lines(text):
948 948 if text.endswith("\n"):
949 949 return text.count("\n")
950 950 return text.count("\n") + int(bool(text))
951 951
952 952 if linenumber:
953 953 def decorate(text, rev):
954 954 return ([(rev, i) for i in xrange(1, lines(text) + 1)], text)
955 955 else:
956 956 def decorate(text, rev):
957 957 return ([(rev, False)] * lines(text), text)
958 958
959 959 def pair(parent, child):
960 960 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts)
961 961 for (a1, a2, b1, b2), t in blocks:
962 962 # Changed blocks ('!') or blocks made only of blank lines ('~')
963 963 # belong to the child.
964 964 if t == '=':
965 965 child[0][b1:b2] = parent[0][a1:a2]
966 966 return child
967 967
968 968 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
969 969
970 970 def parents(f):
971 971 # Cut _descendantrev here to mitigate the penalty of lazy linkrev
972 972 # adjustment. Otherwise, p._adjustlinkrev() would walk changelog
973 973 # from the topmost introrev (= srcrev) down to p.linkrev() if it
974 974 # isn't an ancestor of the srcrev.
975 975 f._changeid
976 976 pl = f.parents()
977 977
978 978 # Don't return renamed parents if we aren't following.
979 979 if not follow:
980 980 pl = [p for p in pl if p.path() == f.path()]
981 981
982 982 # renamed filectx won't have a filelog yet, so set it
983 983 # from the cache to save time
984 984 for p in pl:
985 985 if not '_filelog' in p.__dict__:
986 986 p._filelog = getlog(p.path())
987 987
988 988 return pl
989 989
990 990 # use linkrev to find the first changeset where self appeared
991 991 base = self
992 992 introrev = self.introrev()
993 993 if self.rev() != introrev:
994 994 base = self.filectx(self.filenode(), changeid=introrev)
995 995 if getattr(base, '_ancestrycontext', None) is None:
996 996 cl = self._repo.changelog
997 997 if introrev is None:
998 998 # wctx is not inclusive, but works because _ancestrycontext
999 999 # is used to test filelog revisions
1000 1000 ac = cl.ancestors([p.rev() for p in base.parents()],
1001 1001 inclusive=True)
1002 1002 else:
1003 1003 ac = cl.ancestors([introrev], inclusive=True)
1004 1004 base._ancestrycontext = ac
1005 1005
1006 1006 # This algorithm would prefer to be recursive, but Python is a
1007 1007 # bit recursion-hostile. Instead we do an iterative
1008 1008 # depth-first search.
1009 1009
1010 1010 # 1st DFS pre-calculates pcache and needed
1011 1011 visit = [base]
1012 1012 pcache = {}
1013 1013 needed = {base: 1}
1014 1014 while visit:
1015 1015 f = visit.pop()
1016 1016 if f in pcache:
1017 1017 continue
1018 1018 pl = parents(f)
1019 1019 pcache[f] = pl
1020 1020 for p in pl:
1021 1021 needed[p] = needed.get(p, 0) + 1
1022 1022 if p not in pcache:
1023 1023 visit.append(p)
1024 1024
1025 1025 # 2nd DFS does the actual annotate
1026 1026 visit[:] = [base]
1027 1027 hist = {}
1028 1028 while visit:
1029 1029 f = visit[-1]
1030 1030 if f in hist:
1031 1031 visit.pop()
1032 1032 continue
1033 1033
1034 1034 ready = True
1035 1035 pl = pcache[f]
1036 1036 for p in pl:
1037 1037 if p not in hist:
1038 1038 ready = False
1039 1039 visit.append(p)
1040 1040 if ready:
1041 1041 visit.pop()
1042 1042 curr = decorate(f.data(), f)
1043 1043 for p in pl:
1044 1044 curr = pair(hist[p], curr)
1045 1045 if needed[p] == 1:
1046 1046 del hist[p]
1047 1047 del needed[p]
1048 1048 else:
1049 1049 needed[p] -= 1
1050 1050
1051 1051 hist[f] = curr
1052 1052 del pcache[f]
1053 1053
1054 1054 return zip(hist[base][0], hist[base][1].splitlines(True))
1055 1055
1056 1056 def ancestors(self, followfirst=False):
1057 1057 visit = {}
1058 1058 c = self
1059 1059 if followfirst:
1060 1060 cut = 1
1061 1061 else:
1062 1062 cut = None
1063 1063
1064 1064 while True:
1065 1065 for parent in c.parents()[:cut]:
1066 1066 visit[(parent.linkrev(), parent.filenode())] = parent
1067 1067 if not visit:
1068 1068 break
1069 1069 c = visit.pop(max(visit))
1070 1070 yield c
1071 1071
1072 1072 class filectx(basefilectx):
1073 1073 """A filecontext object makes access to data related to a particular
1074 1074 filerevision convenient."""
1075 1075 def __init__(self, repo, path, changeid=None, fileid=None,
1076 1076 filelog=None, changectx=None):
1077 1077 """changeid can be a changeset revision, node, or tag.
1078 1078 fileid can be a file revision or node."""
1079 1079 self._repo = repo
1080 1080 self._path = path
1081 1081
1082 1082 assert (changeid is not None
1083 1083 or fileid is not None
1084 1084 or changectx is not None), \
1085 1085 ("bad args: changeid=%r, fileid=%r, changectx=%r"
1086 1086 % (changeid, fileid, changectx))
1087 1087
1088 1088 if filelog is not None:
1089 1089 self._filelog = filelog
1090 1090
1091 1091 if changeid is not None:
1092 1092 self._changeid = changeid
1093 1093 if changectx is not None:
1094 1094 self._changectx = changectx
1095 1095 if fileid is not None:
1096 1096 self._fileid = fileid
1097 1097
1098 1098 @propertycache
1099 1099 def _changectx(self):
1100 1100 try:
1101 1101 return changectx(self._repo, self._changeid)
1102 1102 except error.FilteredRepoLookupError:
1103 1103 # Linkrev may point to any revision in the repository. When the
1104 1104 # repository is filtered this may lead to `filectx` trying to build
1105 1105 # `changectx` for filtered revision. In such case we fallback to
1106 1106 # creating `changectx` on the unfiltered version of the reposition.
1107 1107 # This fallback should not be an issue because `changectx` from
1108 1108 # `filectx` are not used in complex operations that care about
1109 1109 # filtering.
1110 1110 #
1111 1111 # This fallback is a cheap and dirty fix that prevent several
1112 1112 # crashes. It does not ensure the behavior is correct. However the
1113 1113 # behavior was not correct before filtering either and "incorrect
1114 1114 # behavior" is seen as better as "crash"
1115 1115 #
1116 1116 # Linkrevs have several serious troubles with filtering that are
1117 1117 # complicated to solve. Proper handling of the issue here should be
1118 1118 # considered when solving linkrev issue are on the table.
1119 1119 return changectx(self._repo.unfiltered(), self._changeid)
1120 1120
1121 1121 def filectx(self, fileid, changeid=None):
1122 1122 '''opens an arbitrary revision of the file without
1123 1123 opening a new filelog'''
1124 1124 return filectx(self._repo, self._path, fileid=fileid,
1125 1125 filelog=self._filelog, changeid=changeid)
1126 1126
1127 1127 def rawdata(self):
1128 1128 return self._filelog.revision(self._filenode, raw=True)
1129 1129
1130 1130 def data(self):
1131 1131 try:
1132 1132 return self._filelog.read(self._filenode)
1133 1133 except error.CensoredNodeError:
1134 1134 if self._repo.ui.config("censor", "policy", "abort") == "ignore":
1135 1135 return ""
1136 1136 raise error.Abort(_("censored node: %s") % short(self._filenode),
1137 1137 hint=_("set censor.policy to ignore errors"))
1138 1138
1139 1139 def size(self):
1140 1140 return self._filelog.size(self._filerev)
1141 1141
1142 1142 def renamed(self):
1143 1143 """check if file was actually renamed in this changeset revision
1144 1144
1145 1145 If rename logged in file revision, we report copy for changeset only
1146 1146 if file revisions linkrev points back to the changeset in question
1147 1147 or both changeset parents contain different file revisions.
1148 1148 """
1149 1149
1150 1150 renamed = self._filelog.renamed(self._filenode)
1151 1151 if not renamed:
1152 1152 return renamed
1153 1153
1154 1154 if self.rev() == self.linkrev():
1155 1155 return renamed
1156 1156
1157 1157 name = self.path()
1158 1158 fnode = self._filenode
1159 1159 for p in self._changectx.parents():
1160 1160 try:
1161 1161 if fnode == p.filenode(name):
1162 1162 return None
1163 1163 except error.LookupError:
1164 1164 pass
1165 1165 return renamed
1166 1166
1167 1167 def children(self):
1168 1168 # hard for renames
1169 1169 c = self._filelog.children(self._filenode)
1170 1170 return [filectx(self._repo, self._path, fileid=x,
1171 1171 filelog=self._filelog) for x in c]
1172 1172
1173 1173 def _changesrange(fctx1, fctx2, linerange2, diffopts):
1174 1174 """Return `(diffinrange, linerange1)` where `diffinrange` is True
1175 1175 if diff from fctx2 to fctx1 has changes in linerange2 and
1176 1176 `linerange1` is the new line range for fctx1.
1177 1177 """
1178 1178 blocks = mdiff.allblocks(fctx1.data(), fctx2.data(), diffopts)
1179 1179 filteredblocks, linerange1 = mdiff.blocksinrange(blocks, linerange2)
1180 1180 diffinrange = any(stype == '!' for _, stype in filteredblocks)
1181 1181 return diffinrange, linerange1
1182 1182
1183 1183 def blockancestors(fctx, fromline, toline, followfirst=False):
1184 1184 """Yield ancestors of `fctx` with respect to the block of lines within
1185 1185 `fromline`-`toline` range.
1186 1186 """
1187 1187 diffopts = patch.diffopts(fctx._repo.ui)
1188 1188 visit = {(fctx.linkrev(), fctx.filenode()): (fctx, (fromline, toline))}
1189 1189 while visit:
1190 1190 c, linerange2 = visit.pop(max(visit))
1191 1191 pl = c.parents()
1192 1192 if followfirst:
1193 1193 pl = pl[:1]
1194 1194 if not pl:
1195 1195 # The block originates from the initial revision.
1196 1196 yield c, linerange2
1197 1197 continue
1198 1198 inrange = False
1199 1199 for p in pl:
1200 1200 inrangep, linerange1 = _changesrange(p, c, linerange2, diffopts)
1201 1201 inrange = inrange or inrangep
1202 1202 if linerange1[0] == linerange1[1]:
1203 1203 # Parent's linerange is empty, meaning that the block got
1204 1204 # introduced in this revision; no need to go futher in this
1205 1205 # branch.
1206 1206 continue
1207 1207 visit[p.linkrev(), p.filenode()] = p, linerange1
1208 1208 if inrange:
1209 1209 yield c, linerange2
1210 1210
1211 1211 def blockdescendants(fctx, fromline, toline):
1212 1212 """Yield descendants of `fctx` with respect to the block of lines within
1213 1213 `fromline`-`toline` range.
1214 1214 """
1215 # First possibly yield 'fctx' if it has changes in range with respect to
1216 # its parents.
1217 try:
1218 c, linerange1 = next(blockancestors(fctx, fromline, toline))
1219 except StopIteration:
1220 pass
1221 else:
1222 if c == fctx:
1223 yield c, linerange1
1224
1215 1225 diffopts = patch.diffopts(fctx._repo.ui)
1216 1226 fl = fctx.filelog()
1217 1227 seen = {fctx.filerev(): (fctx, (fromline, toline))}
1218 1228 for i in fl.descendants([fctx.filerev()]):
1219 1229 c = fctx.filectx(i)
1220 1230 inrange = False
1221 1231 for x in fl.parentrevs(i):
1222 1232 try:
1223 1233 p, linerange2 = seen[x]
1224 1234 except KeyError:
1225 1235 # nullrev or other branch
1226 1236 continue
1227 1237 inrangep, linerange1 = _changesrange(c, p, linerange2, diffopts)
1228 1238 inrange = inrange or inrangep
1229 1239 # If revision 'i' has been seen (it's a merge), we assume that its
1230 1240 # line range is the same independently of which parents was used
1231 1241 # to compute it.
1232 1242 assert i not in seen or seen[i][1] == linerange1, (
1233 1243 'computed line range for %s is not consistent between '
1234 1244 'ancestor branches' % c)
1235 1245 seen[i] = c, linerange1
1236 1246 if inrange:
1237 1247 yield c, linerange1
1238 1248
1239 1249 class committablectx(basectx):
1240 1250 """A committablectx object provides common functionality for a context that
1241 1251 wants the ability to commit, e.g. workingctx or memctx."""
1242 1252 def __init__(self, repo, text="", user=None, date=None, extra=None,
1243 1253 changes=None):
1244 1254 self._repo = repo
1245 1255 self._rev = None
1246 1256 self._node = None
1247 1257 self._text = text
1248 1258 if date:
1249 1259 self._date = util.parsedate(date)
1250 1260 if user:
1251 1261 self._user = user
1252 1262 if changes:
1253 1263 self._status = changes
1254 1264
1255 1265 self._extra = {}
1256 1266 if extra:
1257 1267 self._extra = extra.copy()
1258 1268 if 'branch' not in self._extra:
1259 1269 try:
1260 1270 branch = encoding.fromlocal(self._repo.dirstate.branch())
1261 1271 except UnicodeDecodeError:
1262 1272 raise error.Abort(_('branch name not in UTF-8!'))
1263 1273 self._extra['branch'] = branch
1264 1274 if self._extra['branch'] == '':
1265 1275 self._extra['branch'] = 'default'
1266 1276
1267 1277 def __str__(self):
1268 1278 return str(self._parents[0]) + "+"
1269 1279
1270 1280 def __nonzero__(self):
1271 1281 return True
1272 1282
1273 1283 __bool__ = __nonzero__
1274 1284
1275 1285 def _buildflagfunc(self):
1276 1286 # Create a fallback function for getting file flags when the
1277 1287 # filesystem doesn't support them
1278 1288
1279 1289 copiesget = self._repo.dirstate.copies().get
1280 1290 parents = self.parents()
1281 1291 if len(parents) < 2:
1282 1292 # when we have one parent, it's easy: copy from parent
1283 1293 man = parents[0].manifest()
1284 1294 def func(f):
1285 1295 f = copiesget(f, f)
1286 1296 return man.flags(f)
1287 1297 else:
1288 1298 # merges are tricky: we try to reconstruct the unstored
1289 1299 # result from the merge (issue1802)
1290 1300 p1, p2 = parents
1291 1301 pa = p1.ancestor(p2)
1292 1302 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
1293 1303
1294 1304 def func(f):
1295 1305 f = copiesget(f, f) # may be wrong for merges with copies
1296 1306 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
1297 1307 if fl1 == fl2:
1298 1308 return fl1
1299 1309 if fl1 == fla:
1300 1310 return fl2
1301 1311 if fl2 == fla:
1302 1312 return fl1
1303 1313 return '' # punt for conflicts
1304 1314
1305 1315 return func
1306 1316
1307 1317 @propertycache
1308 1318 def _flagfunc(self):
1309 1319 return self._repo.dirstate.flagfunc(self._buildflagfunc)
1310 1320
1311 1321 @propertycache
1312 1322 def _status(self):
1313 1323 return self._repo.status()
1314 1324
1315 1325 @propertycache
1316 1326 def _user(self):
1317 1327 return self._repo.ui.username()
1318 1328
1319 1329 @propertycache
1320 1330 def _date(self):
1321 1331 return util.makedate()
1322 1332
1323 1333 def subrev(self, subpath):
1324 1334 return None
1325 1335
1326 1336 def manifestnode(self):
1327 1337 return None
1328 1338 def user(self):
1329 1339 return self._user or self._repo.ui.username()
1330 1340 def date(self):
1331 1341 return self._date
1332 1342 def description(self):
1333 1343 return self._text
1334 1344 def files(self):
1335 1345 return sorted(self._status.modified + self._status.added +
1336 1346 self._status.removed)
1337 1347
1338 1348 def modified(self):
1339 1349 return self._status.modified
1340 1350 def added(self):
1341 1351 return self._status.added
1342 1352 def removed(self):
1343 1353 return self._status.removed
1344 1354 def deleted(self):
1345 1355 return self._status.deleted
1346 1356 def branch(self):
1347 1357 return encoding.tolocal(self._extra['branch'])
1348 1358 def closesbranch(self):
1349 1359 return 'close' in self._extra
1350 1360 def extra(self):
1351 1361 return self._extra
1352 1362
1353 1363 def tags(self):
1354 1364 return []
1355 1365
1356 1366 def bookmarks(self):
1357 1367 b = []
1358 1368 for p in self.parents():
1359 1369 b.extend(p.bookmarks())
1360 1370 return b
1361 1371
1362 1372 def phase(self):
1363 1373 phase = phases.draft # default phase to draft
1364 1374 for p in self.parents():
1365 1375 phase = max(phase, p.phase())
1366 1376 return phase
1367 1377
1368 1378 def hidden(self):
1369 1379 return False
1370 1380
1371 1381 def children(self):
1372 1382 return []
1373 1383
1374 1384 def flags(self, path):
1375 1385 if '_manifest' in self.__dict__:
1376 1386 try:
1377 1387 return self._manifest.flags(path)
1378 1388 except KeyError:
1379 1389 return ''
1380 1390
1381 1391 try:
1382 1392 return self._flagfunc(path)
1383 1393 except OSError:
1384 1394 return ''
1385 1395
1386 1396 def ancestor(self, c2):
1387 1397 """return the "best" ancestor context of self and c2"""
1388 1398 return self._parents[0].ancestor(c2) # punt on two parents for now
1389 1399
1390 1400 def walk(self, match):
1391 1401 '''Generates matching file names.'''
1392 1402 return sorted(self._repo.dirstate.walk(match, sorted(self.substate),
1393 1403 True, False))
1394 1404
1395 1405 def matches(self, match):
1396 1406 return sorted(self._repo.dirstate.matches(match))
1397 1407
1398 1408 def ancestors(self):
1399 1409 for p in self._parents:
1400 1410 yield p
1401 1411 for a in self._repo.changelog.ancestors(
1402 1412 [p.rev() for p in self._parents]):
1403 1413 yield changectx(self._repo, a)
1404 1414
1405 1415 def markcommitted(self, node):
1406 1416 """Perform post-commit cleanup necessary after committing this ctx
1407 1417
1408 1418 Specifically, this updates backing stores this working context
1409 1419 wraps to reflect the fact that the changes reflected by this
1410 1420 workingctx have been committed. For example, it marks
1411 1421 modified and added files as normal in the dirstate.
1412 1422
1413 1423 """
1414 1424
1415 1425 self._repo.dirstate.beginparentchange()
1416 1426 for f in self.modified() + self.added():
1417 1427 self._repo.dirstate.normal(f)
1418 1428 for f in self.removed():
1419 1429 self._repo.dirstate.drop(f)
1420 1430 self._repo.dirstate.setparents(node)
1421 1431 self._repo.dirstate.endparentchange()
1422 1432
1423 1433 # write changes out explicitly, because nesting wlock at
1424 1434 # runtime may prevent 'wlock.release()' in 'repo.commit()'
1425 1435 # from immediately doing so for subsequent changing files
1426 1436 self._repo.dirstate.write(self._repo.currenttransaction())
1427 1437
1428 1438 class workingctx(committablectx):
1429 1439 """A workingctx object makes access to data related to
1430 1440 the current working directory convenient.
1431 1441 date - any valid date string or (unixtime, offset), or None.
1432 1442 user - username string, or None.
1433 1443 extra - a dictionary of extra values, or None.
1434 1444 changes - a list of file lists as returned by localrepo.status()
1435 1445 or None to use the repository status.
1436 1446 """
1437 1447 def __init__(self, repo, text="", user=None, date=None, extra=None,
1438 1448 changes=None):
1439 1449 super(workingctx, self).__init__(repo, text, user, date, extra, changes)
1440 1450
1441 1451 def __iter__(self):
1442 1452 d = self._repo.dirstate
1443 1453 for f in d:
1444 1454 if d[f] != 'r':
1445 1455 yield f
1446 1456
1447 1457 def __contains__(self, key):
1448 1458 return self._repo.dirstate[key] not in "?r"
1449 1459
1450 1460 def hex(self):
1451 1461 return hex(wdirid)
1452 1462
1453 1463 @propertycache
1454 1464 def _parents(self):
1455 1465 p = self._repo.dirstate.parents()
1456 1466 if p[1] == nullid:
1457 1467 p = p[:-1]
1458 1468 return [changectx(self._repo, x) for x in p]
1459 1469
1460 1470 def filectx(self, path, filelog=None):
1461 1471 """get a file context from the working directory"""
1462 1472 return workingfilectx(self._repo, path, workingctx=self,
1463 1473 filelog=filelog)
1464 1474
1465 1475 def dirty(self, missing=False, merge=True, branch=True):
1466 1476 "check whether a working directory is modified"
1467 1477 # check subrepos first
1468 1478 for s in sorted(self.substate):
1469 1479 if self.sub(s).dirty():
1470 1480 return True
1471 1481 # check current working dir
1472 1482 return ((merge and self.p2()) or
1473 1483 (branch and self.branch() != self.p1().branch()) or
1474 1484 self.modified() or self.added() or self.removed() or
1475 1485 (missing and self.deleted()))
1476 1486
1477 1487 def add(self, list, prefix=""):
1478 1488 join = lambda f: os.path.join(prefix, f)
1479 1489 with self._repo.wlock():
1480 1490 ui, ds = self._repo.ui, self._repo.dirstate
1481 1491 rejected = []
1482 1492 lstat = self._repo.wvfs.lstat
1483 1493 for f in list:
1484 1494 scmutil.checkportable(ui, join(f))
1485 1495 try:
1486 1496 st = lstat(f)
1487 1497 except OSError:
1488 1498 ui.warn(_("%s does not exist!\n") % join(f))
1489 1499 rejected.append(f)
1490 1500 continue
1491 1501 if st.st_size > 10000000:
1492 1502 ui.warn(_("%s: up to %d MB of RAM may be required "
1493 1503 "to manage this file\n"
1494 1504 "(use 'hg revert %s' to cancel the "
1495 1505 "pending addition)\n")
1496 1506 % (f, 3 * st.st_size // 1000000, join(f)))
1497 1507 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1498 1508 ui.warn(_("%s not added: only files and symlinks "
1499 1509 "supported currently\n") % join(f))
1500 1510 rejected.append(f)
1501 1511 elif ds[f] in 'amn':
1502 1512 ui.warn(_("%s already tracked!\n") % join(f))
1503 1513 elif ds[f] == 'r':
1504 1514 ds.normallookup(f)
1505 1515 else:
1506 1516 ds.add(f)
1507 1517 return rejected
1508 1518
1509 1519 def forget(self, files, prefix=""):
1510 1520 join = lambda f: os.path.join(prefix, f)
1511 1521 with self._repo.wlock():
1512 1522 rejected = []
1513 1523 for f in files:
1514 1524 if f not in self._repo.dirstate:
1515 1525 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
1516 1526 rejected.append(f)
1517 1527 elif self._repo.dirstate[f] != 'a':
1518 1528 self._repo.dirstate.remove(f)
1519 1529 else:
1520 1530 self._repo.dirstate.drop(f)
1521 1531 return rejected
1522 1532
1523 1533 def undelete(self, list):
1524 1534 pctxs = self.parents()
1525 1535 with self._repo.wlock():
1526 1536 for f in list:
1527 1537 if self._repo.dirstate[f] != 'r':
1528 1538 self._repo.ui.warn(_("%s not removed!\n") % f)
1529 1539 else:
1530 1540 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1531 1541 t = fctx.data()
1532 1542 self._repo.wwrite(f, t, fctx.flags())
1533 1543 self._repo.dirstate.normal(f)
1534 1544
1535 1545 def copy(self, source, dest):
1536 1546 try:
1537 1547 st = self._repo.wvfs.lstat(dest)
1538 1548 except OSError as err:
1539 1549 if err.errno != errno.ENOENT:
1540 1550 raise
1541 1551 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1542 1552 return
1543 1553 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1544 1554 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1545 1555 "symbolic link\n") % dest)
1546 1556 else:
1547 1557 with self._repo.wlock():
1548 1558 if self._repo.dirstate[dest] in '?':
1549 1559 self._repo.dirstate.add(dest)
1550 1560 elif self._repo.dirstate[dest] in 'r':
1551 1561 self._repo.dirstate.normallookup(dest)
1552 1562 self._repo.dirstate.copy(source, dest)
1553 1563
1554 1564 def match(self, pats=None, include=None, exclude=None, default='glob',
1555 1565 listsubrepos=False, badfn=None):
1556 1566 if pats is None:
1557 1567 pats = []
1558 1568 r = self._repo
1559 1569
1560 1570 # Only a case insensitive filesystem needs magic to translate user input
1561 1571 # to actual case in the filesystem.
1562 1572 matcherfunc = matchmod.match
1563 1573 if not util.fscasesensitive(r.root):
1564 1574 matcherfunc = matchmod.icasefsmatcher
1565 1575 return matcherfunc(r.root, r.getcwd(), pats,
1566 1576 include, exclude, default,
1567 1577 auditor=r.auditor, ctx=self,
1568 1578 listsubrepos=listsubrepos, badfn=badfn)
1569 1579
1570 1580 def _filtersuspectsymlink(self, files):
1571 1581 if not files or self._repo.dirstate._checklink:
1572 1582 return files
1573 1583
1574 1584 # Symlink placeholders may get non-symlink-like contents
1575 1585 # via user error or dereferencing by NFS or Samba servers,
1576 1586 # so we filter out any placeholders that don't look like a
1577 1587 # symlink
1578 1588 sane = []
1579 1589 for f in files:
1580 1590 if self.flags(f) == 'l':
1581 1591 d = self[f].data()
1582 1592 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1583 1593 self._repo.ui.debug('ignoring suspect symlink placeholder'
1584 1594 ' "%s"\n' % f)
1585 1595 continue
1586 1596 sane.append(f)
1587 1597 return sane
1588 1598
1589 1599 def _checklookup(self, files):
1590 1600 # check for any possibly clean files
1591 1601 if not files:
1592 1602 return [], []
1593 1603
1594 1604 modified = []
1595 1605 fixup = []
1596 1606 pctx = self._parents[0]
1597 1607 # do a full compare of any files that might have changed
1598 1608 for f in sorted(files):
1599 1609 if (f not in pctx or self.flags(f) != pctx.flags(f)
1600 1610 or pctx[f].cmp(self[f])):
1601 1611 modified.append(f)
1602 1612 else:
1603 1613 fixup.append(f)
1604 1614
1605 1615 # update dirstate for files that are actually clean
1606 1616 if fixup:
1607 1617 try:
1608 1618 # updating the dirstate is optional
1609 1619 # so we don't wait on the lock
1610 1620 # wlock can invalidate the dirstate, so cache normal _after_
1611 1621 # taking the lock
1612 1622 with self._repo.wlock(False):
1613 1623 normal = self._repo.dirstate.normal
1614 1624 for f in fixup:
1615 1625 normal(f)
1616 1626 # write changes out explicitly, because nesting
1617 1627 # wlock at runtime may prevent 'wlock.release()'
1618 1628 # after this block from doing so for subsequent
1619 1629 # changing files
1620 1630 self._repo.dirstate.write(self._repo.currenttransaction())
1621 1631 except error.LockError:
1622 1632 pass
1623 1633 return modified, fixup
1624 1634
1625 1635 def _dirstatestatus(self, match=None, ignored=False, clean=False,
1626 1636 unknown=False):
1627 1637 '''Gets the status from the dirstate -- internal use only.'''
1628 1638 listignored, listclean, listunknown = ignored, clean, unknown
1629 1639 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
1630 1640 subrepos = []
1631 1641 if '.hgsub' in self:
1632 1642 subrepos = sorted(self.substate)
1633 1643 cmp, s = self._repo.dirstate.status(match, subrepos, listignored,
1634 1644 listclean, listunknown)
1635 1645
1636 1646 # check for any possibly clean files
1637 1647 if cmp:
1638 1648 modified2, fixup = self._checklookup(cmp)
1639 1649 s.modified.extend(modified2)
1640 1650
1641 1651 # update dirstate for files that are actually clean
1642 1652 if fixup and listclean:
1643 1653 s.clean.extend(fixup)
1644 1654
1645 1655 if match.always():
1646 1656 # cache for performance
1647 1657 if s.unknown or s.ignored or s.clean:
1648 1658 # "_status" is cached with list*=False in the normal route
1649 1659 self._status = scmutil.status(s.modified, s.added, s.removed,
1650 1660 s.deleted, [], [], [])
1651 1661 else:
1652 1662 self._status = s
1653 1663
1654 1664 return s
1655 1665
1656 1666 @propertycache
1657 1667 def _manifest(self):
1658 1668 """generate a manifest corresponding to the values in self._status
1659 1669
1660 1670 This reuse the file nodeid from parent, but we use special node
1661 1671 identifiers for added and modified files. This is used by manifests
1662 1672 merge to see that files are different and by update logic to avoid
1663 1673 deleting newly added files.
1664 1674 """
1665 1675 return self._buildstatusmanifest(self._status)
1666 1676
1667 1677 def _buildstatusmanifest(self, status):
1668 1678 """Builds a manifest that includes the given status results."""
1669 1679 parents = self.parents()
1670 1680
1671 1681 man = parents[0].manifest().copy()
1672 1682
1673 1683 ff = self._flagfunc
1674 1684 for i, l in ((addednodeid, status.added),
1675 1685 (modifiednodeid, status.modified)):
1676 1686 for f in l:
1677 1687 man[f] = i
1678 1688 try:
1679 1689 man.setflag(f, ff(f))
1680 1690 except OSError:
1681 1691 pass
1682 1692
1683 1693 for f in status.deleted + status.removed:
1684 1694 if f in man:
1685 1695 del man[f]
1686 1696
1687 1697 return man
1688 1698
1689 1699 def _buildstatus(self, other, s, match, listignored, listclean,
1690 1700 listunknown):
1691 1701 """build a status with respect to another context
1692 1702
1693 1703 This includes logic for maintaining the fast path of status when
1694 1704 comparing the working directory against its parent, which is to skip
1695 1705 building a new manifest if self (working directory) is not comparing
1696 1706 against its parent (repo['.']).
1697 1707 """
1698 1708 s = self._dirstatestatus(match, listignored, listclean, listunknown)
1699 1709 # Filter out symlinks that, in the case of FAT32 and NTFS filesystems,
1700 1710 # might have accidentally ended up with the entire contents of the file
1701 1711 # they are supposed to be linking to.
1702 1712 s.modified[:] = self._filtersuspectsymlink(s.modified)
1703 1713 if other != self._repo['.']:
1704 1714 s = super(workingctx, self)._buildstatus(other, s, match,
1705 1715 listignored, listclean,
1706 1716 listunknown)
1707 1717 return s
1708 1718
1709 1719 def _matchstatus(self, other, match):
1710 1720 """override the match method with a filter for directory patterns
1711 1721
1712 1722 We use inheritance to customize the match.bad method only in cases of
1713 1723 workingctx since it belongs only to the working directory when
1714 1724 comparing against the parent changeset.
1715 1725
1716 1726 If we aren't comparing against the working directory's parent, then we
1717 1727 just use the default match object sent to us.
1718 1728 """
1719 1729 superself = super(workingctx, self)
1720 1730 match = superself._matchstatus(other, match)
1721 1731 if other != self._repo['.']:
1722 1732 def bad(f, msg):
1723 1733 # 'f' may be a directory pattern from 'match.files()',
1724 1734 # so 'f not in ctx1' is not enough
1725 1735 if f not in other and not other.hasdir(f):
1726 1736 self._repo.ui.warn('%s: %s\n' %
1727 1737 (self._repo.dirstate.pathto(f), msg))
1728 1738 match.bad = bad
1729 1739 return match
1730 1740
1731 1741 class committablefilectx(basefilectx):
1732 1742 """A committablefilectx provides common functionality for a file context
1733 1743 that wants the ability to commit, e.g. workingfilectx or memfilectx."""
1734 1744 def __init__(self, repo, path, filelog=None, ctx=None):
1735 1745 self._repo = repo
1736 1746 self._path = path
1737 1747 self._changeid = None
1738 1748 self._filerev = self._filenode = None
1739 1749
1740 1750 if filelog is not None:
1741 1751 self._filelog = filelog
1742 1752 if ctx:
1743 1753 self._changectx = ctx
1744 1754
1745 1755 def __nonzero__(self):
1746 1756 return True
1747 1757
1748 1758 __bool__ = __nonzero__
1749 1759
1750 1760 def linkrev(self):
1751 1761 # linked to self._changectx no matter if file is modified or not
1752 1762 return self.rev()
1753 1763
1754 1764 def parents(self):
1755 1765 '''return parent filectxs, following copies if necessary'''
1756 1766 def filenode(ctx, path):
1757 1767 return ctx._manifest.get(path, nullid)
1758 1768
1759 1769 path = self._path
1760 1770 fl = self._filelog
1761 1771 pcl = self._changectx._parents
1762 1772 renamed = self.renamed()
1763 1773
1764 1774 if renamed:
1765 1775 pl = [renamed + (None,)]
1766 1776 else:
1767 1777 pl = [(path, filenode(pcl[0], path), fl)]
1768 1778
1769 1779 for pc in pcl[1:]:
1770 1780 pl.append((path, filenode(pc, path), fl))
1771 1781
1772 1782 return [self._parentfilectx(p, fileid=n, filelog=l)
1773 1783 for p, n, l in pl if n != nullid]
1774 1784
1775 1785 def children(self):
1776 1786 return []
1777 1787
1778 1788 class workingfilectx(committablefilectx):
1779 1789 """A workingfilectx object makes access to data related to a particular
1780 1790 file in the working directory convenient."""
1781 1791 def __init__(self, repo, path, filelog=None, workingctx=None):
1782 1792 super(workingfilectx, self).__init__(repo, path, filelog, workingctx)
1783 1793
1784 1794 @propertycache
1785 1795 def _changectx(self):
1786 1796 return workingctx(self._repo)
1787 1797
1788 1798 def data(self):
1789 1799 return self._repo.wread(self._path)
1790 1800 def renamed(self):
1791 1801 rp = self._repo.dirstate.copied(self._path)
1792 1802 if not rp:
1793 1803 return None
1794 1804 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1795 1805
1796 1806 def size(self):
1797 1807 return self._repo.wvfs.lstat(self._path).st_size
1798 1808 def date(self):
1799 1809 t, tz = self._changectx.date()
1800 1810 try:
1801 1811 return (self._repo.wvfs.lstat(self._path).st_mtime, tz)
1802 1812 except OSError as err:
1803 1813 if err.errno != errno.ENOENT:
1804 1814 raise
1805 1815 return (t, tz)
1806 1816
1807 1817 def cmp(self, fctx):
1808 1818 """compare with other file context
1809 1819
1810 1820 returns True if different than fctx.
1811 1821 """
1812 1822 # fctx should be a filectx (not a workingfilectx)
1813 1823 # invert comparison to reuse the same code path
1814 1824 return fctx.cmp(self)
1815 1825
1816 1826 def remove(self, ignoremissing=False):
1817 1827 """wraps unlink for a repo's working directory"""
1818 1828 self._repo.wvfs.unlinkpath(self._path, ignoremissing=ignoremissing)
1819 1829
1820 1830 def write(self, data, flags):
1821 1831 """wraps repo.wwrite"""
1822 1832 self._repo.wwrite(self._path, data, flags)
1823 1833
1824 1834 class workingcommitctx(workingctx):
1825 1835 """A workingcommitctx object makes access to data related to
1826 1836 the revision being committed convenient.
1827 1837
1828 1838 This hides changes in the working directory, if they aren't
1829 1839 committed in this context.
1830 1840 """
1831 1841 def __init__(self, repo, changes,
1832 1842 text="", user=None, date=None, extra=None):
1833 1843 super(workingctx, self).__init__(repo, text, user, date, extra,
1834 1844 changes)
1835 1845
1836 1846 def _dirstatestatus(self, match=None, ignored=False, clean=False,
1837 1847 unknown=False):
1838 1848 """Return matched files only in ``self._status``
1839 1849
1840 1850 Uncommitted files appear "clean" via this context, even if
1841 1851 they aren't actually so in the working directory.
1842 1852 """
1843 1853 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
1844 1854 if clean:
1845 1855 clean = [f for f in self._manifest if f not in self._changedset]
1846 1856 else:
1847 1857 clean = []
1848 1858 return scmutil.status([f for f in self._status.modified if match(f)],
1849 1859 [f for f in self._status.added if match(f)],
1850 1860 [f for f in self._status.removed if match(f)],
1851 1861 [], [], [], clean)
1852 1862
1853 1863 @propertycache
1854 1864 def _changedset(self):
1855 1865 """Return the set of files changed in this context
1856 1866 """
1857 1867 changed = set(self._status.modified)
1858 1868 changed.update(self._status.added)
1859 1869 changed.update(self._status.removed)
1860 1870 return changed
1861 1871
1862 1872 def makecachingfilectxfn(func):
1863 1873 """Create a filectxfn that caches based on the path.
1864 1874
1865 1875 We can't use util.cachefunc because it uses all arguments as the cache
1866 1876 key and this creates a cycle since the arguments include the repo and
1867 1877 memctx.
1868 1878 """
1869 1879 cache = {}
1870 1880
1871 1881 def getfilectx(repo, memctx, path):
1872 1882 if path not in cache:
1873 1883 cache[path] = func(repo, memctx, path)
1874 1884 return cache[path]
1875 1885
1876 1886 return getfilectx
1877 1887
1878 1888 class memctx(committablectx):
1879 1889 """Use memctx to perform in-memory commits via localrepo.commitctx().
1880 1890
1881 1891 Revision information is supplied at initialization time while
1882 1892 related files data and is made available through a callback
1883 1893 mechanism. 'repo' is the current localrepo, 'parents' is a
1884 1894 sequence of two parent revisions identifiers (pass None for every
1885 1895 missing parent), 'text' is the commit message and 'files' lists
1886 1896 names of files touched by the revision (normalized and relative to
1887 1897 repository root).
1888 1898
1889 1899 filectxfn(repo, memctx, path) is a callable receiving the
1890 1900 repository, the current memctx object and the normalized path of
1891 1901 requested file, relative to repository root. It is fired by the
1892 1902 commit function for every file in 'files', but calls order is
1893 1903 undefined. If the file is available in the revision being
1894 1904 committed (updated or added), filectxfn returns a memfilectx
1895 1905 object. If the file was removed, filectxfn return None for recent
1896 1906 Mercurial. Moved files are represented by marking the source file
1897 1907 removed and the new file added with copy information (see
1898 1908 memfilectx).
1899 1909
1900 1910 user receives the committer name and defaults to current
1901 1911 repository username, date is the commit date in any format
1902 1912 supported by util.parsedate() and defaults to current date, extra
1903 1913 is a dictionary of metadata or is left empty.
1904 1914 """
1905 1915
1906 1916 # Mercurial <= 3.1 expects the filectxfn to raise IOError for missing files.
1907 1917 # Extensions that need to retain compatibility across Mercurial 3.1 can use
1908 1918 # this field to determine what to do in filectxfn.
1909 1919 _returnnoneformissingfiles = True
1910 1920
1911 1921 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1912 1922 date=None, extra=None, editor=False):
1913 1923 super(memctx, self).__init__(repo, text, user, date, extra)
1914 1924 self._rev = None
1915 1925 self._node = None
1916 1926 parents = [(p or nullid) for p in parents]
1917 1927 p1, p2 = parents
1918 1928 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1919 1929 files = sorted(set(files))
1920 1930 self._files = files
1921 1931 self.substate = {}
1922 1932
1923 1933 # if store is not callable, wrap it in a function
1924 1934 if not callable(filectxfn):
1925 1935 def getfilectx(repo, memctx, path):
1926 1936 fctx = filectxfn[path]
1927 1937 # this is weird but apparently we only keep track of one parent
1928 1938 # (why not only store that instead of a tuple?)
1929 1939 copied = fctx.renamed()
1930 1940 if copied:
1931 1941 copied = copied[0]
1932 1942 return memfilectx(repo, path, fctx.data(),
1933 1943 islink=fctx.islink(), isexec=fctx.isexec(),
1934 1944 copied=copied, memctx=memctx)
1935 1945 self._filectxfn = getfilectx
1936 1946 else:
1937 1947 # memoizing increases performance for e.g. vcs convert scenarios.
1938 1948 self._filectxfn = makecachingfilectxfn(filectxfn)
1939 1949
1940 1950 if extra:
1941 1951 self._extra = extra.copy()
1942 1952 else:
1943 1953 self._extra = {}
1944 1954
1945 1955 if self._extra.get('branch', '') == '':
1946 1956 self._extra['branch'] = 'default'
1947 1957
1948 1958 if editor:
1949 1959 self._text = editor(self._repo, self, [])
1950 1960 self._repo.savecommitmessage(self._text)
1951 1961
1952 1962 def filectx(self, path, filelog=None):
1953 1963 """get a file context from the working directory
1954 1964
1955 1965 Returns None if file doesn't exist and should be removed."""
1956 1966 return self._filectxfn(self._repo, self, path)
1957 1967
1958 1968 def commit(self):
1959 1969 """commit context to the repo"""
1960 1970 return self._repo.commitctx(self)
1961 1971
1962 1972 @propertycache
1963 1973 def _manifest(self):
1964 1974 """generate a manifest based on the return values of filectxfn"""
1965 1975
1966 1976 # keep this simple for now; just worry about p1
1967 1977 pctx = self._parents[0]
1968 1978 man = pctx.manifest().copy()
1969 1979
1970 1980 for f in self._status.modified:
1971 1981 p1node = nullid
1972 1982 p2node = nullid
1973 1983 p = pctx[f].parents() # if file isn't in pctx, check p2?
1974 1984 if len(p) > 0:
1975 1985 p1node = p[0].filenode()
1976 1986 if len(p) > 1:
1977 1987 p2node = p[1].filenode()
1978 1988 man[f] = revlog.hash(self[f].data(), p1node, p2node)
1979 1989
1980 1990 for f in self._status.added:
1981 1991 man[f] = revlog.hash(self[f].data(), nullid, nullid)
1982 1992
1983 1993 for f in self._status.removed:
1984 1994 if f in man:
1985 1995 del man[f]
1986 1996
1987 1997 return man
1988 1998
1989 1999 @propertycache
1990 2000 def _status(self):
1991 2001 """Calculate exact status from ``files`` specified at construction
1992 2002 """
1993 2003 man1 = self.p1().manifest()
1994 2004 p2 = self._parents[1]
1995 2005 # "1 < len(self._parents)" can't be used for checking
1996 2006 # existence of the 2nd parent, because "memctx._parents" is
1997 2007 # explicitly initialized by the list, of which length is 2.
1998 2008 if p2.node() != nullid:
1999 2009 man2 = p2.manifest()
2000 2010 managing = lambda f: f in man1 or f in man2
2001 2011 else:
2002 2012 managing = lambda f: f in man1
2003 2013
2004 2014 modified, added, removed = [], [], []
2005 2015 for f in self._files:
2006 2016 if not managing(f):
2007 2017 added.append(f)
2008 2018 elif self[f]:
2009 2019 modified.append(f)
2010 2020 else:
2011 2021 removed.append(f)
2012 2022
2013 2023 return scmutil.status(modified, added, removed, [], [], [], [])
2014 2024
2015 2025 class memfilectx(committablefilectx):
2016 2026 """memfilectx represents an in-memory file to commit.
2017 2027
2018 2028 See memctx and committablefilectx for more details.
2019 2029 """
2020 2030 def __init__(self, repo, path, data, islink=False,
2021 2031 isexec=False, copied=None, memctx=None):
2022 2032 """
2023 2033 path is the normalized file path relative to repository root.
2024 2034 data is the file content as a string.
2025 2035 islink is True if the file is a symbolic link.
2026 2036 isexec is True if the file is executable.
2027 2037 copied is the source file path if current file was copied in the
2028 2038 revision being committed, or None."""
2029 2039 super(memfilectx, self).__init__(repo, path, None, memctx)
2030 2040 self._data = data
2031 2041 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
2032 2042 self._copied = None
2033 2043 if copied:
2034 2044 self._copied = (copied, nullid)
2035 2045
2036 2046 def data(self):
2037 2047 return self._data
2038 2048 def size(self):
2039 2049 return len(self.data())
2040 2050 def flags(self):
2041 2051 return self._flags
2042 2052 def renamed(self):
2043 2053 return self._copied
2044 2054
2045 2055 def remove(self, ignoremissing=False):
2046 2056 """wraps unlink for a repo's working directory"""
2047 2057 # need to figure out what to do here
2048 2058 del self._changectx[self._path]
2049 2059
2050 2060 def write(self, data, flags):
2051 2061 """wraps repo.wwrite"""
2052 2062 self._data = data
2053 2063
2054 2064 class metadataonlyctx(committablectx):
2055 2065 """Like memctx but it's reusing the manifest of different commit.
2056 2066 Intended to be used by lightweight operations that are creating
2057 2067 metadata-only changes.
2058 2068
2059 2069 Revision information is supplied at initialization time. 'repo' is the
2060 2070 current localrepo, 'ctx' is original revision which manifest we're reuisng
2061 2071 'parents' is a sequence of two parent revisions identifiers (pass None for
2062 2072 every missing parent), 'text' is the commit.
2063 2073
2064 2074 user receives the committer name and defaults to current repository
2065 2075 username, date is the commit date in any format supported by
2066 2076 util.parsedate() and defaults to current date, extra is a dictionary of
2067 2077 metadata or is left empty.
2068 2078 """
2069 2079 def __new__(cls, repo, originalctx, *args, **kwargs):
2070 2080 return super(metadataonlyctx, cls).__new__(cls, repo)
2071 2081
2072 2082 def __init__(self, repo, originalctx, parents, text, user=None, date=None,
2073 2083 extra=None, editor=False):
2074 2084 super(metadataonlyctx, self).__init__(repo, text, user, date, extra)
2075 2085 self._rev = None
2076 2086 self._node = None
2077 2087 self._originalctx = originalctx
2078 2088 self._manifestnode = originalctx.manifestnode()
2079 2089 parents = [(p or nullid) for p in parents]
2080 2090 p1, p2 = self._parents = [changectx(self._repo, p) for p in parents]
2081 2091
2082 2092 # sanity check to ensure that the reused manifest parents are
2083 2093 # manifests of our commit parents
2084 2094 mp1, mp2 = self.manifestctx().parents
2085 2095 if p1 != nullid and p1.manifestnode() != mp1:
2086 2096 raise RuntimeError('can\'t reuse the manifest: '
2087 2097 'its p1 doesn\'t match the new ctx p1')
2088 2098 if p2 != nullid and p2.manifestnode() != mp2:
2089 2099 raise RuntimeError('can\'t reuse the manifest: '
2090 2100 'its p2 doesn\'t match the new ctx p2')
2091 2101
2092 2102 self._files = originalctx.files()
2093 2103 self.substate = {}
2094 2104
2095 2105 if extra:
2096 2106 self._extra = extra.copy()
2097 2107 else:
2098 2108 self._extra = {}
2099 2109
2100 2110 if self._extra.get('branch', '') == '':
2101 2111 self._extra['branch'] = 'default'
2102 2112
2103 2113 if editor:
2104 2114 self._text = editor(self._repo, self, [])
2105 2115 self._repo.savecommitmessage(self._text)
2106 2116
2107 2117 def manifestnode(self):
2108 2118 return self._manifestnode
2109 2119
2110 2120 @propertycache
2111 2121 def _manifestctx(self):
2112 2122 return self._repo.manifestlog[self._manifestnode]
2113 2123
2114 2124 def filectx(self, path, filelog=None):
2115 2125 return self._originalctx.filectx(path, filelog=filelog)
2116 2126
2117 2127 def commit(self):
2118 2128 """commit context to the repo"""
2119 2129 return self._repo.commitctx(self)
2120 2130
2121 2131 @property
2122 2132 def _manifest(self):
2123 2133 return self._originalctx.manifest()
2124 2134
2125 2135 @propertycache
2126 2136 def _status(self):
2127 2137 """Calculate exact status from ``files`` specified in the ``origctx``
2128 2138 and parents manifests.
2129 2139 """
2130 2140 man1 = self.p1().manifest()
2131 2141 p2 = self._parents[1]
2132 2142 # "1 < len(self._parents)" can't be used for checking
2133 2143 # existence of the 2nd parent, because "metadataonlyctx._parents" is
2134 2144 # explicitly initialized by the list, of which length is 2.
2135 2145 if p2.node() != nullid:
2136 2146 man2 = p2.manifest()
2137 2147 managing = lambda f: f in man1 or f in man2
2138 2148 else:
2139 2149 managing = lambda f: f in man1
2140 2150
2141 2151 modified, added, removed = [], [], []
2142 2152 for f in self._files:
2143 2153 if not managing(f):
2144 2154 added.append(f)
2145 2155 elif self[f]:
2146 2156 modified.append(f)
2147 2157 else:
2148 2158 removed.append(f)
2149 2159
2150 2160 return scmutil.status(modified, added, removed, [], [], [], [])
@@ -1,817 +1,823
1 1 $ HGMERGE=true; export HGMERGE
2 2
3 3 init
4 4
5 5 $ hg init repo
6 6 $ cd repo
7 7
8 8 commit
9 9
10 10 $ echo 'a' > a
11 11 $ hg ci -A -m test -u nobody -d '1 0'
12 12 adding a
13 13
14 14 annotate -c
15 15
16 16 $ hg annotate -c a
17 17 8435f90966e4: a
18 18
19 19 annotate -cl
20 20
21 21 $ hg annotate -cl a
22 22 8435f90966e4:1: a
23 23
24 24 annotate -d
25 25
26 26 $ hg annotate -d a
27 27 Thu Jan 01 00:00:01 1970 +0000: a
28 28
29 29 annotate -n
30 30
31 31 $ hg annotate -n a
32 32 0: a
33 33
34 34 annotate -nl
35 35
36 36 $ hg annotate -nl a
37 37 0:1: a
38 38
39 39 annotate -u
40 40
41 41 $ hg annotate -u a
42 42 nobody: a
43 43
44 44 annotate -cdnu
45 45
46 46 $ hg annotate -cdnu a
47 47 nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000: a
48 48
49 49 annotate -cdnul
50 50
51 51 $ hg annotate -cdnul a
52 52 nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000:1: a
53 53
54 54 annotate (JSON)
55 55
56 56 $ hg annotate -Tjson a
57 57 [
58 58 {
59 59 "line": "a\n",
60 60 "rev": 0
61 61 }
62 62 ]
63 63
64 64 $ hg annotate -Tjson -cdfnul a
65 65 [
66 66 {
67 67 "date": [1.0, 0],
68 68 "file": "a",
69 69 "line": "a\n",
70 70 "line_number": 1,
71 71 "node": "8435f90966e442695d2ded29fdade2bac5ad8065",
72 72 "rev": 0,
73 73 "user": "nobody"
74 74 }
75 75 ]
76 76
77 77 $ cat <<EOF >>a
78 78 > a
79 79 > a
80 80 > EOF
81 81 $ hg ci -ma1 -d '1 0'
82 82 $ hg cp a b
83 83 $ hg ci -mb -d '1 0'
84 84 $ cat <<EOF >> b
85 85 > b4
86 86 > b5
87 87 > b6
88 88 > EOF
89 89 $ hg ci -mb2 -d '2 0'
90 90
91 91 annotate -n b
92 92
93 93 $ hg annotate -n b
94 94 0: a
95 95 1: a
96 96 1: a
97 97 3: b4
98 98 3: b5
99 99 3: b6
100 100
101 101 annotate --no-follow b
102 102
103 103 $ hg annotate --no-follow b
104 104 2: a
105 105 2: a
106 106 2: a
107 107 3: b4
108 108 3: b5
109 109 3: b6
110 110
111 111 annotate -nl b
112 112
113 113 $ hg annotate -nl b
114 114 0:1: a
115 115 1:2: a
116 116 1:3: a
117 117 3:4: b4
118 118 3:5: b5
119 119 3:6: b6
120 120
121 121 annotate -nf b
122 122
123 123 $ hg annotate -nf b
124 124 0 a: a
125 125 1 a: a
126 126 1 a: a
127 127 3 b: b4
128 128 3 b: b5
129 129 3 b: b6
130 130
131 131 annotate -nlf b
132 132
133 133 $ hg annotate -nlf b
134 134 0 a:1: a
135 135 1 a:2: a
136 136 1 a:3: a
137 137 3 b:4: b4
138 138 3 b:5: b5
139 139 3 b:6: b6
140 140
141 141 $ hg up -C 2
142 142 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
143 143 $ cat <<EOF >> b
144 144 > b4
145 145 > c
146 146 > b5
147 147 > EOF
148 148 $ hg ci -mb2.1 -d '2 0'
149 149 created new head
150 150 $ hg merge
151 151 merging b
152 152 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
153 153 (branch merge, don't forget to commit)
154 154 $ hg ci -mmergeb -d '3 0'
155 155
156 156 annotate after merge
157 157
158 158 $ hg annotate -nf b
159 159 0 a: a
160 160 1 a: a
161 161 1 a: a
162 162 3 b: b4
163 163 4 b: c
164 164 3 b: b5
165 165
166 166 annotate after merge with -l
167 167
168 168 $ hg annotate -nlf b
169 169 0 a:1: a
170 170 1 a:2: a
171 171 1 a:3: a
172 172 3 b:4: b4
173 173 4 b:5: c
174 174 3 b:5: b5
175 175
176 176 $ hg up -C 1
177 177 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
178 178 $ hg cp a b
179 179 $ cat <<EOF > b
180 180 > a
181 181 > z
182 182 > a
183 183 > EOF
184 184 $ hg ci -mc -d '3 0'
185 185 created new head
186 186 $ hg merge
187 187 merging b
188 188 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
189 189 (branch merge, don't forget to commit)
190 190 $ cat <<EOF >> b
191 191 > b4
192 192 > c
193 193 > b5
194 194 > EOF
195 195 $ echo d >> b
196 196 $ hg ci -mmerge2 -d '4 0'
197 197
198 198 annotate after rename merge
199 199
200 200 $ hg annotate -nf b
201 201 0 a: a
202 202 6 b: z
203 203 1 a: a
204 204 3 b: b4
205 205 4 b: c
206 206 3 b: b5
207 207 7 b: d
208 208
209 209 annotate after rename merge with -l
210 210
211 211 $ hg annotate -nlf b
212 212 0 a:1: a
213 213 6 b:2: z
214 214 1 a:3: a
215 215 3 b:4: b4
216 216 4 b:5: c
217 217 3 b:5: b5
218 218 7 b:7: d
219 219
220 220 Issue2807: alignment of line numbers with -l
221 221
222 222 $ echo more >> b
223 223 $ hg ci -mmore -d '5 0'
224 224 $ echo more >> b
225 225 $ hg ci -mmore -d '6 0'
226 226 $ echo more >> b
227 227 $ hg ci -mmore -d '7 0'
228 228 $ hg annotate -nlf b
229 229 0 a: 1: a
230 230 6 b: 2: z
231 231 1 a: 3: a
232 232 3 b: 4: b4
233 233 4 b: 5: c
234 234 3 b: 5: b5
235 235 7 b: 7: d
236 236 8 b: 8: more
237 237 9 b: 9: more
238 238 10 b:10: more
239 239
240 240 linkrev vs rev
241 241
242 242 $ hg annotate -r tip -n a
243 243 0: a
244 244 1: a
245 245 1: a
246 246
247 247 linkrev vs rev with -l
248 248
249 249 $ hg annotate -r tip -nl a
250 250 0:1: a
251 251 1:2: a
252 252 1:3: a
253 253
254 254 Issue589: "undelete" sequence leads to crash
255 255
256 256 annotate was crashing when trying to --follow something
257 257
258 258 like A -> B -> A
259 259
260 260 generate ABA rename configuration
261 261
262 262 $ echo foo > foo
263 263 $ hg add foo
264 264 $ hg ci -m addfoo
265 265 $ hg rename foo bar
266 266 $ hg ci -m renamefoo
267 267 $ hg rename bar foo
268 268 $ hg ci -m renamebar
269 269
270 270 annotate after ABA with follow
271 271
272 272 $ hg annotate --follow foo
273 273 foo: foo
274 274
275 275 missing file
276 276
277 277 $ hg ann nosuchfile
278 278 abort: nosuchfile: no such file in rev e9e6b4fa872f
279 279 [255]
280 280
281 281 annotate file without '\n' on last line
282 282
283 283 $ printf "" > c
284 284 $ hg ci -A -m test -u nobody -d '1 0'
285 285 adding c
286 286 $ hg annotate c
287 287 $ printf "a\nb" > c
288 288 $ hg ci -m test
289 289 $ hg annotate c
290 290 [0-9]+: a (re)
291 291 [0-9]+: b (re)
292 292
293 293 Issue3841: check annotation of the file of which filelog includes
294 294 merging between the revision and its ancestor
295 295
296 296 to reproduce the situation with recent Mercurial, this script uses (1)
297 297 "hg debugsetparents" to merge without ancestor check by "hg merge",
298 298 and (2) the extension to allow filelog merging between the revision
299 299 and its ancestor by overriding "repo._filecommit".
300 300
301 301 $ cat > ../legacyrepo.py <<EOF
302 302 > from mercurial import node, error
303 303 > def reposetup(ui, repo):
304 304 > class legacyrepo(repo.__class__):
305 305 > def _filecommit(self, fctx, manifest1, manifest2,
306 306 > linkrev, tr, changelist):
307 307 > fname = fctx.path()
308 308 > text = fctx.data()
309 309 > flog = self.file(fname)
310 310 > fparent1 = manifest1.get(fname, node.nullid)
311 311 > fparent2 = manifest2.get(fname, node.nullid)
312 312 > meta = {}
313 313 > copy = fctx.renamed()
314 314 > if copy and copy[0] != fname:
315 315 > raise error.Abort('copying is not supported')
316 316 > if fparent2 != node.nullid:
317 317 > changelist.append(fname)
318 318 > return flog.add(text, meta, tr, linkrev,
319 319 > fparent1, fparent2)
320 320 > raise error.Abort('only merging is supported')
321 321 > repo.__class__ = legacyrepo
322 322 > EOF
323 323
324 324 $ cat > baz <<EOF
325 325 > 1
326 326 > 2
327 327 > 3
328 328 > 4
329 329 > 5
330 330 > EOF
331 331 $ hg add baz
332 332 $ hg commit -m "baz:0"
333 333
334 334 $ cat > baz <<EOF
335 335 > 1 baz:1
336 336 > 2
337 337 > 3
338 338 > 4
339 339 > 5
340 340 > EOF
341 341 $ hg commit -m "baz:1"
342 342
343 343 $ cat > baz <<EOF
344 344 > 1 baz:1
345 345 > 2 baz:2
346 346 > 3
347 347 > 4
348 348 > 5
349 349 > EOF
350 350 $ hg debugsetparents 17 17
351 351 $ hg --config extensions.legacyrepo=../legacyrepo.py commit -m "baz:2"
352 352 $ hg debugindexdot .hg/store/data/baz.i
353 353 digraph G {
354 354 -1 -> 0
355 355 0 -> 1
356 356 1 -> 2
357 357 1 -> 2
358 358 }
359 359 $ hg annotate baz
360 360 17: 1 baz:1
361 361 18: 2 baz:2
362 362 16: 3
363 363 16: 4
364 364 16: 5
365 365
366 366 $ cat > baz <<EOF
367 367 > 1 baz:1
368 368 > 2 baz:2
369 369 > 3 baz:3
370 370 > 4
371 371 > 5
372 372 > EOF
373 373 $ hg commit -m "baz:3"
374 374
375 375 $ cat > baz <<EOF
376 376 > 1 baz:1
377 377 > 2 baz:2
378 378 > 3 baz:3
379 379 > 4 baz:4
380 380 > 5
381 381 > EOF
382 382 $ hg debugsetparents 19 18
383 383 $ hg --config extensions.legacyrepo=../legacyrepo.py commit -m "baz:4"
384 384 $ hg debugindexdot .hg/store/data/baz.i
385 385 digraph G {
386 386 -1 -> 0
387 387 0 -> 1
388 388 1 -> 2
389 389 1 -> 2
390 390 2 -> 3
391 391 3 -> 4
392 392 2 -> 4
393 393 }
394 394 $ hg annotate baz
395 395 17: 1 baz:1
396 396 18: 2 baz:2
397 397 19: 3 baz:3
398 398 20: 4 baz:4
399 399 16: 5
400 400
401 401 annotate clean file
402 402
403 403 $ hg annotate -ncr "wdir()" foo
404 404 11 472b18db256d : foo
405 405
406 406 annotate modified file
407 407
408 408 $ echo foofoo >> foo
409 409 $ hg annotate -r "wdir()" foo
410 410 11 : foo
411 411 20+: foofoo
412 412
413 413 $ hg annotate -cr "wdir()" foo
414 414 472b18db256d : foo
415 415 b6bedd5477e7+: foofoo
416 416
417 417 $ hg annotate -ncr "wdir()" foo
418 418 11 472b18db256d : foo
419 419 20 b6bedd5477e7+: foofoo
420 420
421 421 $ hg annotate --debug -ncr "wdir()" foo
422 422 11 472b18db256d1e8282064eab4bfdaf48cbfe83cd : foo
423 423 20 b6bedd5477e797f25e568a6402d4697f3f895a72+: foofoo
424 424
425 425 $ hg annotate -udr "wdir()" foo
426 426 test Thu Jan 01 00:00:00 1970 +0000: foo
427 427 test [A-Za-z0-9:+ ]+: foofoo (re)
428 428
429 429 $ hg annotate -ncr "wdir()" -Tjson foo
430 430 [
431 431 {
432 432 "line": "foo\n",
433 433 "node": "472b18db256d1e8282064eab4bfdaf48cbfe83cd",
434 434 "rev": 11
435 435 },
436 436 {
437 437 "line": "foofoo\n",
438 438 "node": null,
439 439 "rev": null
440 440 }
441 441 ]
442 442
443 443 annotate added file
444 444
445 445 $ echo bar > bar
446 446 $ hg add bar
447 447 $ hg annotate -ncr "wdir()" bar
448 448 20 b6bedd5477e7+: bar
449 449
450 450 annotate renamed file
451 451
452 452 $ hg rename foo renamefoo2
453 453 $ hg annotate -ncr "wdir()" renamefoo2
454 454 11 472b18db256d : foo
455 455 20 b6bedd5477e7+: foofoo
456 456
457 457 annotate missing file
458 458
459 459 $ rm baz
460 460 #if windows
461 461 $ hg annotate -ncr "wdir()" baz
462 462 abort: $TESTTMP\repo\baz: The system cannot find the file specified
463 463 [255]
464 464 #else
465 465 $ hg annotate -ncr "wdir()" baz
466 466 abort: No such file or directory: $TESTTMP/repo/baz
467 467 [255]
468 468 #endif
469 469
470 470 annotate removed file
471 471
472 472 $ hg rm baz
473 473 #if windows
474 474 $ hg annotate -ncr "wdir()" baz
475 475 abort: $TESTTMP\repo\baz: The system cannot find the file specified
476 476 [255]
477 477 #else
478 478 $ hg annotate -ncr "wdir()" baz
479 479 abort: No such file or directory: $TESTTMP/repo/baz
480 480 [255]
481 481 #endif
482 482
483 483 $ hg revert --all --no-backup --quiet
484 484 $ hg id -n
485 485 20
486 486
487 487 Test followlines() revset; we usually check both followlines(pat, range) and
488 488 followlines(pat, range, descend=True) to make sure both give the same result
489 489 when they should.
490 490
491 491 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5)'
492 492 16: baz:0
493 493 19: baz:3
494 494 20: baz:4
495 495 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=20)'
496 496 16: baz:0
497 497 19: baz:3
498 498 20: baz:4
499 499 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19)'
500 500 16: baz:0
501 501 19: baz:3
502 502 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19, descend=True)'
503 19: baz:3
503 504 20: baz:4
504 505 $ printf "0\n0\n" | cat - baz > baz1
505 506 $ mv baz1 baz
506 507 $ hg ci -m 'added two lines with 0'
507 508 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)'
508 509 16: baz:0
509 510 19: baz:3
510 511 20: baz:4
511 512 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, descend=True, startrev=19)'
513 19: baz:3
512 514 20: baz:4
513 515 $ echo 6 >> baz
514 516 $ hg ci -m 'added line 8'
515 517 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)'
516 518 16: baz:0
517 519 19: baz:3
518 520 20: baz:4
519 521 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19, descend=True)'
522 19: baz:3
520 523 20: baz:4
521 524 $ sed 's/3/3+/' baz > baz.new
522 525 $ mv baz.new baz
523 526 $ hg ci -m 'baz:3->3+'
524 527 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)'
525 528 16: baz:0
526 529 19: baz:3
527 530 20: baz:4
528 531 23: baz:3->3+
529 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=19, descend=True)'
532 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 3:5, startrev=17, descend=True)'
533 19: baz:3
530 534 20: baz:4
531 535 23: baz:3->3+
532 536 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 1:2)'
533 537 21: added two lines with 0
534 538
535 539 file patterns are okay
536 540 $ hg log -T '{rev}: {desc}\n' -r 'followlines("path:baz", 1:2)'
537 541 21: added two lines with 0
538 542
539 543 renames are followed
540 544 $ hg mv baz qux
541 545 $ sed 's/4/4+/' qux > qux.new
542 546 $ mv qux.new qux
543 547 $ hg ci -m 'qux:4->4+'
544 548 $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
545 549 16: baz:0
546 550 19: baz:3
547 551 20: baz:4
548 552 23: baz:3->3+
549 553 24: qux:4->4+
550 554
551 555 but are missed when following children
552 556 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7, startrev=22, descend=True)'
553 557 23: baz:3->3+
554 558
555 559 merge
556 560 $ hg up 23 --quiet
557 561 $ echo 7 >> baz
558 562 $ hg ci -m 'one more line, out of line range'
559 563 created new head
560 564 $ sed 's/3+/3-/' baz > baz.new
561 565 $ mv baz.new baz
562 566 $ hg ci -m 'baz:3+->3-'
563 567 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7)'
564 568 16: baz:0
565 569 19: baz:3
566 570 20: baz:4
567 571 23: baz:3->3+
568 572 26: baz:3+->3-
569 573 $ hg merge 24
570 574 merging baz and qux to qux
571 575 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
572 576 (branch merge, don't forget to commit)
573 577 $ hg ci -m merge
574 578 $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
575 579 16: baz:0
576 580 19: baz:3
577 581 20: baz:4
578 582 23: baz:3->3+
579 583 24: qux:4->4+
580 584 26: baz:3+->3-
581 585 27: merge
582 586 $ hg up 24 --quiet
583 587 $ hg merge 26
584 588 merging qux and baz to qux
585 589 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
586 590 (branch merge, don't forget to commit)
587 591 $ hg ci -m 'merge from other side'
588 592 created new head
589 593 $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
590 594 16: baz:0
591 595 19: baz:3
592 596 20: baz:4
593 597 23: baz:3->3+
594 598 24: qux:4->4+
595 599 26: baz:3+->3-
596 600 28: merge from other side
597 601 $ hg up 23 --quiet
598 602
599 603 we are missing the branch with rename when following children
600 604 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 5:7, startrev=25, descend=True)'
601 605 26: baz:3+->3-
602 606
603 607 we follow all branches in descending direction
604 608 $ hg up 22 --quiet
605 609 $ sed 's/3/+3/' baz > baz.new
606 610 $ mv baz.new baz
607 611 $ hg ci -m 'baz:3->+3'
608 612 created new head
609 613 $ hg log -T '{rev}: {desc}\n' -r 'followlines(baz, 2:5, startrev=16, descend=True)' --graph
610 614 @ 29: baz:3->+3
611 615 :
612 616 : o 26: baz:3+->3-
613 617 : :
614 618 : o 23: baz:3->3+
615 619 :/
616 620 o 20: baz:4
617 621 |\
618 622 | o 19: baz:3
619 623 |/
620 o 18: baz:2
621 |\
622 ~ ~
624 o 18: baz:2
625 :
626 o 16: baz:0
627 |
628 ~
623 629
624 630 check error cases
625 631 $ hg up 23 --quiet
626 632 $ hg log -r 'followlines()'
627 633 hg: parse error: followlines takes at least 1 positional arguments
628 634 [255]
629 635 $ hg log -r 'followlines(baz)'
630 636 hg: parse error: followlines requires a line range
631 637 [255]
632 638 $ hg log -r 'followlines(baz, 1)'
633 639 hg: parse error: followlines expects a line range
634 640 [255]
635 641 $ hg log -r 'followlines(baz, 1:2, startrev=desc("b"))'
636 642 hg: parse error: followlines expects exactly one revision
637 643 [255]
638 644 $ hg log -r 'followlines("glob:*", 1:2)'
639 645 hg: parse error: followlines expects exactly one file
640 646 [255]
641 647 $ hg log -r 'followlines(baz, 1:)'
642 648 hg: parse error: line range bounds must be integers
643 649 [255]
644 650 $ hg log -r 'followlines(baz, :1)'
645 651 hg: parse error: line range bounds must be integers
646 652 [255]
647 653 $ hg log -r 'followlines(baz, x:4)'
648 654 hg: parse error: line range bounds must be integers
649 655 [255]
650 656 $ hg log -r 'followlines(baz, 5:4)'
651 657 hg: parse error: line range must be positive
652 658 [255]
653 659 $ hg log -r 'followlines(baz, 0:4)'
654 660 hg: parse error: fromline must be strictly positive
655 661 [255]
656 662 $ hg log -r 'followlines(baz, 2:40)'
657 663 abort: line range exceeds file size
658 664 [255]
659 665
660 666 Test annotate with whitespace options
661 667
662 668 $ cd ..
663 669 $ hg init repo-ws
664 670 $ cd repo-ws
665 671 $ cat > a <<EOF
666 672 > aa
667 673 >
668 674 > b b
669 675 > EOF
670 676 $ hg ci -Am "adda"
671 677 adding a
672 678 $ sed 's/EOL$//g' > a <<EOF
673 679 > a a
674 680 >
675 681 > EOL
676 682 > b b
677 683 > EOF
678 684 $ hg ci -m "changea"
679 685
680 686 Annotate with no option
681 687
682 688 $ hg annotate a
683 689 1: a a
684 690 0:
685 691 1:
686 692 1: b b
687 693
688 694 Annotate with --ignore-space-change
689 695
690 696 $ hg annotate --ignore-space-change a
691 697 1: a a
692 698 1:
693 699 0:
694 700 0: b b
695 701
696 702 Annotate with --ignore-all-space
697 703
698 704 $ hg annotate --ignore-all-space a
699 705 0: a a
700 706 0:
701 707 1:
702 708 0: b b
703 709
704 710 Annotate with --ignore-blank-lines (similar to no options case)
705 711
706 712 $ hg annotate --ignore-blank-lines a
707 713 1: a a
708 714 0:
709 715 1:
710 716 1: b b
711 717
712 718 $ cd ..
713 719
714 720 Annotate with linkrev pointing to another branch
715 721 ------------------------------------------------
716 722
717 723 create history with a filerev whose linkrev points to another branch
718 724
719 725 $ hg init branchedlinkrev
720 726 $ cd branchedlinkrev
721 727 $ echo A > a
722 728 $ hg commit -Am 'contentA'
723 729 adding a
724 730 $ echo B >> a
725 731 $ hg commit -m 'contentB'
726 732 $ hg up --rev 'desc(contentA)'
727 733 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
728 734 $ echo unrelated > unrelated
729 735 $ hg commit -Am 'unrelated'
730 736 adding unrelated
731 737 created new head
732 738 $ hg graft -r 'desc(contentB)'
733 739 grafting 1:fd27c222e3e6 "contentB"
734 740 $ echo C >> a
735 741 $ hg commit -m 'contentC'
736 742 $ echo W >> a
737 743 $ hg log -G
738 744 @ changeset: 4:072f1e8df249
739 745 | tag: tip
740 746 | user: test
741 747 | date: Thu Jan 01 00:00:00 1970 +0000
742 748 | summary: contentC
743 749 |
744 750 o changeset: 3:ff38df03cc4b
745 751 | user: test
746 752 | date: Thu Jan 01 00:00:00 1970 +0000
747 753 | summary: contentB
748 754 |
749 755 o changeset: 2:62aaf3f6fc06
750 756 | parent: 0:f0932f74827e
751 757 | user: test
752 758 | date: Thu Jan 01 00:00:00 1970 +0000
753 759 | summary: unrelated
754 760 |
755 761 | o changeset: 1:fd27c222e3e6
756 762 |/ user: test
757 763 | date: Thu Jan 01 00:00:00 1970 +0000
758 764 | summary: contentB
759 765 |
760 766 o changeset: 0:f0932f74827e
761 767 user: test
762 768 date: Thu Jan 01 00:00:00 1970 +0000
763 769 summary: contentA
764 770
765 771
766 772 Annotate should list ancestor of starting revision only
767 773
768 774 $ hg annotate a
769 775 0: A
770 776 3: B
771 777 4: C
772 778
773 779 $ hg annotate a -r 'wdir()'
774 780 0 : A
775 781 3 : B
776 782 4 : C
777 783 4+: W
778 784
779 785 Even when the starting revision is the linkrev-shadowed one:
780 786
781 787 $ hg annotate a -r 3
782 788 0: A
783 789 3: B
784 790
785 791 $ cd ..
786 792
787 793 Issue5360: Deleted chunk in p1 of a merge changeset
788 794
789 795 $ hg init repo-5360
790 796 $ cd repo-5360
791 797 $ echo 1 > a
792 798 $ hg commit -A a -m 1
793 799 $ echo 2 >> a
794 800 $ hg commit -m 2
795 801 $ echo a > a
796 802 $ hg commit -m a
797 803 $ hg update '.^' -q
798 804 $ echo 3 >> a
799 805 $ hg commit -m 3 -q
800 806 $ hg merge 2 -q
801 807 $ cat > a << EOF
802 808 > b
803 809 > 1
804 810 > 2
805 811 > 3
806 812 > a
807 813 > EOF
808 814 $ hg resolve --mark -q
809 815 $ hg commit -m m
810 816 $ hg annotate a
811 817 4: b
812 818 0: 1
813 819 1: 2
814 820 3: 3
815 821 2: a
816 822
817 823 $ cd ..
@@ -1,1805 +1,1837
1 1 #require serve
2 2
3 3 $ hg init test
4 4 $ cd test
5 5 $ echo b > b
6 6 $ hg ci -Am "b"
7 7 adding b
8 8 $ echo a > a
9 9 $ hg ci -Am "first a"
10 10 adding a
11 11 $ hg tag -r 1 a-tag
12 12 $ hg bookmark -r 1 a-bookmark
13 13 $ hg rm a
14 14 $ hg ci -m "del a"
15 15 $ hg branch a-branch
16 16 marked working directory as branch a-branch
17 17 (branches are permanent and global, did you want a bookmark?)
18 18 $ echo b > a
19 19 $ hg ci -Am "second a"
20 20 adding a
21 21 $ hg rm a
22 22 $ hg ci -m "del2 a"
23 23 $ hg mv b c
24 24 $ hg ci -m "mv b"
25 25 $ echo c >> c
26 26 $ hg ci -m "change c"
27 27 $ hg log -p
28 28 changeset: 7:46c1a66bd8fc
29 29 branch: a-branch
30 30 tag: tip
31 31 user: test
32 32 date: Thu Jan 01 00:00:00 1970 +0000
33 33 summary: change c
34 34
35 35 diff -r c9637d3cc8ef -r 46c1a66bd8fc c
36 36 --- a/c Thu Jan 01 00:00:00 1970 +0000
37 37 +++ b/c Thu Jan 01 00:00:00 1970 +0000
38 38 @@ -1,1 +1,2 @@
39 39 b
40 40 +c
41 41
42 42 changeset: 6:c9637d3cc8ef
43 43 branch: a-branch
44 44 user: test
45 45 date: Thu Jan 01 00:00:00 1970 +0000
46 46 summary: mv b
47 47
48 48 diff -r 958bd88be4eb -r c9637d3cc8ef b
49 49 --- a/b Thu Jan 01 00:00:00 1970 +0000
50 50 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
51 51 @@ -1,1 +0,0 @@
52 52 -b
53 53 diff -r 958bd88be4eb -r c9637d3cc8ef c
54 54 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
55 55 +++ b/c Thu Jan 01 00:00:00 1970 +0000
56 56 @@ -0,0 +1,1 @@
57 57 +b
58 58
59 59 changeset: 5:958bd88be4eb
60 60 branch: a-branch
61 61 user: test
62 62 date: Thu Jan 01 00:00:00 1970 +0000
63 63 summary: del2 a
64 64
65 65 diff -r 3f41bc784e7e -r 958bd88be4eb a
66 66 --- a/a Thu Jan 01 00:00:00 1970 +0000
67 67 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
68 68 @@ -1,1 +0,0 @@
69 69 -b
70 70
71 71 changeset: 4:3f41bc784e7e
72 72 branch: a-branch
73 73 user: test
74 74 date: Thu Jan 01 00:00:00 1970 +0000
75 75 summary: second a
76 76
77 77 diff -r 292258f86fdf -r 3f41bc784e7e a
78 78 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
79 79 +++ b/a Thu Jan 01 00:00:00 1970 +0000
80 80 @@ -0,0 +1,1 @@
81 81 +b
82 82
83 83 changeset: 3:292258f86fdf
84 84 user: test
85 85 date: Thu Jan 01 00:00:00 1970 +0000
86 86 summary: del a
87 87
88 88 diff -r 94c9dd5ca9b4 -r 292258f86fdf a
89 89 --- a/a Thu Jan 01 00:00:00 1970 +0000
90 90 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
91 91 @@ -1,1 +0,0 @@
92 92 -a
93 93
94 94 changeset: 2:94c9dd5ca9b4
95 95 user: test
96 96 date: Thu Jan 01 00:00:00 1970 +0000
97 97 summary: Added tag a-tag for changeset 5ed941583260
98 98
99 99 diff -r 5ed941583260 -r 94c9dd5ca9b4 .hgtags
100 100 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
101 101 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
102 102 @@ -0,0 +1,1 @@
103 103 +5ed941583260248620985524192fdc382ef57c36 a-tag
104 104
105 105 changeset: 1:5ed941583260
106 106 bookmark: a-bookmark
107 107 tag: a-tag
108 108 user: test
109 109 date: Thu Jan 01 00:00:00 1970 +0000
110 110 summary: first a
111 111
112 112 diff -r 6563da9dcf87 -r 5ed941583260 a
113 113 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
114 114 +++ b/a Thu Jan 01 00:00:00 1970 +0000
115 115 @@ -0,0 +1,1 @@
116 116 +a
117 117
118 118 changeset: 0:6563da9dcf87
119 119 user: test
120 120 date: Thu Jan 01 00:00:00 1970 +0000
121 121 summary: b
122 122
123 123 diff -r 000000000000 -r 6563da9dcf87 b
124 124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
125 125 +++ b/b Thu Jan 01 00:00:00 1970 +0000
126 126 @@ -0,0 +1,1 @@
127 127 +b
128 128
129 129 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log
130 130 $ cat hg.pid >> $DAEMON_PIDS
131 131
132 132 tip - two revisions
133 133
134 134 $ (get-with-headers.py localhost:$HGPORT 'log/tip/a')
135 135 200 Script output follows
136 136
137 137 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
138 138 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
139 139 <head>
140 140 <link rel="icon" href="/static/hgicon.png" type="image/png" />
141 141 <meta name="robots" content="index, nofollow" />
142 142 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
143 143 <script type="text/javascript" src="/static/mercurial.js"></script>
144 144
145 145 <title>test: a history</title>
146 146 <link rel="alternate" type="application/atom+xml"
147 147 href="/atom-log/tip/a" title="Atom feed for test:a" />
148 148 <link rel="alternate" type="application/rss+xml"
149 149 href="/rss-log/tip/a" title="RSS feed for test:a" />
150 150 </head>
151 151 <body>
152 152
153 153 <div class="container">
154 154 <div class="menu">
155 155 <div class="logo">
156 156 <a href="https://mercurial-scm.org/">
157 157 <img src="/static/hglogo.png" alt="mercurial" /></a>
158 158 </div>
159 159 <ul>
160 160 <li><a href="/shortlog/tip">log</a></li>
161 161 <li><a href="/graph/tip">graph</a></li>
162 162 <li><a href="/tags">tags</a></li>
163 163 <li><a href="/bookmarks">bookmarks</a></li>
164 164 <li><a href="/branches">branches</a></li>
165 165 </ul>
166 166 <ul>
167 167 <li><a href="/rev/tip">changeset</a></li>
168 168 <li><a href="/file/tip">browse</a></li>
169 169 </ul>
170 170 <ul>
171 171 <li><a href="/file/tip/a">file</a></li>
172 172 <li><a href="/diff/tip/a">diff</a></li>
173 173 <li><a href="/comparison/tip/a">comparison</a></li>
174 174 <li><a href="/annotate/tip/a">annotate</a></li>
175 175 <li class="active">file log</li>
176 176 <li><a href="/raw-file/tip/a">raw</a></li>
177 177 </ul>
178 178 <ul>
179 179 <li><a href="/help">help</a></li>
180 180 </ul>
181 181 <div class="atom-logo">
182 182 <a href="/atom-log/tip/a" title="subscribe to atom feed">
183 183 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
184 184 </a>
185 185 </div>
186 186 </div>
187 187
188 188 <div class="main">
189 189 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
190 190 <h3>
191 191 log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
192 192 <span class="branchname">a-branch</span>
193 193
194 194 </h3>
195 195
196 196 <form class="search" action="/log">
197 197
198 198 <p><input name="rev" id="search1" type="text" size="30" /></p>
199 199 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
200 200 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
201 201 </form>
202 202
203 203 <div class="navigate">
204 204 <a href="/log/tip/a?revcount=30">less</a>
205 205 <a href="/log/tip/a?revcount=120">more</a>
206 206 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
207 207
208 208 <table class="bigtable">
209 209 <thead>
210 210 <tr>
211 211 <th class="age">age</th>
212 212 <th class="author">author</th>
213 213 <th class="description">description</th>
214 214 </tr>
215 215 </thead>
216 216 <tbody class="stripes2">
217 217 <tr>
218 218 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
219 219 <td class="author">test</td>
220 220 <td class="description">
221 221 <a href="/rev/3f41bc784e7e">second a</a>
222 222 <span class="branchname">a-branch</span>
223 223 </td>
224 224 </tr>
225 225
226 226 <tr>
227 227 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
228 228 <td class="author">test</td>
229 229 <td class="description">
230 230 <a href="/rev/5ed941583260">first a</a>
231 231 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
232 232 </td>
233 233 </tr>
234 234
235 235
236 236 </tbody>
237 237 </table>
238 238
239 239 <div class="navigate">
240 240 <a href="/log/tip/a?revcount=30">less</a>
241 241 <a href="/log/tip/a?revcount=120">more</a>
242 242 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
243 243 </div>
244 244
245 245 </div>
246 246 </div>
247 247
248 248
249 249
250 250 </body>
251 251 </html>
252 252
253 253
254 254 second version - two revisions
255 255
256 256 $ (get-with-headers.py localhost:$HGPORT 'log/4/a')
257 257 200 Script output follows
258 258
259 259 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
260 260 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
261 261 <head>
262 262 <link rel="icon" href="/static/hgicon.png" type="image/png" />
263 263 <meta name="robots" content="index, nofollow" />
264 264 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
265 265 <script type="text/javascript" src="/static/mercurial.js"></script>
266 266
267 267 <title>test: a history</title>
268 268 <link rel="alternate" type="application/atom+xml"
269 269 href="/atom-log/tip/a" title="Atom feed for test:a" />
270 270 <link rel="alternate" type="application/rss+xml"
271 271 href="/rss-log/tip/a" title="RSS feed for test:a" />
272 272 </head>
273 273 <body>
274 274
275 275 <div class="container">
276 276 <div class="menu">
277 277 <div class="logo">
278 278 <a href="https://mercurial-scm.org/">
279 279 <img src="/static/hglogo.png" alt="mercurial" /></a>
280 280 </div>
281 281 <ul>
282 282 <li><a href="/shortlog/4">log</a></li>
283 283 <li><a href="/graph/4">graph</a></li>
284 284 <li><a href="/tags">tags</a></li>
285 285 <li><a href="/bookmarks">bookmarks</a></li>
286 286 <li><a href="/branches">branches</a></li>
287 287 </ul>
288 288 <ul>
289 289 <li><a href="/rev/4">changeset</a></li>
290 290 <li><a href="/file/4">browse</a></li>
291 291 </ul>
292 292 <ul>
293 293 <li><a href="/file/4/a">file</a></li>
294 294 <li><a href="/diff/4/a">diff</a></li>
295 295 <li><a href="/comparison/4/a">comparison</a></li>
296 296 <li><a href="/annotate/4/a">annotate</a></li>
297 297 <li class="active">file log</li>
298 298 <li><a href="/raw-file/4/a">raw</a></li>
299 299 </ul>
300 300 <ul>
301 301 <li><a href="/help">help</a></li>
302 302 </ul>
303 303 <div class="atom-logo">
304 304 <a href="/atom-log/tip/a" title="subscribe to atom feed">
305 305 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
306 306 </a>
307 307 </div>
308 308 </div>
309 309
310 310 <div class="main">
311 311 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
312 312 <h3>
313 313 log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
314 314 <span class="branchname">a-branch</span>
315 315
316 316 </h3>
317 317
318 318 <form class="search" action="/log">
319 319
320 320 <p><input name="rev" id="search1" type="text" size="30" /></p>
321 321 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
322 322 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
323 323 </form>
324 324
325 325 <div class="navigate">
326 326 <a href="/log/4/a?revcount=30">less</a>
327 327 <a href="/log/4/a?revcount=120">more</a>
328 328 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
329 329
330 330 <table class="bigtable">
331 331 <thead>
332 332 <tr>
333 333 <th class="age">age</th>
334 334 <th class="author">author</th>
335 335 <th class="description">description</th>
336 336 </tr>
337 337 </thead>
338 338 <tbody class="stripes2">
339 339 <tr>
340 340 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
341 341 <td class="author">test</td>
342 342 <td class="description">
343 343 <a href="/rev/3f41bc784e7e">second a</a>
344 344 <span class="branchname">a-branch</span>
345 345 </td>
346 346 </tr>
347 347
348 348 <tr>
349 349 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
350 350 <td class="author">test</td>
351 351 <td class="description">
352 352 <a href="/rev/5ed941583260">first a</a>
353 353 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
354 354 </td>
355 355 </tr>
356 356
357 357
358 358 </tbody>
359 359 </table>
360 360
361 361 <div class="navigate">
362 362 <a href="/log/4/a?revcount=30">less</a>
363 363 <a href="/log/4/a?revcount=120">more</a>
364 364 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
365 365 </div>
366 366
367 367 </div>
368 368 </div>
369 369
370 370
371 371
372 372 </body>
373 373 </html>
374 374
375 375
376 376 first deleted - one revision
377 377
378 378 $ (get-with-headers.py localhost:$HGPORT 'log/3/a')
379 379 200 Script output follows
380 380
381 381 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
382 382 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
383 383 <head>
384 384 <link rel="icon" href="/static/hgicon.png" type="image/png" />
385 385 <meta name="robots" content="index, nofollow" />
386 386 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
387 387 <script type="text/javascript" src="/static/mercurial.js"></script>
388 388
389 389 <title>test: a history</title>
390 390 <link rel="alternate" type="application/atom+xml"
391 391 href="/atom-log/tip/a" title="Atom feed for test:a" />
392 392 <link rel="alternate" type="application/rss+xml"
393 393 href="/rss-log/tip/a" title="RSS feed for test:a" />
394 394 </head>
395 395 <body>
396 396
397 397 <div class="container">
398 398 <div class="menu">
399 399 <div class="logo">
400 400 <a href="https://mercurial-scm.org/">
401 401 <img src="/static/hglogo.png" alt="mercurial" /></a>
402 402 </div>
403 403 <ul>
404 404 <li><a href="/shortlog/3">log</a></li>
405 405 <li><a href="/graph/3">graph</a></li>
406 406 <li><a href="/tags">tags</a></li>
407 407 <li><a href="/bookmarks">bookmarks</a></li>
408 408 <li><a href="/branches">branches</a></li>
409 409 </ul>
410 410 <ul>
411 411 <li><a href="/rev/3">changeset</a></li>
412 412 <li><a href="/file/3">browse</a></li>
413 413 </ul>
414 414 <ul>
415 415 <li><a href="/file/3/a">file</a></li>
416 416 <li><a href="/diff/3/a">diff</a></li>
417 417 <li><a href="/comparison/3/a">comparison</a></li>
418 418 <li><a href="/annotate/3/a">annotate</a></li>
419 419 <li class="active">file log</li>
420 420 <li><a href="/raw-file/3/a">raw</a></li>
421 421 </ul>
422 422 <ul>
423 423 <li><a href="/help">help</a></li>
424 424 </ul>
425 425 <div class="atom-logo">
426 426 <a href="/atom-log/tip/a" title="subscribe to atom feed">
427 427 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
428 428 </a>
429 429 </div>
430 430 </div>
431 431
432 432 <div class="main">
433 433 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
434 434 <h3>
435 435 log a @ 1:<a href="/rev/5ed941583260">5ed941583260</a>
436 436 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
437 437
438 438 </h3>
439 439
440 440 <form class="search" action="/log">
441 441
442 442 <p><input name="rev" id="search1" type="text" size="30" /></p>
443 443 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
444 444 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
445 445 </form>
446 446
447 447 <div class="navigate">
448 448 <a href="/log/3/a?revcount=30">less</a>
449 449 <a href="/log/3/a?revcount=120">more</a>
450 450 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
451 451
452 452 <table class="bigtable">
453 453 <thead>
454 454 <tr>
455 455 <th class="age">age</th>
456 456 <th class="author">author</th>
457 457 <th class="description">description</th>
458 458 </tr>
459 459 </thead>
460 460 <tbody class="stripes2">
461 461 <tr>
462 462 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
463 463 <td class="author">test</td>
464 464 <td class="description">
465 465 <a href="/rev/5ed941583260">first a</a>
466 466 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
467 467 </td>
468 468 </tr>
469 469
470 470
471 471 </tbody>
472 472 </table>
473 473
474 474 <div class="navigate">
475 475 <a href="/log/3/a?revcount=30">less</a>
476 476 <a href="/log/3/a?revcount=120">more</a>
477 477 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
478 478 </div>
479 479
480 480 </div>
481 481 </div>
482 482
483 483
484 484
485 485 </body>
486 486 </html>
487 487
488 488
489 489 first version - one revision
490 490
491 491 $ (get-with-headers.py localhost:$HGPORT 'log/1/a')
492 492 200 Script output follows
493 493
494 494 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
495 495 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
496 496 <head>
497 497 <link rel="icon" href="/static/hgicon.png" type="image/png" />
498 498 <meta name="robots" content="index, nofollow" />
499 499 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
500 500 <script type="text/javascript" src="/static/mercurial.js"></script>
501 501
502 502 <title>test: a history</title>
503 503 <link rel="alternate" type="application/atom+xml"
504 504 href="/atom-log/tip/a" title="Atom feed for test:a" />
505 505 <link rel="alternate" type="application/rss+xml"
506 506 href="/rss-log/tip/a" title="RSS feed for test:a" />
507 507 </head>
508 508 <body>
509 509
510 510 <div class="container">
511 511 <div class="menu">
512 512 <div class="logo">
513 513 <a href="https://mercurial-scm.org/">
514 514 <img src="/static/hglogo.png" alt="mercurial" /></a>
515 515 </div>
516 516 <ul>
517 517 <li><a href="/shortlog/1">log</a></li>
518 518 <li><a href="/graph/1">graph</a></li>
519 519 <li><a href="/tags">tags</a></li>
520 520 <li><a href="/bookmarks">bookmarks</a></li>
521 521 <li><a href="/branches">branches</a></li>
522 522 </ul>
523 523 <ul>
524 524 <li><a href="/rev/1">changeset</a></li>
525 525 <li><a href="/file/1">browse</a></li>
526 526 </ul>
527 527 <ul>
528 528 <li><a href="/file/1/a">file</a></li>
529 529 <li><a href="/diff/1/a">diff</a></li>
530 530 <li><a href="/comparison/1/a">comparison</a></li>
531 531 <li><a href="/annotate/1/a">annotate</a></li>
532 532 <li class="active">file log</li>
533 533 <li><a href="/raw-file/1/a">raw</a></li>
534 534 </ul>
535 535 <ul>
536 536 <li><a href="/help">help</a></li>
537 537 </ul>
538 538 <div class="atom-logo">
539 539 <a href="/atom-log/tip/a" title="subscribe to atom feed">
540 540 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
541 541 </a>
542 542 </div>
543 543 </div>
544 544
545 545 <div class="main">
546 546 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
547 547 <h3>
548 548 log a @ 1:<a href="/rev/5ed941583260">5ed941583260</a>
549 549 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
550 550
551 551 </h3>
552 552
553 553 <form class="search" action="/log">
554 554
555 555 <p><input name="rev" id="search1" type="text" size="30" /></p>
556 556 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
557 557 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
558 558 </form>
559 559
560 560 <div class="navigate">
561 561 <a href="/log/1/a?revcount=30">less</a>
562 562 <a href="/log/1/a?revcount=120">more</a>
563 563 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
564 564
565 565 <table class="bigtable">
566 566 <thead>
567 567 <tr>
568 568 <th class="age">age</th>
569 569 <th class="author">author</th>
570 570 <th class="description">description</th>
571 571 </tr>
572 572 </thead>
573 573 <tbody class="stripes2">
574 574 <tr>
575 575 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
576 576 <td class="author">test</td>
577 577 <td class="description">
578 578 <a href="/rev/5ed941583260">first a</a>
579 579 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
580 580 </td>
581 581 </tr>
582 582
583 583
584 584 </tbody>
585 585 </table>
586 586
587 587 <div class="navigate">
588 588 <a href="/log/1/a?revcount=30">less</a>
589 589 <a href="/log/1/a?revcount=120">more</a>
590 590 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
591 591 </div>
592 592
593 593 </div>
594 594 </div>
595 595
596 596
597 597
598 598 </body>
599 599 </html>
600 600
601 601
602 602 before addition - error
603 603
604 604 $ (get-with-headers.py localhost:$HGPORT 'log/0/a')
605 605 404 Not Found
606 606
607 607 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
608 608 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
609 609 <head>
610 610 <link rel="icon" href="/static/hgicon.png" type="image/png" />
611 611 <meta name="robots" content="index, nofollow" />
612 612 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
613 613 <script type="text/javascript" src="/static/mercurial.js"></script>
614 614
615 615 <title>test: error</title>
616 616 </head>
617 617 <body>
618 618
619 619 <div class="container">
620 620 <div class="menu">
621 621 <div class="logo">
622 622 <a href="https://mercurial-scm.org/">
623 623 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
624 624 </div>
625 625 <ul>
626 626 <li><a href="/shortlog">log</a></li>
627 627 <li><a href="/graph">graph</a></li>
628 628 <li><a href="/tags">tags</a></li>
629 629 <li><a href="/bookmarks">bookmarks</a></li>
630 630 <li><a href="/branches">branches</a></li>
631 631 </ul>
632 632 <ul>
633 633 <li><a href="/help">help</a></li>
634 634 </ul>
635 635 </div>
636 636
637 637 <div class="main">
638 638
639 639 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
640 640 <h3>error</h3>
641 641
642 642 <form class="search" action="/log">
643 643
644 644 <p><input name="rev" id="search1" type="text" size="30"></p>
645 645 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
646 646 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
647 647 </form>
648 648
649 649 <div class="description">
650 650 <p>
651 651 An error occurred while processing your request:
652 652 </p>
653 653 <p>
654 654 a@6563da9dcf87: not found in manifest
655 655 </p>
656 656 </div>
657 657 </div>
658 658 </div>
659 659
660 660
661 661
662 662 </body>
663 663 </html>
664 664
665 665 [1]
666 666
667 667 $ hg log -r 'followlines(c, 1:2, startrev=tip) and follow(c)'
668 668 changeset: 0:6563da9dcf87
669 669 user: test
670 670 date: Thu Jan 01 00:00:00 1970 +0000
671 671 summary: b
672 672
673 673 changeset: 7:46c1a66bd8fc
674 674 branch: a-branch
675 675 tag: tip
676 676 user: test
677 677 date: Thu Jan 01 00:00:00 1970 +0000
678 678 summary: change c
679 679
680 680 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?linerange=1:2')
681 681 200 Script output follows
682 682
683 683 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
684 684 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
685 685 <head>
686 686 <link rel="icon" href="/static/hgicon.png" type="image/png" />
687 687 <meta name="robots" content="index, nofollow" />
688 688 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
689 689 <script type="text/javascript" src="/static/mercurial.js"></script>
690 690
691 691 <title>test: c history</title>
692 692 <link rel="alternate" type="application/atom+xml"
693 693 href="/atom-log/tip/c" title="Atom feed for test:c" />
694 694 <link rel="alternate" type="application/rss+xml"
695 695 href="/rss-log/tip/c" title="RSS feed for test:c" />
696 696 </head>
697 697 <body>
698 698
699 699 <div class="container">
700 700 <div class="menu">
701 701 <div class="logo">
702 702 <a href="https://mercurial-scm.org/">
703 703 <img src="/static/hglogo.png" alt="mercurial" /></a>
704 704 </div>
705 705 <ul>
706 706 <li><a href="/shortlog/tip">log</a></li>
707 707 <li><a href="/graph/tip">graph</a></li>
708 708 <li><a href="/tags">tags</a></li>
709 709 <li><a href="/bookmarks">bookmarks</a></li>
710 710 <li><a href="/branches">branches</a></li>
711 711 </ul>
712 712 <ul>
713 713 <li><a href="/rev/tip">changeset</a></li>
714 714 <li><a href="/file/tip">browse</a></li>
715 715 </ul>
716 716 <ul>
717 717 <li><a href="/file/tip/c">file</a></li>
718 718 <li><a href="/diff/tip/c">diff</a></li>
719 719 <li><a href="/comparison/tip/c">comparison</a></li>
720 720 <li><a href="/annotate/tip/c">annotate</a></li>
721 721 <li class="active">file log</li>
722 722 <li><a href="/raw-file/tip/c">raw</a></li>
723 723 </ul>
724 724 <ul>
725 725 <li><a href="/help">help</a></li>
726 726 </ul>
727 727 <div class="atom-logo">
728 728 <a href="/atom-log/tip/c" title="subscribe to atom feed">
729 729 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
730 730 </a>
731 731 </div>
732 732 </div>
733 733
734 734 <div class="main">
735 735 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
736 736 <h3>
737 737 log c @ 7:<a href="/rev/46c1a66bd8fc">46c1a66bd8fc</a>
738 738 <span class="branchname">a-branch</span> <span class="tag">tip</span>
739 739 (following lines 1:2 <a href="/log/tip/c">back to filelog</a>)
740 740 </h3>
741 741
742 742 <form class="search" action="/log">
743 743
744 744 <p><input name="rev" id="search1" type="text" size="30" /></p>
745 745 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
746 746 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
747 747 </form>
748 748
749 749 <div class="navigate">
750 750 <a href="/log/tip/c?linerange=1%3A2&revcount=30">less</a>
751 751 <a href="/log/tip/c?linerange=1%3A2&revcount=120">more</a>
752 752 | </div>
753 753
754 754 <table class="bigtable">
755 755 <thead>
756 756 <tr>
757 757 <th class="age">age</th>
758 758 <th class="author">author</th>
759 759 <th class="description">description</th>
760 760 </tr>
761 761 </thead>
762 762 <tbody class="stripes2">
763 763 <tr>
764 764 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
765 765 <td class="author">test</td>
766 766 <td class="description">
767 767 <a href="/rev/46c1a66bd8fc">change c</a>
768 768 <span class="branchhead">a-branch</span> <span class="tag">tip</span>
769 769 </td>
770 770 </tr>
771 771
772 772 <tr>
773 773 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
774 774 <td class="author">test</td>
775 775 <td class="description">
776 776 <a href="/rev/6563da9dcf87">b</a>
777 777
778 778 </td>
779 779 </tr>
780 780
781 781
782 782 </tbody>
783 783 </table>
784 784
785 785 <div class="navigate">
786 786 <a href="/log/tip/c?linerange=1%3A2&revcount=30">less</a>
787 787 <a href="/log/tip/c?linerange=1%3A2&revcount=120">more</a>
788 788 |
789 789 </div>
790 790
791 791 </div>
792 792 </div>
793 793
794 794
795 795
796 796 </body>
797 797 </html>
798 798
799 799 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?linerange=1%3A2&revcount=1')
800 800 200 Script output follows
801 801
802 802 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
803 803 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
804 804 <head>
805 805 <link rel="icon" href="/static/hgicon.png" type="image/png" />
806 806 <meta name="robots" content="index, nofollow" />
807 807 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
808 808 <script type="text/javascript" src="/static/mercurial.js"></script>
809 809
810 810 <title>test: c history</title>
811 811 <link rel="alternate" type="application/atom+xml"
812 812 href="/atom-log/tip/c" title="Atom feed for test:c" />
813 813 <link rel="alternate" type="application/rss+xml"
814 814 href="/rss-log/tip/c" title="RSS feed for test:c" />
815 815 </head>
816 816 <body>
817 817
818 818 <div class="container">
819 819 <div class="menu">
820 820 <div class="logo">
821 821 <a href="https://mercurial-scm.org/">
822 822 <img src="/static/hglogo.png" alt="mercurial" /></a>
823 823 </div>
824 824 <ul>
825 825 <li><a href="/shortlog/tip?revcount=1">log</a></li>
826 826 <li><a href="/graph/tip?revcount=1">graph</a></li>
827 827 <li><a href="/tags?revcount=1">tags</a></li>
828 828 <li><a href="/bookmarks?revcount=1">bookmarks</a></li>
829 829 <li><a href="/branches?revcount=1">branches</a></li>
830 830 </ul>
831 831 <ul>
832 832 <li><a href="/rev/tip?revcount=1">changeset</a></li>
833 833 <li><a href="/file/tip?revcount=1">browse</a></li>
834 834 </ul>
835 835 <ul>
836 836 <li><a href="/file/tip/c?revcount=1">file</a></li>
837 837 <li><a href="/diff/tip/c?revcount=1">diff</a></li>
838 838 <li><a href="/comparison/tip/c?revcount=1">comparison</a></li>
839 839 <li><a href="/annotate/tip/c?revcount=1">annotate</a></li>
840 840 <li class="active">file log</li>
841 841 <li><a href="/raw-file/tip/c">raw</a></li>
842 842 </ul>
843 843 <ul>
844 844 <li><a href="/help?revcount=1">help</a></li>
845 845 </ul>
846 846 <div class="atom-logo">
847 847 <a href="/atom-log/tip/c" title="subscribe to atom feed">
848 848 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
849 849 </a>
850 850 </div>
851 851 </div>
852 852
853 853 <div class="main">
854 854 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
855 855 <h3>
856 856 log c @ 7:<a href="/rev/46c1a66bd8fc?revcount=1">46c1a66bd8fc</a>
857 857 <span class="branchname">a-branch</span> <span class="tag">tip</span>
858 858 (following lines 1:2 <a href="/log/tip/c?revcount=1">back to filelog</a>)
859 859 </h3>
860 860
861 861 <form class="search" action="/log">
862 862 <input type="hidden" name="revcount" value="1" />
863 863 <p><input name="rev" id="search1" type="text" size="30" /></p>
864 864 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
865 865 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
866 866 </form>
867 867
868 868 <div class="navigate">
869 869 <a href="/log/tip/c?linerange=1%3A2&revcount=1">less</a>
870 870 <a href="/log/tip/c?linerange=1%3A2&revcount=2">more</a>
871 871 | </div>
872 872
873 873 <table class="bigtable">
874 874 <thead>
875 875 <tr>
876 876 <th class="age">age</th>
877 877 <th class="author">author</th>
878 878 <th class="description">description</th>
879 879 </tr>
880 880 </thead>
881 881 <tbody class="stripes2">
882 882 <tr>
883 883 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
884 884 <td class="author">test</td>
885 885 <td class="description">
886 886 <a href="/rev/46c1a66bd8fc?revcount=1">change c</a>
887 887 <span class="branchhead">a-branch</span> <span class="tag">tip</span>
888 888 </td>
889 889 </tr>
890 890
891 891
892 892 </tbody>
893 893 </table>
894 894
895 895 <div class="navigate">
896 896 <a href="/log/tip/c?linerange=1%3A2&revcount=1">less</a>
897 897 <a href="/log/tip/c?linerange=1%3A2&revcount=2">more</a>
898 898 |
899 899 </div>
900 900
901 901 </div>
902 902 </div>
903 903
904 904
905 905
906 906 </body>
907 907 </html>
908 908
909 909 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=1' --headeronly)
910 910 400 invalid linerange parameter
911 911 [1]
912 912 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=1:a' --headeronly)
913 913 400 invalid linerange parameter
914 914 [1]
915 915 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=1:2&linerange=3:4' --headeronly)
916 916 400 redundant linerange parameter
917 917 [1]
918 918 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=3:2' --headeronly)
919 919 400 line range must be positive
920 920 [1]
921 921 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=0:1' --headeronly)
922 922 400 fromline must be strictly positive
923 923 [1]
924 924
925 925 should show base link, use spartan because it shows it
926 926
927 927 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?style=spartan')
928 928 200 Script output follows
929 929
930 930 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
931 931 <html>
932 932 <head>
933 933 <link rel="icon" href="/static/hgicon.png" type="image/png">
934 934 <meta name="robots" content="index, nofollow" />
935 935 <link rel="stylesheet" href="/static/style.css" type="text/css" />
936 936 <script type="text/javascript" src="/static/mercurial.js"></script>
937 937
938 938 <title>test: c history</title>
939 939 <link rel="alternate" type="application/atom+xml"
940 940 href="/atom-log/tip/c" title="Atom feed for test:c">
941 941 <link rel="alternate" type="application/rss+xml"
942 942 href="/rss-log/tip/c" title="RSS feed for test:c">
943 943 </head>
944 944 <body>
945 945
946 946 <div class="buttons">
947 947 <a href="/log?style=spartan">changelog</a>
948 948 <a href="/shortlog?style=spartan">shortlog</a>
949 949 <a href="/graph?style=spartan">graph</a>
950 950 <a href="/tags?style=spartan">tags</a>
951 951 <a href="/branches?style=spartan">branches</a>
952 952 <a href="/file/tip/c?style=spartan">file</a>
953 953 <a href="/annotate/tip/c?style=spartan">annotate</a>
954 954 <a href="/help?style=spartan">help</a>
955 955 <a type="application/rss+xml" href="/rss-log/tip/c">rss</a>
956 956 <a type="application/atom+xml" href="/atom-log/tip/c" title="Atom feed for test:c">atom</a>
957 957 </div>
958 958
959 959 <h2><a href="/">Mercurial</a> / c revision history</h2>
960 960
961 961 <p>navigate: <small class="navigate"><a href="/log/c9637d3cc8ef/c?style=spartan">(0)</a> <a href="/log/tip/c?style=spartan">tip</a> </small></p>
962 962
963 963 <table class="logEntry parity0">
964 964 <tr>
965 965 <th class="label"><span class="age">Thu, 01 Jan 1970 00:00:00 +0000</span>:</th>
966 966 <th class="firstline"><a href="/rev/46c1a66bd8fc?style=spartan">change c</a></th>
967 967 </tr>
968 968 <tr>
969 969 <th class="revision">revision 1:</th>
970 970 <td class="node">
971 971 <a href="/file/46c1a66bd8fc/c?style=spartan">46c1a66bd8fc</a>
972 972 <a href="/diff/46c1a66bd8fc/c?style=spartan">(diff)</a>
973 973 <a href="/annotate/46c1a66bd8fc/c?style=spartan">(annotate)</a>
974 974 </td>
975 975 </tr>
976 976
977 977 <tr>
978 978 <th class="author">author:</th>
979 979 <td class="author">&#116;&#101;&#115;&#116;</td>
980 980 </tr>
981 981 <tr>
982 982 <th class="date">date:</th>
983 983 <td class="date">Thu, 01 Jan 1970 00:00:00 +0000</td>
984 984 </tr>
985 985 </table>
986 986
987 987
988 988 <table class="logEntry parity1">
989 989 <tr>
990 990 <th class="label"><span class="age">Thu, 01 Jan 1970 00:00:00 +0000</span>:</th>
991 991 <th class="firstline"><a href="/rev/c9637d3cc8ef?style=spartan">mv b</a></th>
992 992 </tr>
993 993 <tr>
994 994 <th class="revision">revision 0:</th>
995 995 <td class="node">
996 996 <a href="/file/c9637d3cc8ef/c?style=spartan">c9637d3cc8ef</a>
997 997 <a href="/diff/c9637d3cc8ef/c?style=spartan">(diff)</a>
998 998 <a href="/annotate/c9637d3cc8ef/c?style=spartan">(annotate)</a>
999 999 </td>
1000 1000 </tr>
1001 1001
1002 1002 <tr>
1003 1003 <th>base:</th>
1004 1004 <td>
1005 1005 <a href="/file/1e88685f5dde/b?style=spartan">
1006 1006 b@1e88685f5dde
1007 1007 </a>
1008 1008 </td>
1009 1009 </tr>
1010 1010 <tr>
1011 1011 <th class="author">author:</th>
1012 1012 <td class="author">&#116;&#101;&#115;&#116;</td>
1013 1013 </tr>
1014 1014 <tr>
1015 1015 <th class="date">date:</th>
1016 1016 <td class="date">Thu, 01 Jan 1970 00:00:00 +0000</td>
1017 1017 </tr>
1018 1018 </table>
1019 1019
1020 1020
1021 1021
1022 1022
1023 1023
1024 1024 <div class="logo">
1025 1025 <a href="https://mercurial-scm.org/">
1026 1026 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
1027 1027 </div>
1028 1028
1029 1029 </body>
1030 1030 </html>
1031 1031
1032 1032
1033 1033 filelog with patch
1034 1034
1035 1035 $ (get-with-headers.py localhost:$HGPORT 'log/4/a?patch=1')
1036 1036 200 Script output follows
1037 1037
1038 1038 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1039 1039 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1040 1040 <head>
1041 1041 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1042 1042 <meta name="robots" content="index, nofollow" />
1043 1043 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1044 1044 <script type="text/javascript" src="/static/mercurial.js"></script>
1045 1045
1046 1046 <title>test: a history</title>
1047 1047 <link rel="alternate" type="application/atom+xml"
1048 1048 href="/atom-log/tip/a" title="Atom feed for test:a" />
1049 1049 <link rel="alternate" type="application/rss+xml"
1050 1050 href="/rss-log/tip/a" title="RSS feed for test:a" />
1051 1051 </head>
1052 1052 <body>
1053 1053
1054 1054 <div class="container">
1055 1055 <div class="menu">
1056 1056 <div class="logo">
1057 1057 <a href="https://mercurial-scm.org/">
1058 1058 <img src="/static/hglogo.png" alt="mercurial" /></a>
1059 1059 </div>
1060 1060 <ul>
1061 1061 <li><a href="/shortlog/4">log</a></li>
1062 1062 <li><a href="/graph/4">graph</a></li>
1063 1063 <li><a href="/tags">tags</a></li>
1064 1064 <li><a href="/bookmarks">bookmarks</a></li>
1065 1065 <li><a href="/branches">branches</a></li>
1066 1066 </ul>
1067 1067 <ul>
1068 1068 <li><a href="/rev/4">changeset</a></li>
1069 1069 <li><a href="/file/4">browse</a></li>
1070 1070 </ul>
1071 1071 <ul>
1072 1072 <li><a href="/file/4/a">file</a></li>
1073 1073 <li><a href="/diff/4/a">diff</a></li>
1074 1074 <li><a href="/comparison/4/a">comparison</a></li>
1075 1075 <li><a href="/annotate/4/a">annotate</a></li>
1076 1076 <li class="active">file log</li>
1077 1077 <li><a href="/raw-file/4/a">raw</a></li>
1078 1078 </ul>
1079 1079 <ul>
1080 1080 <li><a href="/help">help</a></li>
1081 1081 </ul>
1082 1082 <div class="atom-logo">
1083 1083 <a href="/atom-log/tip/a" title="subscribe to atom feed">
1084 1084 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
1085 1085 </a>
1086 1086 </div>
1087 1087 </div>
1088 1088
1089 1089 <div class="main">
1090 1090 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1091 1091 <h3>
1092 1092 log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
1093 1093 <span class="branchname">a-branch</span>
1094 1094
1095 1095 </h3>
1096 1096
1097 1097 <form class="search" action="/log">
1098 1098
1099 1099 <p><input name="rev" id="search1" type="text" size="30" /></p>
1100 1100 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1101 1101 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1102 1102 </form>
1103 1103
1104 1104 <div class="navigate">
1105 1105 <a href="/log/4/a?patch=1&revcount=30">less</a>
1106 1106 <a href="/log/4/a?patch=1&revcount=120">more</a>
1107 1107 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
1108 1108
1109 1109 <table class="bigtable">
1110 1110 <thead>
1111 1111 <tr>
1112 1112 <th class="age">age</th>
1113 1113 <th class="author">author</th>
1114 1114 <th class="description">description</th>
1115 1115 </tr>
1116 1116 </thead>
1117 1117 <tbody class="stripes2">
1118 1118 <tr>
1119 1119 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1120 1120 <td class="author">test</td>
1121 1121 <td class="description">
1122 1122 <a href="/rev/3f41bc784e7e">second a</a>
1123 1123 <span class="branchname">a-branch</span>
1124 1124 </td>
1125 1125 </tr>
1126 1126 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1127 1127 <span id="3f41bc784e7e-l1.1" class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000</span><a href="#3f41bc784e7e-l1.1"></a>
1128 1128 <span id="3f41bc784e7e-l1.2" class="plusline">+++ b/a Thu Jan 01 00:00:00 1970 +0000</span><a href="#3f41bc784e7e-l1.2"></a>
1129 1129 <span id="3f41bc784e7e-l1.3" class="atline">@@ -0,0 +1,1 @@</span><a href="#3f41bc784e7e-l1.3"></a>
1130 1130 <span id="3f41bc784e7e-l1.4" class="plusline">+b</span><a href="#3f41bc784e7e-l1.4"></a></pre></div></td></tr>
1131 1131 <tr>
1132 1132 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1133 1133 <td class="author">test</td>
1134 1134 <td class="description">
1135 1135 <a href="/rev/5ed941583260">first a</a>
1136 1136 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
1137 1137 </td>
1138 1138 </tr>
1139 1139 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1140 1140 <span id="5ed941583260-l1.1" class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000</span><a href="#5ed941583260-l1.1"></a>
1141 1141 <span id="5ed941583260-l1.2" class="plusline">+++ b/a Thu Jan 01 00:00:00 1970 +0000</span><a href="#5ed941583260-l1.2"></a>
1142 1142 <span id="5ed941583260-l1.3" class="atline">@@ -0,0 +1,1 @@</span><a href="#5ed941583260-l1.3"></a>
1143 1143 <span id="5ed941583260-l1.4" class="plusline">+a</span><a href="#5ed941583260-l1.4"></a></pre></div></td></tr>
1144 1144
1145 1145 </tbody>
1146 1146 </table>
1147 1147
1148 1148 <div class="navigate">
1149 1149 <a href="/log/4/a?patch=1&revcount=30">less</a>
1150 1150 <a href="/log/4/a?patch=1&revcount=120">more</a>
1151 1151 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
1152 1152 </div>
1153 1153
1154 1154 </div>
1155 1155 </div>
1156 1156
1157 1157
1158 1158
1159 1159 </body>
1160 1160 </html>
1161 1161
1162 1162 filelog with 'linerange' and 'patch'
1163 1163
1164 1164 $ cat c
1165 1165 b
1166 1166 c
1167 1167 $ cat <<EOF > c
1168 1168 > 0
1169 1169 > 0
1170 1170 > b
1171 1171 > c+
1172 1172 >
1173 1173 > a
1174 1174 > a
1175 1175 >
1176 1176 > d
1177 1177 > e
1178 1178 > f
1179 1179 > EOF
1180 1180 $ hg ci -m 'make c bigger and touch its beginning' c
1181 1181 $ cat <<EOF > c
1182 1182 > 0
1183 1183 > 0
1184 1184 > b
1185 1185 > c+
1186 1186 >
1187 1187 > a
1188 1188 > a
1189 1189 >
1190 1190 > d
1191 1191 > e+
1192 1192 > f
1193 1193 > EOF
1194 1194 $ hg ci -m 'just touch end of c' c
1195 1195 $ cat <<EOF > c
1196 1196 > 0
1197 1197 > 0
1198 1198 > b
1199 1199 > c++
1200 1200 >
1201 1201 > a
1202 1202 > a
1203 1203 >
1204 1204 > d
1205 1205 > e+
1206 1206 > f
1207 1207 > EOF
1208 1208 $ hg ci -m 'touch beginning of c' c
1209 1209 $ cat <<EOF > c
1210 1210 > 0
1211 1211 > 0
1212 1212 > b-
1213 1213 > c++
1214 1214 >
1215 1215 > a
1216 1216 > a
1217 1217 >
1218 1218 > d
1219 1219 > e+
1220 1220 > f+
1221 1221 > EOF
1222 1222 $ hg ci -m 'touching beginning and end of c' c
1223 1223 $ hg log -r 'followlines(c, 3:4, startrev=tip) and follow(c)' -p
1224 1224 changeset: 0:6563da9dcf87
1225 1225 user: test
1226 1226 date: Thu Jan 01 00:00:00 1970 +0000
1227 1227 summary: b
1228 1228
1229 1229 diff -r 000000000000 -r 6563da9dcf87 b
1230 1230 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1231 1231 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1232 1232 @@ -0,0 +1,1 @@
1233 1233 +b
1234 1234
1235 1235 changeset: 7:46c1a66bd8fc
1236 1236 branch: a-branch
1237 1237 user: test
1238 1238 date: Thu Jan 01 00:00:00 1970 +0000
1239 1239 summary: change c
1240 1240
1241 1241 diff -r c9637d3cc8ef -r 46c1a66bd8fc c
1242 1242 --- a/c Thu Jan 01 00:00:00 1970 +0000
1243 1243 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1244 1244 @@ -1,1 +1,2 @@
1245 1245 b
1246 1246 +c
1247 1247
1248 1248 changeset: 8:5c6574614c37
1249 1249 branch: a-branch
1250 1250 user: test
1251 1251 date: Thu Jan 01 00:00:00 1970 +0000
1252 1252 summary: make c bigger and touch its beginning
1253 1253
1254 1254 diff -r 46c1a66bd8fc -r 5c6574614c37 c
1255 1255 --- a/c Thu Jan 01 00:00:00 1970 +0000
1256 1256 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1257 1257 @@ -1,2 +1,11 @@
1258 1258 +0
1259 1259 +0
1260 1260 b
1261 1261 -c
1262 1262 +c+
1263 1263 +
1264 1264 +a
1265 1265 +a
1266 1266 +
1267 1267 +d
1268 1268 +e
1269 1269 +f
1270 1270
1271 1271 changeset: 10:e95928d60479
1272 1272 branch: a-branch
1273 1273 user: test
1274 1274 date: Thu Jan 01 00:00:00 1970 +0000
1275 1275 summary: touch beginning of c
1276 1276
1277 1277 diff -r e1d3e9c5a23f -r e95928d60479 c
1278 1278 --- a/c Thu Jan 01 00:00:00 1970 +0000
1279 1279 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1280 1280 @@ -1,7 +1,7 @@
1281 1281 0
1282 1282 0
1283 1283 b
1284 1284 -c+
1285 1285 +c++
1286 1286
1287 1287 a
1288 1288 a
1289 1289
1290 1290 changeset: 11:fb9bc322513a
1291 1291 branch: a-branch
1292 1292 tag: tip
1293 1293 user: test
1294 1294 date: Thu Jan 01 00:00:00 1970 +0000
1295 1295 summary: touching beginning and end of c
1296 1296
1297 1297 diff -r e95928d60479 -r fb9bc322513a c
1298 1298 --- a/c Thu Jan 01 00:00:00 1970 +0000
1299 1299 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1300 1300 @@ -1,6 +1,6 @@
1301 1301 0
1302 1302 0
1303 1303 -b
1304 1304 +b-
1305 1305 c++
1306 1306
1307 1307 a
1308 1308 @@ -8,4 +8,4 @@
1309 1309
1310 1310 d
1311 1311 e+
1312 1312 -f
1313 1313 +f+
1314 1314
1315 1315 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?linerange=3:4&patch=')
1316 1316 200 Script output follows
1317 1317
1318 1318 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1319 1319 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1320 1320 <head>
1321 1321 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1322 1322 <meta name="robots" content="index, nofollow" />
1323 1323 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1324 1324 <script type="text/javascript" src="/static/mercurial.js"></script>
1325 1325
1326 1326 <title>test: c history</title>
1327 1327 <link rel="alternate" type="application/atom+xml"
1328 1328 href="/atom-log/tip/c" title="Atom feed for test:c" />
1329 1329 <link rel="alternate" type="application/rss+xml"
1330 1330 href="/rss-log/tip/c" title="RSS feed for test:c" />
1331 1331 </head>
1332 1332 <body>
1333 1333
1334 1334 <div class="container">
1335 1335 <div class="menu">
1336 1336 <div class="logo">
1337 1337 <a href="https://mercurial-scm.org/">
1338 1338 <img src="/static/hglogo.png" alt="mercurial" /></a>
1339 1339 </div>
1340 1340 <ul>
1341 1341 <li><a href="/shortlog/tip">log</a></li>
1342 1342 <li><a href="/graph/tip">graph</a></li>
1343 1343 <li><a href="/tags">tags</a></li>
1344 1344 <li><a href="/bookmarks">bookmarks</a></li>
1345 1345 <li><a href="/branches">branches</a></li>
1346 1346 </ul>
1347 1347 <ul>
1348 1348 <li><a href="/rev/tip">changeset</a></li>
1349 1349 <li><a href="/file/tip">browse</a></li>
1350 1350 </ul>
1351 1351 <ul>
1352 1352 <li><a href="/file/tip/c">file</a></li>
1353 1353 <li><a href="/diff/tip/c">diff</a></li>
1354 1354 <li><a href="/comparison/tip/c">comparison</a></li>
1355 1355 <li><a href="/annotate/tip/c">annotate</a></li>
1356 1356 <li class="active">file log</li>
1357 1357 <li><a href="/raw-file/tip/c">raw</a></li>
1358 1358 </ul>
1359 1359 <ul>
1360 1360 <li><a href="/help">help</a></li>
1361 1361 </ul>
1362 1362 <div class="atom-logo">
1363 1363 <a href="/atom-log/tip/c" title="subscribe to atom feed">
1364 1364 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
1365 1365 </a>
1366 1366 </div>
1367 1367 </div>
1368 1368
1369 1369 <div class="main">
1370 1370 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1371 1371 <h3>
1372 1372 log c @ 11:<a href="/rev/fb9bc322513a">fb9bc322513a</a>
1373 1373 <span class="branchname">a-branch</span> <span class="tag">tip</span>
1374 1374 (following lines 3:4 <a href="/log/tip/c">back to filelog</a>)
1375 1375 </h3>
1376 1376
1377 1377 <form class="search" action="/log">
1378 1378
1379 1379 <p><input name="rev" id="search1" type="text" size="30" /></p>
1380 1380 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1381 1381 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1382 1382 </form>
1383 1383
1384 1384 <div class="navigate">
1385 1385 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=30">less</a>
1386 1386 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=120">more</a>
1387 1387 | </div>
1388 1388
1389 1389 <table class="bigtable">
1390 1390 <thead>
1391 1391 <tr>
1392 1392 <th class="age">age</th>
1393 1393 <th class="author">author</th>
1394 1394 <th class="description">description</th>
1395 1395 </tr>
1396 1396 </thead>
1397 1397 <tbody class="stripes2">
1398 1398 <tr>
1399 1399 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1400 1400 <td class="author">test</td>
1401 1401 <td class="description">
1402 1402 <a href="/rev/fb9bc322513a">touching beginning and end of c</a>
1403 1403 <span class="branchhead">a-branch</span> <span class="tag">tip</span>
1404 1404 </td>
1405 1405 </tr>
1406 1406 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1407 1407 <span id="fb9bc322513a-l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#fb9bc322513a-l1.1"></a>
1408 1408 <span id="fb9bc322513a-l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#fb9bc322513a-l1.2"></a>
1409 1409 <span id="fb9bc322513a-l1.3" class="atline">@@ -1,6 +1,6 @@</span><a href="#fb9bc322513a-l1.3"></a>
1410 1410 <span id="fb9bc322513a-l1.4"> 0</span><a href="#fb9bc322513a-l1.4"></a>
1411 1411 <span id="fb9bc322513a-l1.5"> 0</span><a href="#fb9bc322513a-l1.5"></a>
1412 1412 <span id="fb9bc322513a-l1.6" class="minusline">-b</span><a href="#fb9bc322513a-l1.6"></a>
1413 1413 <span id="fb9bc322513a-l1.7" class="plusline">+b-</span><a href="#fb9bc322513a-l1.7"></a>
1414 1414 <span id="fb9bc322513a-l1.8"> c++</span><a href="#fb9bc322513a-l1.8"></a>
1415 1415 <span id="fb9bc322513a-l1.9"> </span><a href="#fb9bc322513a-l1.9"></a>
1416 1416 <span id="fb9bc322513a-l1.10"> a</span><a href="#fb9bc322513a-l1.10"></a></pre></div></td></tr>
1417 1417 <tr>
1418 1418 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1419 1419 <td class="author">test</td>
1420 1420 <td class="description">
1421 1421 <a href="/rev/e95928d60479">touch beginning of c</a>
1422 1422 <span class="branchname">a-branch</span>
1423 1423 </td>
1424 1424 </tr>
1425 1425 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1426 1426 <span id="e95928d60479-l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#e95928d60479-l1.1"></a>
1427 1427 <span id="e95928d60479-l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#e95928d60479-l1.2"></a>
1428 1428 <span id="e95928d60479-l1.3" class="atline">@@ -1,7 +1,7 @@</span><a href="#e95928d60479-l1.3"></a>
1429 1429 <span id="e95928d60479-l1.4"> 0</span><a href="#e95928d60479-l1.4"></a>
1430 1430 <span id="e95928d60479-l1.5"> 0</span><a href="#e95928d60479-l1.5"></a>
1431 1431 <span id="e95928d60479-l1.6"> b</span><a href="#e95928d60479-l1.6"></a>
1432 1432 <span id="e95928d60479-l1.7" class="minusline">-c+</span><a href="#e95928d60479-l1.7"></a>
1433 1433 <span id="e95928d60479-l1.8" class="plusline">+c++</span><a href="#e95928d60479-l1.8"></a>
1434 1434 <span id="e95928d60479-l1.9"> </span><a href="#e95928d60479-l1.9"></a>
1435 1435 <span id="e95928d60479-l1.10"> a</span><a href="#e95928d60479-l1.10"></a>
1436 1436 <span id="e95928d60479-l1.11"> a</span><a href="#e95928d60479-l1.11"></a></pre></div></td></tr>
1437 1437 <tr>
1438 1438 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1439 1439 <td class="author">test</td>
1440 1440 <td class="description">
1441 1441 <a href="/rev/5c6574614c37">make c bigger and touch its beginning</a>
1442 1442 <span class="branchname">a-branch</span>
1443 1443 </td>
1444 1444 </tr>
1445 1445 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1446 1446 <span id="5c6574614c37-l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#5c6574614c37-l1.1"></a>
1447 1447 <span id="5c6574614c37-l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#5c6574614c37-l1.2"></a>
1448 1448 <span id="5c6574614c37-l1.3" class="atline">@@ -1,2 +1,11 @@</span><a href="#5c6574614c37-l1.3"></a>
1449 1449 <span id="5c6574614c37-l1.4" class="plusline">+0</span><a href="#5c6574614c37-l1.4"></a>
1450 1450 <span id="5c6574614c37-l1.5" class="plusline">+0</span><a href="#5c6574614c37-l1.5"></a>
1451 1451 <span id="5c6574614c37-l1.6"> b</span><a href="#5c6574614c37-l1.6"></a>
1452 1452 <span id="5c6574614c37-l1.7" class="minusline">-c</span><a href="#5c6574614c37-l1.7"></a>
1453 1453 <span id="5c6574614c37-l1.8" class="plusline">+c+</span><a href="#5c6574614c37-l1.8"></a>
1454 1454 <span id="5c6574614c37-l1.9" class="plusline">+</span><a href="#5c6574614c37-l1.9"></a>
1455 1455 <span id="5c6574614c37-l1.10" class="plusline">+a</span><a href="#5c6574614c37-l1.10"></a>
1456 1456 <span id="5c6574614c37-l1.11" class="plusline">+a</span><a href="#5c6574614c37-l1.11"></a>
1457 1457 <span id="5c6574614c37-l1.12" class="plusline">+</span><a href="#5c6574614c37-l1.12"></a>
1458 1458 <span id="5c6574614c37-l1.13" class="plusline">+d</span><a href="#5c6574614c37-l1.13"></a>
1459 1459 <span id="5c6574614c37-l1.14" class="plusline">+e</span><a href="#5c6574614c37-l1.14"></a>
1460 1460 <span id="5c6574614c37-l1.15" class="plusline">+f</span><a href="#5c6574614c37-l1.15"></a></pre></div></td></tr>
1461 1461 <tr>
1462 1462 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1463 1463 <td class="author">test</td>
1464 1464 <td class="description">
1465 1465 <a href="/rev/46c1a66bd8fc">change c</a>
1466 1466 <span class="branchname">a-branch</span>
1467 1467 </td>
1468 1468 </tr>
1469 1469 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1470 1470 <span id="46c1a66bd8fc-l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#46c1a66bd8fc-l1.1"></a>
1471 1471 <span id="46c1a66bd8fc-l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#46c1a66bd8fc-l1.2"></a>
1472 1472 <span id="46c1a66bd8fc-l1.3" class="atline">@@ -1,1 +1,2 @@</span><a href="#46c1a66bd8fc-l1.3"></a>
1473 1473 <span id="46c1a66bd8fc-l1.4"> b</span><a href="#46c1a66bd8fc-l1.4"></a>
1474 1474 <span id="46c1a66bd8fc-l1.5" class="plusline">+c</span><a href="#46c1a66bd8fc-l1.5"></a></pre></div></td></tr>
1475 1475 <tr>
1476 1476 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1477 1477 <td class="author">test</td>
1478 1478 <td class="description">
1479 1479 <a href="/rev/6563da9dcf87">b</a>
1480 1480
1481 1481 </td>
1482 1482 </tr>
1483 1483 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1484 1484 <span id="6563da9dcf87-l1.1" class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000</span><a href="#6563da9dcf87-l1.1"></a>
1485 1485 <span id="6563da9dcf87-l1.2" class="plusline">+++ b/b Thu Jan 01 00:00:00 1970 +0000</span><a href="#6563da9dcf87-l1.2"></a></pre></div></td></tr>
1486 1486
1487 1487 </tbody>
1488 1488 </table>
1489 1489
1490 1490 <div class="navigate">
1491 1491 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=30">less</a>
1492 1492 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=120">more</a>
1493 1493 |
1494 1494 </div>
1495 1495
1496 1496 </div>
1497 1497 </div>
1498 1498
1499 1499
1500 1500
1501 1501 </body>
1502 1502 </html>
1503 1503
1504 1504 $ hg log -r 'followlines(c, 3:4, startrev=8, descend=True) and follow(c)' -p
1505 changeset: 8:5c6574614c37
1506 branch: a-branch
1507 user: test
1508 date: Thu Jan 01 00:00:00 1970 +0000
1509 summary: make c bigger and touch its beginning
1510
1511 diff -r 46c1a66bd8fc -r 5c6574614c37 c
1512 --- a/c Thu Jan 01 00:00:00 1970 +0000
1513 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1514 @@ -1,2 +1,11 @@
1515 +0
1516 +0
1517 b
1518 -c
1519 +c+
1520 +
1521 +a
1522 +a
1523 +
1524 +d
1525 +e
1526 +f
1527
1505 1528 changeset: 10:e95928d60479
1506 1529 branch: a-branch
1507 1530 user: test
1508 1531 date: Thu Jan 01 00:00:00 1970 +0000
1509 1532 summary: touch beginning of c
1510 1533
1511 1534 diff -r e1d3e9c5a23f -r e95928d60479 c
1512 1535 --- a/c Thu Jan 01 00:00:00 1970 +0000
1513 1536 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1514 1537 @@ -1,7 +1,7 @@
1515 1538 0
1516 1539 0
1517 1540 b
1518 1541 -c+
1519 1542 +c++
1520 1543
1521 1544 a
1522 1545 a
1523 1546
1524 1547 changeset: 11:fb9bc322513a
1525 1548 branch: a-branch
1526 1549 tag: tip
1527 1550 user: test
1528 1551 date: Thu Jan 01 00:00:00 1970 +0000
1529 1552 summary: touching beginning and end of c
1530 1553
1531 1554 diff -r e95928d60479 -r fb9bc322513a c
1532 1555 --- a/c Thu Jan 01 00:00:00 1970 +0000
1533 1556 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1534 1557 @@ -1,6 +1,6 @@
1535 1558 0
1536 1559 0
1537 1560 -b
1538 1561 +b-
1539 1562 c++
1540 1563
1541 1564 a
1542 1565 @@ -8,4 +8,4 @@
1543 1566
1544 1567 d
1545 1568 e+
1546 1569 -f
1547 1570 +f+
1548 1571
1549 1572 $ (get-with-headers.py localhost:$HGPORT 'log/8/c?linerange=3:4&descend=')
1550 1573 200 Script output follows
1551 1574
1552 1575 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1553 1576 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1554 1577 <head>
1555 1578 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1556 1579 <meta name="robots" content="index, nofollow" />
1557 1580 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1558 1581 <script type="text/javascript" src="/static/mercurial.js"></script>
1559 1582
1560 1583 <title>test: c history</title>
1561 1584 <link rel="alternate" type="application/atom+xml"
1562 1585 href="/atom-log/tip/c" title="Atom feed for test:c" />
1563 1586 <link rel="alternate" type="application/rss+xml"
1564 1587 href="/rss-log/tip/c" title="RSS feed for test:c" />
1565 1588 </head>
1566 1589 <body>
1567 1590
1568 1591 <div class="container">
1569 1592 <div class="menu">
1570 1593 <div class="logo">
1571 1594 <a href="https://mercurial-scm.org/">
1572 1595 <img src="/static/hglogo.png" alt="mercurial" /></a>
1573 1596 </div>
1574 1597 <ul>
1575 1598 <li><a href="/shortlog/8">log</a></li>
1576 1599 <li><a href="/graph/8">graph</a></li>
1577 1600 <li><a href="/tags">tags</a></li>
1578 1601 <li><a href="/bookmarks">bookmarks</a></li>
1579 1602 <li><a href="/branches">branches</a></li>
1580 1603 </ul>
1581 1604 <ul>
1582 1605 <li><a href="/rev/8">changeset</a></li>
1583 1606 <li><a href="/file/8">browse</a></li>
1584 1607 </ul>
1585 1608 <ul>
1586 1609 <li><a href="/file/8/c">file</a></li>
1587 1610 <li><a href="/diff/8/c">diff</a></li>
1588 1611 <li><a href="/comparison/8/c">comparison</a></li>
1589 1612 <li><a href="/annotate/8/c">annotate</a></li>
1590 1613 <li class="active">file log</li>
1591 1614 <li><a href="/raw-file/8/c">raw</a></li>
1592 1615 </ul>
1593 1616 <ul>
1594 1617 <li><a href="/help">help</a></li>
1595 1618 </ul>
1596 1619 <div class="atom-logo">
1597 1620 <a href="/atom-log/tip/c" title="subscribe to atom feed">
1598 1621 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
1599 1622 </a>
1600 1623 </div>
1601 1624 </div>
1602 1625
1603 1626 <div class="main">
1604 1627 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1605 1628 <h3>
1606 1629 log c @ 8:<a href="/rev/5c6574614c37">5c6574614c37</a>
1607 1630 <span class="branchname">a-branch</span>
1608 1631 (following lines 3:4, descending <a href="/log/8/c">back to filelog</a>)
1609 1632 </h3>
1610 1633
1611 1634 <form class="search" action="/log">
1612 1635
1613 1636 <p><input name="rev" id="search1" type="text" size="30" /></p>
1614 1637 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1615 1638 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1616 1639 </form>
1617 1640
1618 1641 <div class="navigate">
1619 1642 <a href="/log/8/c?descend=&linerange=3%3A4&revcount=30">less</a>
1620 1643 <a href="/log/8/c?descend=&linerange=3%3A4&revcount=120">more</a>
1621 1644 | </div>
1622 1645
1623 1646 <table class="bigtable">
1624 1647 <thead>
1625 1648 <tr>
1626 1649 <th class="age">age</th>
1627 1650 <th class="author">author</th>
1628 1651 <th class="description">description</th>
1629 1652 </tr>
1630 1653 </thead>
1631 1654 <tbody class="stripes2">
1632 1655 <tr>
1633 1656 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1634 1657 <td class="author">test</td>
1635 1658 <td class="description">
1659 <a href="/rev/5c6574614c37">make c bigger and touch its beginning</a>
1660 <span class="branchname">a-branch</span>
1661 </td>
1662 </tr>
1663
1664 <tr>
1665 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1666 <td class="author">test</td>
1667 <td class="description">
1636 1668 <a href="/rev/e95928d60479">touch beginning of c</a>
1637 1669 <span class="branchname">a-branch</span>
1638 1670 </td>
1639 1671 </tr>
1640 1672
1641 1673 <tr>
1642 1674 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1643 1675 <td class="author">test</td>
1644 1676 <td class="description">
1645 1677 <a href="/rev/fb9bc322513a">touching beginning and end of c</a>
1646 1678 <span class="branchhead">a-branch</span> <span class="tag">tip</span>
1647 1679 </td>
1648 1680 </tr>
1649 1681
1650 1682
1651 1683 </tbody>
1652 1684 </table>
1653 1685
1654 1686 <div class="navigate">
1655 1687 <a href="/log/8/c?descend=&linerange=3%3A4&revcount=30">less</a>
1656 1688 <a href="/log/8/c?descend=&linerange=3%3A4&revcount=120">more</a>
1657 1689 |
1658 1690 </div>
1659 1691
1660 1692 </div>
1661 1693 </div>
1662 1694
1663 1695
1664 1696
1665 1697 </body>
1666 1698 </html>
1667 1699
1668 1700
1669 1701 rss log
1670 1702
1671 1703 $ (get-with-headers.py localhost:$HGPORT 'rss-log/tip/a')
1672 1704 200 Script output follows
1673 1705
1674 1706 <?xml version="1.0" encoding="ascii"?>
1675 1707 <rss version="2.0">
1676 1708 <channel>
1677 1709 <link>http://*:$HGPORT/</link> (glob)
1678 1710 <language>en-us</language>
1679 1711
1680 1712 <title>test: a history</title>
1681 1713 <description>a revision history</description>
1682 1714 <item>
1683 1715 <title>second a</title>
1684 1716 <link>http://*:$HGPORT/log/3f41bc784e7e/a</link> (glob)
1685 1717 <description><![CDATA[second a]]></description>
1686 1718 <author>&#116;&#101;&#115;&#116;</author>
1687 1719 <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
1688 1720 </item>
1689 1721 <item>
1690 1722 <title>first a</title>
1691 1723 <link>http://*:$HGPORT/log/5ed941583260/a</link> (glob)
1692 1724 <description><![CDATA[first a]]></description>
1693 1725 <author>&#116;&#101;&#115;&#116;</author>
1694 1726 <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
1695 1727 </item>
1696 1728
1697 1729 </channel>
1698 1730 </rss>
1699 1731
1700 1732 atom log
1701 1733
1702 1734 $ (get-with-headers.py localhost:$HGPORT 'atom-log/tip/a')
1703 1735 200 Script output follows
1704 1736
1705 1737 <?xml version="1.0" encoding="ascii"?>
1706 1738 <feed xmlns="http://www.w3.org/2005/Atom">
1707 1739 <id>http://*:$HGPORT/atom-log/tip/a</id> (glob)
1708 1740 <link rel="self" href="http://*:$HGPORT/atom-log/tip/a"/> (glob)
1709 1741 <title>test: a history</title>
1710 1742 <updated>1970-01-01T00:00:00+00:00</updated>
1711 1743
1712 1744 <entry>
1713 1745 <title>[a-branch] second a</title>
1714 1746 <id>http://*:$HGPORT/#changeset-3f41bc784e7e73035c6d47112c6cc7efb673adf8</id> (glob)
1715 1747 <link href="http://*:$HGPORT/rev/3f41bc784e7e"/> (glob)
1716 1748 <author>
1717 1749 <name>test</name>
1718 1750 <email>&#116;&#101;&#115;&#116;</email>
1719 1751 </author>
1720 1752 <updated>1970-01-01T00:00:00+00:00</updated>
1721 1753 <published>1970-01-01T00:00:00+00:00</published>
1722 1754 <content type="xhtml">
1723 1755 <table xmlns="http://www.w3.org/1999/xhtml">
1724 1756 <tr>
1725 1757 <th style="text-align:left;">changeset</th>
1726 1758 <td>3f41bc784e7e</td>
1727 1759 </tr>
1728 1760 <tr>
1729 1761 <th style="text-align:left;">branch</th>
1730 1762 <td>a-branch</td>
1731 1763 </tr>
1732 1764 <tr>
1733 1765 <th style="text-align:left;">bookmark</th>
1734 1766 <td></td>
1735 1767 </tr>
1736 1768 <tr>
1737 1769 <th style="text-align:left;">tag</th>
1738 1770 <td></td>
1739 1771 </tr>
1740 1772 <tr>
1741 1773 <th style="text-align:left;">user</th>
1742 1774 <td>&#116;&#101;&#115;&#116;</td>
1743 1775 </tr>
1744 1776 <tr>
1745 1777 <th style="text-align:left;vertical-align:top;">description</th>
1746 1778 <td>second a</td>
1747 1779 </tr>
1748 1780 <tr>
1749 1781 <th style="text-align:left;vertical-align:top;">files</th>
1750 1782 <td></td>
1751 1783 </tr>
1752 1784 </table>
1753 1785 </content>
1754 1786 </entry>
1755 1787 <entry>
1756 1788 <title>first a</title>
1757 1789 <id>http://*:$HGPORT/#changeset-5ed941583260248620985524192fdc382ef57c36</id> (glob)
1758 1790 <link href="http://*:$HGPORT/rev/5ed941583260"/> (glob)
1759 1791 <author>
1760 1792 <name>test</name>
1761 1793 <email>&#116;&#101;&#115;&#116;</email>
1762 1794 </author>
1763 1795 <updated>1970-01-01T00:00:00+00:00</updated>
1764 1796 <published>1970-01-01T00:00:00+00:00</published>
1765 1797 <content type="xhtml">
1766 1798 <table xmlns="http://www.w3.org/1999/xhtml">
1767 1799 <tr>
1768 1800 <th style="text-align:left;">changeset</th>
1769 1801 <td>5ed941583260</td>
1770 1802 </tr>
1771 1803 <tr>
1772 1804 <th style="text-align:left;">branch</th>
1773 1805 <td></td>
1774 1806 </tr>
1775 1807 <tr>
1776 1808 <th style="text-align:left;">bookmark</th>
1777 1809 <td>a-bookmark</td>
1778 1810 </tr>
1779 1811 <tr>
1780 1812 <th style="text-align:left;">tag</th>
1781 1813 <td>a-tag</td>
1782 1814 </tr>
1783 1815 <tr>
1784 1816 <th style="text-align:left;">user</th>
1785 1817 <td>&#116;&#101;&#115;&#116;</td>
1786 1818 </tr>
1787 1819 <tr>
1788 1820 <th style="text-align:left;vertical-align:top;">description</th>
1789 1821 <td>first a</td>
1790 1822 </tr>
1791 1823 <tr>
1792 1824 <th style="text-align:left;vertical-align:top;">files</th>
1793 1825 <td></td>
1794 1826 </tr>
1795 1827 </table>
1796 1828 </content>
1797 1829 </entry>
1798 1830
1799 1831 </feed>
1800 1832
1801 1833 errors
1802 1834
1803 1835 $ cat errors.log
1804 1836
1805 1837 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now