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