##// END OF EJS Templates
context: fix bug introduced in fb89cd21a7a0, path should be used
Benoit Boissinot -
r10927:c1d375e9 stable
parent child Browse files
Show More
@@ -1,928 +1,928 b''
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 node import nullid, nullrev, short, hex
9 9 from i18n import _
10 10 import ancestor, bdiff, error, util, subrepo
11 11 import os, errno
12 12
13 13 propertycache = util.propertycache
14 14
15 15 class changectx(object):
16 16 """A changecontext object makes access to data related to a particular
17 17 changeset convenient."""
18 18 def __init__(self, repo, changeid=''):
19 19 """changeid is a revision number, node, or tag"""
20 20 if changeid == '':
21 21 changeid = '.'
22 22 self._repo = repo
23 23 if isinstance(changeid, (long, int)):
24 24 self._rev = changeid
25 25 self._node = self._repo.changelog.node(changeid)
26 26 else:
27 27 self._node = self._repo.lookup(changeid)
28 28 self._rev = self._repo.changelog.rev(self._node)
29 29
30 30 def __str__(self):
31 31 return short(self.node())
32 32
33 33 def __int__(self):
34 34 return self.rev()
35 35
36 36 def __repr__(self):
37 37 return "<changectx %s>" % str(self)
38 38
39 39 def __hash__(self):
40 40 try:
41 41 return hash(self._rev)
42 42 except AttributeError:
43 43 return id(self)
44 44
45 45 def __eq__(self, other):
46 46 try:
47 47 return self._rev == other._rev
48 48 except AttributeError:
49 49 return False
50 50
51 51 def __ne__(self, other):
52 52 return not (self == other)
53 53
54 54 def __nonzero__(self):
55 55 return self._rev != nullrev
56 56
57 57 @propertycache
58 58 def _changeset(self):
59 59 return self._repo.changelog.read(self.node())
60 60
61 61 @propertycache
62 62 def _manifest(self):
63 63 return self._repo.manifest.read(self._changeset[0])
64 64
65 65 @propertycache
66 66 def _manifestdelta(self):
67 67 return self._repo.manifest.readdelta(self._changeset[0])
68 68
69 69 @propertycache
70 70 def _parents(self):
71 71 p = self._repo.changelog.parentrevs(self._rev)
72 72 if p[1] == nullrev:
73 73 p = p[:-1]
74 74 return [changectx(self._repo, x) for x in p]
75 75
76 76 @propertycache
77 77 def substate(self):
78 78 return subrepo.state(self)
79 79
80 80 def __contains__(self, key):
81 81 return key in self._manifest
82 82
83 83 def __getitem__(self, key):
84 84 return self.filectx(key)
85 85
86 86 def __iter__(self):
87 87 for f in sorted(self._manifest):
88 88 yield f
89 89
90 90 def changeset(self):
91 91 return self._changeset
92 92 def manifest(self):
93 93 return self._manifest
94 94 def manifestnode(self):
95 95 return self._changeset[0]
96 96
97 97 def rev(self):
98 98 return self._rev
99 99 def node(self):
100 100 return self._node
101 101 def hex(self):
102 102 return hex(self._node)
103 103 def user(self):
104 104 return self._changeset[1]
105 105 def date(self):
106 106 return self._changeset[2]
107 107 def files(self):
108 108 return self._changeset[3]
109 109 def description(self):
110 110 return self._changeset[4]
111 111 def branch(self):
112 112 return self._changeset[5].get("branch")
113 113 def extra(self):
114 114 return self._changeset[5]
115 115 def tags(self):
116 116 return self._repo.nodetags(self._node)
117 117
118 118 def parents(self):
119 119 """return contexts for each parent changeset"""
120 120 return self._parents
121 121
122 122 def p1(self):
123 123 return self._parents[0]
124 124
125 125 def p2(self):
126 126 if len(self._parents) == 2:
127 127 return self._parents[1]
128 128 return changectx(self._repo, -1)
129 129
130 130 def children(self):
131 131 """return contexts for each child changeset"""
132 132 c = self._repo.changelog.children(self._node)
133 133 return [changectx(self._repo, x) for x in c]
134 134
135 135 def ancestors(self):
136 136 for a in self._repo.changelog.ancestors(self._rev):
137 137 yield changectx(self._repo, a)
138 138
139 139 def descendants(self):
140 140 for d in self._repo.changelog.descendants(self._rev):
141 141 yield changectx(self._repo, d)
142 142
143 143 def _fileinfo(self, path):
144 144 if '_manifest' in self.__dict__:
145 145 try:
146 146 return self._manifest[path], self._manifest.flags(path)
147 147 except KeyError:
148 148 raise error.LookupError(self._node, path,
149 149 _('not found in manifest'))
150 150 if '_manifestdelta' in self.__dict__ or path in self.files():
151 151 if path in self._manifestdelta:
152 152 return self._manifestdelta[path], self._manifestdelta.flags(path)
153 153 node, flag = self._repo.manifest.find(self._changeset[0], path)
154 154 if not node:
155 155 raise error.LookupError(self._node, path,
156 156 _('not found in manifest'))
157 157
158 158 return node, flag
159 159
160 160 def filenode(self, path):
161 161 return self._fileinfo(path)[0]
162 162
163 163 def flags(self, path):
164 164 try:
165 165 return self._fileinfo(path)[1]
166 166 except error.LookupError:
167 167 return ''
168 168
169 169 def filectx(self, path, fileid=None, filelog=None):
170 170 """get a file context from this changeset"""
171 171 if fileid is None:
172 172 fileid = self.filenode(path)
173 173 return filectx(self._repo, path, fileid=fileid,
174 174 changectx=self, filelog=filelog)
175 175
176 176 def ancestor(self, c2):
177 177 """
178 178 return the ancestor context of self and c2
179 179 """
180 180 # deal with workingctxs
181 181 n2 = c2._node
182 182 if n2 == None:
183 183 n2 = c2._parents[0]._node
184 184 n = self._repo.changelog.ancestor(self._node, n2)
185 185 return changectx(self._repo, n)
186 186
187 187 def walk(self, match):
188 188 fset = set(match.files())
189 189 # for dirstate.walk, files=['.'] means "walk the whole tree".
190 190 # follow that here, too
191 191 fset.discard('.')
192 192 for fn in self:
193 193 for ffn in fset:
194 194 # match if the file is the exact name or a directory
195 195 if ffn == fn or fn.startswith("%s/" % ffn):
196 196 fset.remove(ffn)
197 197 break
198 198 if match(fn):
199 199 yield fn
200 200 for fn in sorted(fset):
201 201 if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn):
202 202 yield fn
203 203
204 204 def sub(self, path):
205 205 return subrepo.subrepo(self, path)
206 206
207 207 class filectx(object):
208 208 """A filecontext object makes access to data related to a particular
209 209 filerevision convenient."""
210 210 def __init__(self, repo, path, changeid=None, fileid=None,
211 211 filelog=None, changectx=None):
212 212 """changeid can be a changeset revision, node, or tag.
213 213 fileid can be a file revision or node."""
214 214 self._repo = repo
215 215 self._path = path
216 216
217 217 assert (changeid is not None
218 218 or fileid is not None
219 219 or changectx is not None), \
220 220 ("bad args: changeid=%r, fileid=%r, changectx=%r"
221 221 % (changeid, fileid, changectx))
222 222
223 223 if filelog:
224 224 self._filelog = filelog
225 225
226 226 if changeid is not None:
227 227 self._changeid = changeid
228 228 if changectx is not None:
229 229 self._changectx = changectx
230 230 if fileid is not None:
231 231 self._fileid = fileid
232 232
233 233 @propertycache
234 234 def _changectx(self):
235 235 return changectx(self._repo, self._changeid)
236 236
237 237 @propertycache
238 238 def _filelog(self):
239 239 return self._repo.file(self._path)
240 240
241 241 @propertycache
242 242 def _changeid(self):
243 243 if '_changectx' in self.__dict__:
244 244 return self._changectx.rev()
245 245 else:
246 246 return self._filelog.linkrev(self._filerev)
247 247
248 248 @propertycache
249 249 def _filenode(self):
250 250 if '_fileid' in self.__dict__:
251 251 return self._filelog.lookup(self._fileid)
252 252 else:
253 253 return self._changectx.filenode(self._path)
254 254
255 255 @propertycache
256 256 def _filerev(self):
257 257 return self._filelog.rev(self._filenode)
258 258
259 259 @propertycache
260 260 def _repopath(self):
261 261 return self._path
262 262
263 263 def __nonzero__(self):
264 264 try:
265 265 self._filenode
266 266 return True
267 267 except error.LookupError:
268 268 # file is missing
269 269 return False
270 270
271 271 def __str__(self):
272 272 return "%s@%s" % (self.path(), short(self.node()))
273 273
274 274 def __repr__(self):
275 275 return "<filectx %s>" % str(self)
276 276
277 277 def __hash__(self):
278 278 try:
279 279 return hash((self._path, self._fileid))
280 280 except AttributeError:
281 281 return id(self)
282 282
283 283 def __eq__(self, other):
284 284 try:
285 285 return (self._path == other._path
286 286 and self._fileid == other._fileid)
287 287 except AttributeError:
288 288 return False
289 289
290 290 def __ne__(self, other):
291 291 return not (self == other)
292 292
293 293 def filectx(self, fileid):
294 294 '''opens an arbitrary revision of the file without
295 295 opening a new filelog'''
296 296 return filectx(self._repo, self._path, fileid=fileid,
297 297 filelog=self._filelog)
298 298
299 299 def filerev(self):
300 300 return self._filerev
301 301 def filenode(self):
302 302 return self._filenode
303 303 def flags(self):
304 304 return self._changectx.flags(self._path)
305 305 def filelog(self):
306 306 return self._filelog
307 307
308 308 def rev(self):
309 309 if '_changectx' in self.__dict__:
310 310 return self._changectx.rev()
311 311 if '_changeid' in self.__dict__:
312 312 return self._changectx.rev()
313 313 return self._filelog.linkrev(self._filerev)
314 314
315 315 def linkrev(self):
316 316 return self._filelog.linkrev(self._filerev)
317 317 def node(self):
318 318 return self._changectx.node()
319 319 def hex(self):
320 320 return hex(self.node())
321 321 def user(self):
322 322 return self._changectx.user()
323 323 def date(self):
324 324 return self._changectx.date()
325 325 def files(self):
326 326 return self._changectx.files()
327 327 def description(self):
328 328 return self._changectx.description()
329 329 def branch(self):
330 330 return self._changectx.branch()
331 331 def extra(self):
332 332 return self._changectx.extra()
333 333 def manifest(self):
334 334 return self._changectx.manifest()
335 335 def changectx(self):
336 336 return self._changectx
337 337
338 338 def data(self):
339 339 return self._filelog.read(self._filenode)
340 340 def path(self):
341 341 return self._path
342 342 def size(self):
343 343 return self._filelog.size(self._filerev)
344 344
345 345 def cmp(self, text):
346 346 return self._filelog.cmp(self._filenode, text)
347 347
348 348 def renamed(self):
349 349 """check if file was actually renamed in this changeset revision
350 350
351 351 If rename logged in file revision, we report copy for changeset only
352 352 if file revisions linkrev points back to the changeset in question
353 353 or both changeset parents contain different file revisions.
354 354 """
355 355
356 356 renamed = self._filelog.renamed(self._filenode)
357 357 if not renamed:
358 358 return renamed
359 359
360 360 if self.rev() == self.linkrev():
361 361 return renamed
362 362
363 363 name = self.path()
364 364 fnode = self._filenode
365 365 for p in self._changectx.parents():
366 366 try:
367 367 if fnode == p.filenode(name):
368 368 return None
369 369 except error.LookupError:
370 370 pass
371 371 return renamed
372 372
373 373 def parents(self):
374 374 p = self._path
375 375 fl = self._filelog
376 376 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
377 377
378 378 r = self._filelog.renamed(self._filenode)
379 379 if r:
380 380 pl[0] = (r[0], r[1], None)
381 381
382 382 return [filectx(self._repo, p, fileid=n, filelog=l)
383 383 for p, n, l in pl if n != nullid]
384 384
385 385 def children(self):
386 386 # hard for renames
387 387 c = self._filelog.children(self._filenode)
388 388 return [filectx(self._repo, self._path, fileid=x,
389 389 filelog=self._filelog) for x in c]
390 390
391 391 def annotate(self, follow=False, linenumber=None):
392 392 '''returns a list of tuples of (ctx, line) for each line
393 393 in the file, where ctx is the filectx of the node where
394 394 that line was last changed.
395 395 This returns tuples of ((ctx, linenumber), line) for each line,
396 396 if "linenumber" parameter is NOT "None".
397 397 In such tuples, linenumber means one at the first appearance
398 398 in the managed file.
399 399 To reduce annotation cost,
400 400 this returns fixed value(False is used) as linenumber,
401 401 if "linenumber" parameter is "False".'''
402 402
403 403 def decorate_compat(text, rev):
404 404 return ([rev] * len(text.splitlines()), text)
405 405
406 406 def without_linenumber(text, rev):
407 407 return ([(rev, False)] * len(text.splitlines()), text)
408 408
409 409 def with_linenumber(text, rev):
410 410 size = len(text.splitlines())
411 411 return ([(rev, i) for i in xrange(1, size + 1)], text)
412 412
413 413 decorate = (((linenumber is None) and decorate_compat) or
414 414 (linenumber and with_linenumber) or
415 415 without_linenumber)
416 416
417 417 def pair(parent, child):
418 418 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
419 419 child[0][b1:b2] = parent[0][a1:a2]
420 420 return child
421 421
422 422 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
423 423 def getctx(path, fileid):
424 424 log = path == self._path and self._filelog or getlog(path)
425 425 return filectx(self._repo, path, fileid=fileid, filelog=log)
426 426 getctx = util.lrucachefunc(getctx)
427 427
428 428 def parents(f):
429 429 # we want to reuse filectx objects as much as possible
430 430 p = f._path
431 431 if f._filerev is None: # working dir
432 432 pl = [(n.path(), n.filerev()) for n in f.parents()]
433 433 else:
434 434 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
435 435
436 436 if follow:
437 437 r = f.renamed()
438 438 if r:
439 439 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
440 440
441 441 return [getctx(p, n) for p, n in pl if n != nullrev]
442 442
443 443 # use linkrev to find the first changeset where self appeared
444 444 if self.rev() != self.linkrev():
445 445 base = self.filectx(self.filerev())
446 446 else:
447 447 base = self
448 448
449 449 # find all ancestors
450 450 needed = {base: 1}
451 451 visit = [base]
452 452 files = [base._path]
453 453 while visit:
454 454 f = visit.pop(0)
455 455 for p in parents(f):
456 456 if p not in needed:
457 457 needed[p] = 1
458 458 visit.append(p)
459 459 if p._path not in files:
460 460 files.append(p._path)
461 461 else:
462 462 # count how many times we'll use this
463 463 needed[p] += 1
464 464
465 465 # sort by revision (per file) which is a topological order
466 466 visit = []
467 467 for f in files:
468 468 visit.extend(n for n in needed if n._path == f)
469 469
470 470 hist = {}
471 471 for f in sorted(visit, key=lambda x: x.rev()):
472 472 curr = decorate(f.data(), f)
473 473 for p in parents(f):
474 474 curr = pair(hist[p], curr)
475 475 # trim the history of unneeded revs
476 476 needed[p] -= 1
477 477 if not needed[p]:
478 478 del hist[p]
479 479 hist[f] = curr
480 480
481 481 return zip(hist[f][0], hist[f][1].splitlines(True))
482 482
483 483 def ancestor(self, fc2):
484 484 """
485 485 find the common ancestor file context, if any, of self, and fc2
486 486 """
487 487
488 488 actx = self.changectx().ancestor(fc2.changectx())
489 489
490 490 # the trivial case: changesets are unrelated, files must be too
491 491 if not actx:
492 492 return None
493 493
494 494 # the easy case: no (relevant) renames
495 495 if fc2.path() == self.path() and self.path() in actx:
496 496 return actx[self.path()]
497 497 acache = {}
498 498
499 499 # prime the ancestor cache for the working directory
500 500 for c in (self, fc2):
501 501 if c._filerev is None:
502 502 pl = [(n.path(), n.filenode()) for n in c.parents()]
503 503 acache[(c._path, None)] = pl
504 504
505 505 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
506 506 def parents(vertex):
507 507 if vertex in acache:
508 508 return acache[vertex]
509 509 f, n = vertex
510 510 if f not in flcache:
511 511 flcache[f] = self._repo.file(f)
512 512 fl = flcache[f]
513 513 pl = [(f, p) for p in fl.parents(n) if p != nullid]
514 514 re = fl.renamed(n)
515 515 if re:
516 516 pl.append(re)
517 517 acache[vertex] = pl
518 518 return pl
519 519
520 520 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
521 521 v = ancestor.ancestor(a, b, parents)
522 522 if v:
523 523 f, n = v
524 524 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
525 525
526 526 return None
527 527
528 528 def ancestors(self):
529 529 seen = set(str(self))
530 530 visit = [self]
531 531 while visit:
532 532 for parent in visit.pop(0).parents():
533 533 s = str(parent)
534 534 if s not in seen:
535 535 visit.append(parent)
536 536 seen.add(s)
537 537 yield parent
538 538
539 539 class workingctx(changectx):
540 540 """A workingctx object makes access to data related to
541 541 the current working directory convenient.
542 542 parents - a pair of parent nodeids, or None to use the dirstate.
543 543 date - any valid date string or (unixtime, offset), or None.
544 544 user - username string, or None.
545 545 extra - a dictionary of extra values, or None.
546 546 changes - a list of file lists as returned by localrepo.status()
547 547 or None to use the repository status.
548 548 """
549 549 def __init__(self, repo, parents=None, text="", user=None, date=None,
550 550 extra=None, changes=None):
551 551 self._repo = repo
552 552 self._rev = None
553 553 self._node = None
554 554 self._text = text
555 555 if date:
556 556 self._date = util.parsedate(date)
557 557 if user:
558 558 self._user = user
559 559 if parents:
560 560 self._parents = [changectx(self._repo, p) for p in parents]
561 561 if changes:
562 562 self._status = list(changes)
563 563
564 564 self._extra = {}
565 565 if extra:
566 566 self._extra = extra.copy()
567 567 if 'branch' not in self._extra:
568 568 branch = self._repo.dirstate.branch()
569 569 try:
570 570 branch = branch.decode('UTF-8').encode('UTF-8')
571 571 except UnicodeDecodeError:
572 572 raise util.Abort(_('branch name not in UTF-8!'))
573 573 self._extra['branch'] = branch
574 574 if self._extra['branch'] == '':
575 575 self._extra['branch'] = 'default'
576 576
577 577 def __str__(self):
578 578 return str(self._parents[0]) + "+"
579 579
580 580 def __nonzero__(self):
581 581 return True
582 582
583 583 def __contains__(self, key):
584 584 return self._repo.dirstate[key] not in "?r"
585 585
586 586 @propertycache
587 587 def _manifest(self):
588 588 """generate a manifest corresponding to the working directory"""
589 589
590 590 man = self._parents[0].manifest().copy()
591 591 copied = self._repo.dirstate.copies()
592 592 if len(self._parents) > 1:
593 593 man2 = self.p2().manifest()
594 594 def getman(f):
595 595 if f in man:
596 596 return man
597 597 return man2
598 598 else:
599 599 getman = lambda f: man
600 600 def cf(f):
601 601 f = copied.get(f, f)
602 602 return getman(f).flags(f)
603 603 ff = self._repo.dirstate.flagfunc(cf)
604 604 modified, added, removed, deleted, unknown = self._status[:5]
605 605 for i, l in (("a", added), ("m", modified), ("u", unknown)):
606 606 for f in l:
607 607 orig = copied.get(f, f)
608 608 man[f] = getman(orig).get(orig, nullid) + i
609 609 try:
610 610 man.set(f, ff(f))
611 611 except OSError:
612 612 pass
613 613
614 614 for f in deleted + removed:
615 615 if f in man:
616 616 del man[f]
617 617
618 618 return man
619 619
620 620 @propertycache
621 621 def _status(self):
622 622 return self._repo.status(unknown=True)
623 623
624 624 @propertycache
625 625 def _user(self):
626 626 return self._repo.ui.username()
627 627
628 628 @propertycache
629 629 def _date(self):
630 630 return util.makedate()
631 631
632 632 @propertycache
633 633 def _parents(self):
634 634 p = self._repo.dirstate.parents()
635 635 if p[1] == nullid:
636 636 p = p[:-1]
637 637 self._parents = [changectx(self._repo, x) for x in p]
638 638 return self._parents
639 639
640 640 def manifest(self):
641 641 return self._manifest
642 642 def user(self):
643 643 return self._user or self._repo.ui.username()
644 644 def date(self):
645 645 return self._date
646 646 def description(self):
647 647 return self._text
648 648 def files(self):
649 649 return sorted(self._status[0] + self._status[1] + self._status[2])
650 650
651 651 def modified(self):
652 652 return self._status[0]
653 653 def added(self):
654 654 return self._status[1]
655 655 def removed(self):
656 656 return self._status[2]
657 657 def deleted(self):
658 658 return self._status[3]
659 659 def unknown(self):
660 660 return self._status[4]
661 661 def clean(self):
662 662 return self._status[5]
663 663 def branch(self):
664 664 return self._extra['branch']
665 665 def extra(self):
666 666 return self._extra
667 667
668 668 def tags(self):
669 669 t = []
670 670 [t.extend(p.tags()) for p in self.parents()]
671 671 return t
672 672
673 673 def children(self):
674 674 return []
675 675
676 676 def flags(self, path):
677 677 if '_manifest' in self.__dict__:
678 678 try:
679 679 return self._manifest.flags(path)
680 680 except KeyError:
681 681 return ''
682 682
683 683 orig = self._repo.dirstate.copies().get(path, path)
684 684
685 685 def findflag(ctx):
686 686 mnode = ctx.changeset()[0]
687 687 node, flag = self._repo.manifest.find(mnode, orig)
688 688 ff = self._repo.dirstate.flagfunc(lambda x: flag or None)
689 689 try:
690 return ff(orig)
690 return ff(path)
691 691 except OSError:
692 692 pass
693 693
694 694 flag = findflag(self._parents[0])
695 695 if flag is None and len(self.parents()) > 1:
696 696 flag = findflag(self._parents[1])
697 697 if flag is None or self._repo.dirstate[path] == 'r':
698 698 return ''
699 699 return flag
700 700
701 701 def filectx(self, path, filelog=None):
702 702 """get a file context from the working directory"""
703 703 return workingfilectx(self._repo, path, workingctx=self,
704 704 filelog=filelog)
705 705
706 706 def ancestor(self, c2):
707 707 """return the ancestor context of self and c2"""
708 708 return self._parents[0].ancestor(c2) # punt on two parents for now
709 709
710 710 def walk(self, match):
711 711 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
712 712 True, False))
713 713
714 714 def dirty(self, missing=False):
715 715 "check whether a working directory is modified"
716 716
717 717 return (self.p2() or self.branch() != self.p1().branch() or
718 718 self.modified() or self.added() or self.removed() or
719 719 (missing and self.deleted()))
720 720
721 721 class workingfilectx(filectx):
722 722 """A workingfilectx object makes access to data related to a particular
723 723 file in the working directory convenient."""
724 724 def __init__(self, repo, path, filelog=None, workingctx=None):
725 725 """changeid can be a changeset revision, node, or tag.
726 726 fileid can be a file revision or node."""
727 727 self._repo = repo
728 728 self._path = path
729 729 self._changeid = None
730 730 self._filerev = self._filenode = None
731 731
732 732 if filelog:
733 733 self._filelog = filelog
734 734 if workingctx:
735 735 self._changectx = workingctx
736 736
737 737 @propertycache
738 738 def _changectx(self):
739 739 return workingctx(self._repo)
740 740
741 741 def __nonzero__(self):
742 742 return True
743 743
744 744 def __str__(self):
745 745 return "%s@%s" % (self.path(), self._changectx)
746 746
747 747 def data(self):
748 748 return self._repo.wread(self._path)
749 749 def renamed(self):
750 750 rp = self._repo.dirstate.copied(self._path)
751 751 if not rp:
752 752 return None
753 753 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
754 754
755 755 def parents(self):
756 756 '''return parent filectxs, following copies if necessary'''
757 757 def filenode(ctx, path):
758 758 return ctx._manifest.get(path, nullid)
759 759
760 760 path = self._path
761 761 fl = self._filelog
762 762 pcl = self._changectx._parents
763 763 renamed = self.renamed()
764 764
765 765 if renamed:
766 766 pl = [renamed + (None,)]
767 767 else:
768 768 pl = [(path, filenode(pcl[0], path), fl)]
769 769
770 770 for pc in pcl[1:]:
771 771 pl.append((path, filenode(pc, path), fl))
772 772
773 773 return [filectx(self._repo, p, fileid=n, filelog=l)
774 774 for p, n, l in pl if n != nullid]
775 775
776 776 def children(self):
777 777 return []
778 778
779 779 def size(self):
780 780 return os.stat(self._repo.wjoin(self._path)).st_size
781 781 def date(self):
782 782 t, tz = self._changectx.date()
783 783 try:
784 784 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
785 785 except OSError, err:
786 786 if err.errno != errno.ENOENT:
787 787 raise
788 788 return (t, tz)
789 789
790 790 def cmp(self, text):
791 791 return self._repo.wread(self._path) == text
792 792
793 793 class memctx(object):
794 794 """Use memctx to perform in-memory commits via localrepo.commitctx().
795 795
796 796 Revision information is supplied at initialization time while
797 797 related files data and is made available through a callback
798 798 mechanism. 'repo' is the current localrepo, 'parents' is a
799 799 sequence of two parent revisions identifiers (pass None for every
800 800 missing parent), 'text' is the commit message and 'files' lists
801 801 names of files touched by the revision (normalized and relative to
802 802 repository root).
803 803
804 804 filectxfn(repo, memctx, path) is a callable receiving the
805 805 repository, the current memctx object and the normalized path of
806 806 requested file, relative to repository root. It is fired by the
807 807 commit function for every file in 'files', but calls order is
808 808 undefined. If the file is available in the revision being
809 809 committed (updated or added), filectxfn returns a memfilectx
810 810 object. If the file was removed, filectxfn raises an
811 811 IOError. Moved files are represented by marking the source file
812 812 removed and the new file added with copy information (see
813 813 memfilectx).
814 814
815 815 user receives the committer name and defaults to current
816 816 repository username, date is the commit date in any format
817 817 supported by util.parsedate() and defaults to current date, extra
818 818 is a dictionary of metadata or is left empty.
819 819 """
820 820 def __init__(self, repo, parents, text, files, filectxfn, user=None,
821 821 date=None, extra=None):
822 822 self._repo = repo
823 823 self._rev = None
824 824 self._node = None
825 825 self._text = text
826 826 self._date = date and util.parsedate(date) or util.makedate()
827 827 self._user = user
828 828 parents = [(p or nullid) for p in parents]
829 829 p1, p2 = parents
830 830 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
831 831 files = sorted(set(files))
832 832 self._status = [files, [], [], [], []]
833 833 self._filectxfn = filectxfn
834 834
835 835 self._extra = extra and extra.copy() or {}
836 836 if 'branch' not in self._extra:
837 837 self._extra['branch'] = 'default'
838 838 elif self._extra.get('branch') == '':
839 839 self._extra['branch'] = 'default'
840 840
841 841 def __str__(self):
842 842 return str(self._parents[0]) + "+"
843 843
844 844 def __int__(self):
845 845 return self._rev
846 846
847 847 def __nonzero__(self):
848 848 return True
849 849
850 850 def __getitem__(self, key):
851 851 return self.filectx(key)
852 852
853 853 def p1(self):
854 854 return self._parents[0]
855 855 def p2(self):
856 856 return self._parents[1]
857 857
858 858 def user(self):
859 859 return self._user or self._repo.ui.username()
860 860 def date(self):
861 861 return self._date
862 862 def description(self):
863 863 return self._text
864 864 def files(self):
865 865 return self.modified()
866 866 def modified(self):
867 867 return self._status[0]
868 868 def added(self):
869 869 return self._status[1]
870 870 def removed(self):
871 871 return self._status[2]
872 872 def deleted(self):
873 873 return self._status[3]
874 874 def unknown(self):
875 875 return self._status[4]
876 876 def clean(self):
877 877 return self._status[5]
878 878 def branch(self):
879 879 return self._extra['branch']
880 880 def extra(self):
881 881 return self._extra
882 882 def flags(self, f):
883 883 return self[f].flags()
884 884
885 885 def parents(self):
886 886 """return contexts for each parent changeset"""
887 887 return self._parents
888 888
889 889 def filectx(self, path, filelog=None):
890 890 """get a file context from the working directory"""
891 891 return self._filectxfn(self._repo, self, path)
892 892
893 893 class memfilectx(object):
894 894 """memfilectx represents an in-memory file to commit.
895 895
896 896 See memctx for more details.
897 897 """
898 898 def __init__(self, path, data, islink, isexec, copied):
899 899 """
900 900 path is the normalized file path relative to repository root.
901 901 data is the file content as a string.
902 902 islink is True if the file is a symbolic link.
903 903 isexec is True if the file is executable.
904 904 copied is the source file path if current file was copied in the
905 905 revision being committed, or None."""
906 906 self._path = path
907 907 self._data = data
908 908 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
909 909 self._copied = None
910 910 if copied:
911 911 self._copied = (copied, nullid)
912 912
913 913 def __nonzero__(self):
914 914 return True
915 915 def __str__(self):
916 916 return "%s@%s" % (self.path(), self._changectx)
917 917 def path(self):
918 918 return self._path
919 919 def data(self):
920 920 return self._data
921 921 def flags(self):
922 922 return self._flags
923 923 def isexec(self):
924 924 return 'x' in self._flags
925 925 def islink(self):
926 926 return 'l' in self._flags
927 927 def renamed(self):
928 928 return self._copied
General Comments 0
You need to be logged in to leave comments. Login now