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