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