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