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