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