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