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