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