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