Mercurial > traipse_dev
comparison upmana/mercurial/dirstate.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 # dirstate.py - working directory tracking for mercurial | |
2 # | |
3 # Copyright 2005-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 | |
9 from i18n import _ | |
10 import util, ignore, osutil, parsers | |
11 import struct, os, stat, errno | |
12 import cStringIO, sys | |
13 | |
14 _unknown = ('?', 0, 0, 0) | |
15 _format = ">cllll" | |
16 propertycache = util.propertycache | |
17 | |
18 def _finddirs(path): | |
19 pos = path.rfind('/') | |
20 while pos != -1: | |
21 yield path[:pos] | |
22 pos = path.rfind('/', 0, pos) | |
23 | |
24 def _incdirs(dirs, path): | |
25 for base in _finddirs(path): | |
26 if base in dirs: | |
27 dirs[base] += 1 | |
28 return | |
29 dirs[base] = 1 | |
30 | |
31 def _decdirs(dirs, path): | |
32 for base in _finddirs(path): | |
33 if dirs[base] > 1: | |
34 dirs[base] -= 1 | |
35 return | |
36 del dirs[base] | |
37 | |
38 class dirstate(object): | |
39 | |
40 def __init__(self, opener, ui, root): | |
41 self._opener = opener | |
42 self._root = root | |
43 self._rootdir = os.path.join(root, '') | |
44 self._dirty = False | |
45 self._dirtypl = False | |
46 self._ui = ui | |
47 | |
48 @propertycache | |
49 def _map(self): | |
50 self._read() | |
51 return self._map | |
52 | |
53 @propertycache | |
54 def _copymap(self): | |
55 self._read() | |
56 return self._copymap | |
57 | |
58 @propertycache | |
59 def _foldmap(self): | |
60 f = {} | |
61 for name in self._map: | |
62 f[os.path.normcase(name)] = name | |
63 return f | |
64 | |
65 @propertycache | |
66 def _branch(self): | |
67 try: | |
68 return self._opener("branch").read().strip() or "default" | |
69 except IOError: | |
70 return "default" | |
71 | |
72 @propertycache | |
73 def _pl(self): | |
74 try: | |
75 st = self._opener("dirstate").read(40) | |
76 l = len(st) | |
77 if l == 40: | |
78 return st[:20], st[20:40] | |
79 elif l > 0 and l < 40: | |
80 raise util.Abort(_('working directory state appears damaged!')) | |
81 except IOError, err: | |
82 if err.errno != errno.ENOENT: raise | |
83 return [nullid, nullid] | |
84 | |
85 @propertycache | |
86 def _dirs(self): | |
87 dirs = {} | |
88 for f,s in self._map.iteritems(): | |
89 if s[0] != 'r': | |
90 _incdirs(dirs, f) | |
91 return dirs | |
92 | |
93 @propertycache | |
94 def _ignore(self): | |
95 files = [self._join('.hgignore')] | |
96 for name, path in self._ui.configitems("ui"): | |
97 if name == 'ignore' or name.startswith('ignore.'): | |
98 files.append(os.path.expanduser(path)) | |
99 return ignore.ignore(self._root, files, self._ui.warn) | |
100 | |
101 @propertycache | |
102 def _slash(self): | |
103 return self._ui.configbool('ui', 'slash') and os.sep != '/' | |
104 | |
105 @propertycache | |
106 def _checklink(self): | |
107 return util.checklink(self._root) | |
108 | |
109 @propertycache | |
110 def _checkexec(self): | |
111 return util.checkexec(self._root) | |
112 | |
113 @propertycache | |
114 def _checkcase(self): | |
115 return not util.checkcase(self._join('.hg')) | |
116 | |
117 def _join(self, f): | |
118 # much faster than os.path.join() | |
119 # it's safe because f is always a relative path | |
120 return self._rootdir + f | |
121 | |
122 def flagfunc(self, fallback): | |
123 if self._checklink: | |
124 if self._checkexec: | |
125 def f(x): | |
126 p = self._join(x) | |
127 if os.path.islink(p): | |
128 return 'l' | |
129 if util.is_exec(p): | |
130 return 'x' | |
131 return '' | |
132 return f | |
133 def f(x): | |
134 if os.path.islink(self._join(x)): | |
135 return 'l' | |
136 if 'x' in fallback(x): | |
137 return 'x' | |
138 return '' | |
139 return f | |
140 if self._checkexec: | |
141 def f(x): | |
142 if 'l' in fallback(x): | |
143 return 'l' | |
144 if util.is_exec(self._join(x)): | |
145 return 'x' | |
146 return '' | |
147 return f | |
148 return fallback | |
149 | |
150 def getcwd(self): | |
151 cwd = os.getcwd() | |
152 if cwd == self._root: return '' | |
153 # self._root ends with a path separator if self._root is '/' or 'C:\' | |
154 rootsep = self._root | |
155 if not util.endswithsep(rootsep): | |
156 rootsep += os.sep | |
157 if cwd.startswith(rootsep): | |
158 return cwd[len(rootsep):] | |
159 else: | |
160 # we're outside the repo. return an absolute path. | |
161 return cwd | |
162 | |
163 def pathto(self, f, cwd=None): | |
164 if cwd is None: | |
165 cwd = self.getcwd() | |
166 path = util.pathto(self._root, cwd, f) | |
167 if self._slash: | |
168 return util.normpath(path) | |
169 return path | |
170 | |
171 def __getitem__(self, key): | |
172 ''' current states: | |
173 n normal | |
174 m needs merging | |
175 r marked for removal | |
176 a marked for addition | |
177 ? not tracked''' | |
178 return self._map.get(key, ("?",))[0] | |
179 | |
180 def __contains__(self, key): | |
181 return key in self._map | |
182 | |
183 def __iter__(self): | |
184 for x in sorted(self._map): | |
185 yield x | |
186 | |
187 def parents(self): | |
188 return self._pl | |
189 | |
190 def branch(self): | |
191 return self._branch | |
192 | |
193 def setparents(self, p1, p2=nullid): | |
194 self._dirty = self._dirtypl = True | |
195 self._pl = p1, p2 | |
196 | |
197 def setbranch(self, branch): | |
198 self._branch = branch | |
199 self._opener("branch", "w").write(branch + '\n') | |
200 | |
201 def _read(self): | |
202 self._map = {} | |
203 self._copymap = {} | |
204 try: | |
205 st = self._opener("dirstate").read() | |
206 except IOError, err: | |
207 if err.errno != errno.ENOENT: raise | |
208 return | |
209 if not st: | |
210 return | |
211 | |
212 p = parsers.parse_dirstate(self._map, self._copymap, st) | |
213 if not self._dirtypl: | |
214 self._pl = p | |
215 | |
216 def invalidate(self): | |
217 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split(): | |
218 if a in self.__dict__: | |
219 delattr(self, a) | |
220 self._dirty = False | |
221 | |
222 def copy(self, source, dest): | |
223 """Mark dest as a copy of source. Unmark dest if source is None. | |
224 """ | |
225 if source == dest: | |
226 return | |
227 self._dirty = True | |
228 if source is not None: | |
229 self._copymap[dest] = source | |
230 elif dest in self._copymap: | |
231 del self._copymap[dest] | |
232 | |
233 def copied(self, file): | |
234 return self._copymap.get(file, None) | |
235 | |
236 def copies(self): | |
237 return self._copymap | |
238 | |
239 def _droppath(self, f): | |
240 if self[f] not in "?r" and "_dirs" in self.__dict__: | |
241 _decdirs(self._dirs, f) | |
242 | |
243 def _addpath(self, f, check=False): | |
244 oldstate = self[f] | |
245 if check or oldstate == "r": | |
246 if '\r' in f or '\n' in f: | |
247 raise util.Abort( | |
248 _("'\\n' and '\\r' disallowed in filenames: %r") % f) | |
249 if f in self._dirs: | |
250 raise util.Abort(_('directory %r already in dirstate') % f) | |
251 # shadows | |
252 for d in _finddirs(f): | |
253 if d in self._dirs: | |
254 break | |
255 if d in self._map and self[d] != 'r': | |
256 raise util.Abort( | |
257 _('file %r in dirstate clashes with %r') % (d, f)) | |
258 if oldstate in "?r" and "_dirs" in self.__dict__: | |
259 _incdirs(self._dirs, f) | |
260 | |
261 def normal(self, f): | |
262 'mark a file normal and clean' | |
263 self._dirty = True | |
264 self._addpath(f) | |
265 s = os.lstat(self._join(f)) | |
266 self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime)) | |
267 if f in self._copymap: | |
268 del self._copymap[f] | |
269 | |
270 def normallookup(self, f): | |
271 'mark a file normal, but possibly dirty' | |
272 if self._pl[1] != nullid and f in self._map: | |
273 # if there is a merge going on and the file was either | |
274 # in state 'm' or dirty before being removed, restore that state. | |
275 entry = self._map[f] | |
276 if entry[0] == 'r' and entry[2] in (-1, -2): | |
277 source = self._copymap.get(f) | |
278 if entry[2] == -1: | |
279 self.merge(f) | |
280 elif entry[2] == -2: | |
281 self.normaldirty(f) | |
282 if source: | |
283 self.copy(source, f) | |
284 return | |
285 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2: | |
286 return | |
287 self._dirty = True | |
288 self._addpath(f) | |
289 self._map[f] = ('n', 0, -1, -1) | |
290 if f in self._copymap: | |
291 del self._copymap[f] | |
292 | |
293 def normaldirty(self, f): | |
294 'mark a file normal, but dirty' | |
295 self._dirty = True | |
296 self._addpath(f) | |
297 self._map[f] = ('n', 0, -2, -1) | |
298 if f in self._copymap: | |
299 del self._copymap[f] | |
300 | |
301 def add(self, f): | |
302 'mark a file added' | |
303 self._dirty = True | |
304 self._addpath(f, True) | |
305 self._map[f] = ('a', 0, -1, -1) | |
306 if f in self._copymap: | |
307 del self._copymap[f] | |
308 | |
309 def remove(self, f): | |
310 'mark a file removed' | |
311 self._dirty = True | |
312 self._droppath(f) | |
313 size = 0 | |
314 if self._pl[1] != nullid and f in self._map: | |
315 entry = self._map[f] | |
316 if entry[0] == 'm': | |
317 size = -1 | |
318 elif entry[0] == 'n' and entry[2] == -2: | |
319 size = -2 | |
320 self._map[f] = ('r', 0, size, 0) | |
321 if size == 0 and f in self._copymap: | |
322 del self._copymap[f] | |
323 | |
324 def merge(self, f): | |
325 'mark a file merged' | |
326 self._dirty = True | |
327 s = os.lstat(self._join(f)) | |
328 self._addpath(f) | |
329 self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime)) | |
330 if f in self._copymap: | |
331 del self._copymap[f] | |
332 | |
333 def forget(self, f): | |
334 'forget a file' | |
335 self._dirty = True | |
336 try: | |
337 self._droppath(f) | |
338 del self._map[f] | |
339 except KeyError: | |
340 self._ui.warn(_("not in dirstate: %s\n") % f) | |
341 | |
342 def _normalize(self, path, knownpath): | |
343 norm_path = os.path.normcase(path) | |
344 fold_path = self._foldmap.get(norm_path, None) | |
345 if fold_path is None: | |
346 if knownpath or not os.path.exists(os.path.join(self._root, path)): | |
347 fold_path = path | |
348 else: | |
349 fold_path = self._foldmap.setdefault(norm_path, | |
350 util.fspath(path, self._root)) | |
351 return fold_path | |
352 | |
353 def clear(self): | |
354 self._map = {} | |
355 if "_dirs" in self.__dict__: | |
356 delattr(self, "_dirs"); | |
357 self._copymap = {} | |
358 self._pl = [nullid, nullid] | |
359 self._dirty = True | |
360 | |
361 def rebuild(self, parent, files): | |
362 self.clear() | |
363 for f in files: | |
364 if 'x' in files.flags(f): | |
365 self._map[f] = ('n', 0777, -1, 0) | |
366 else: | |
367 self._map[f] = ('n', 0666, -1, 0) | |
368 self._pl = (parent, nullid) | |
369 self._dirty = True | |
370 | |
371 def write(self): | |
372 if not self._dirty: | |
373 return | |
374 st = self._opener("dirstate", "w", atomictemp=True) | |
375 | |
376 try: | |
377 gran = int(self._ui.config('dirstate', 'granularity', 1)) | |
378 except ValueError: | |
379 gran = 1 | |
380 limit = sys.maxint | |
381 if gran > 0: | |
382 limit = util.fstat(st).st_mtime - gran | |
383 | |
384 cs = cStringIO.StringIO() | |
385 copymap = self._copymap | |
386 pack = struct.pack | |
387 write = cs.write | |
388 write("".join(self._pl)) | |
389 for f, e in self._map.iteritems(): | |
390 if f in copymap: | |
391 f = "%s\0%s" % (f, copymap[f]) | |
392 if e[3] > limit and e[0] == 'n': | |
393 e = (e[0], 0, -1, -1) | |
394 e = pack(_format, e[0], e[1], e[2], e[3], len(f)) | |
395 write(e) | |
396 write(f) | |
397 st.write(cs.getvalue()) | |
398 st.rename() | |
399 self._dirty = self._dirtypl = False | |
400 | |
401 def _dirignore(self, f): | |
402 if f == '.': | |
403 return False | |
404 if self._ignore(f): | |
405 return True | |
406 for p in _finddirs(f): | |
407 if self._ignore(p): | |
408 return True | |
409 return False | |
410 | |
411 def walk(self, match, unknown, ignored): | |
412 ''' | |
413 walk recursively through the directory tree, finding all files | |
414 matched by the match function | |
415 | |
416 results are yielded in a tuple (filename, stat), where stat | |
417 and st is the stat result if the file was found in the directory. | |
418 ''' | |
419 | |
420 def fwarn(f, msg): | |
421 self._ui.warn('%s: %s\n' % (self.pathto(f), msg)) | |
422 return False | |
423 | |
424 def badtype(mode): | |
425 kind = _('unknown') | |
426 if stat.S_ISCHR(mode): kind = _('character device') | |
427 elif stat.S_ISBLK(mode): kind = _('block device') | |
428 elif stat.S_ISFIFO(mode): kind = _('fifo') | |
429 elif stat.S_ISSOCK(mode): kind = _('socket') | |
430 elif stat.S_ISDIR(mode): kind = _('directory') | |
431 return _('unsupported file type (type is %s)') % kind | |
432 | |
433 ignore = self._ignore | |
434 dirignore = self._dirignore | |
435 if ignored: | |
436 ignore = util.never | |
437 dirignore = util.never | |
438 elif not unknown: | |
439 # if unknown and ignored are False, skip step 2 | |
440 ignore = util.always | |
441 dirignore = util.always | |
442 | |
443 matchfn = match.matchfn | |
444 badfn = match.bad | |
445 dmap = self._map | |
446 normpath = util.normpath | |
447 listdir = osutil.listdir | |
448 lstat = os.lstat | |
449 getkind = stat.S_IFMT | |
450 dirkind = stat.S_IFDIR | |
451 regkind = stat.S_IFREG | |
452 lnkkind = stat.S_IFLNK | |
453 join = self._join | |
454 work = [] | |
455 wadd = work.append | |
456 | |
457 if self._checkcase: | |
458 normalize = self._normalize | |
459 else: | |
460 normalize = lambda x, y: x | |
461 | |
462 exact = skipstep3 = False | |
463 if matchfn == match.exact: # match.exact | |
464 exact = True | |
465 dirignore = util.always # skip step 2 | |
466 elif match.files() and not match.anypats(): # match.match, no patterns | |
467 skipstep3 = True | |
468 | |
469 files = set(match.files()) | |
470 if not files or '.' in files: | |
471 files = [''] | |
472 results = {'.hg': None} | |
473 | |
474 # step 1: find all explicit files | |
475 for ff in sorted(files): | |
476 nf = normalize(normpath(ff), False) | |
477 if nf in results: | |
478 continue | |
479 | |
480 try: | |
481 st = lstat(join(nf)) | |
482 kind = getkind(st.st_mode) | |
483 if kind == dirkind: | |
484 skipstep3 = False | |
485 if nf in dmap: | |
486 #file deleted on disk but still in dirstate | |
487 results[nf] = None | |
488 match.dir(nf) | |
489 if not dirignore(nf): | |
490 wadd(nf) | |
491 elif kind == regkind or kind == lnkkind: | |
492 results[nf] = st | |
493 else: | |
494 badfn(ff, badtype(kind)) | |
495 if nf in dmap: | |
496 results[nf] = None | |
497 except OSError, inst: | |
498 if nf in dmap: # does it exactly match a file? | |
499 results[nf] = None | |
500 else: # does it match a directory? | |
501 prefix = nf + "/" | |
502 for fn in dmap: | |
503 if fn.startswith(prefix): | |
504 match.dir(nf) | |
505 skipstep3 = False | |
506 break | |
507 else: | |
508 badfn(ff, inst.strerror) | |
509 | |
510 # step 2: visit subdirectories | |
511 while work: | |
512 nd = work.pop() | |
513 skip = None | |
514 if nd == '.': | |
515 nd = '' | |
516 else: | |
517 skip = '.hg' | |
518 try: | |
519 entries = listdir(join(nd), stat=True, skip=skip) | |
520 except OSError, inst: | |
521 if inst.errno == errno.EACCES: | |
522 fwarn(nd, inst.strerror) | |
523 continue | |
524 raise | |
525 for f, kind, st in entries: | |
526 nf = normalize(nd and (nd + "/" + f) or f, True) | |
527 if nf not in results: | |
528 if kind == dirkind: | |
529 if not ignore(nf): | |
530 match.dir(nf) | |
531 wadd(nf) | |
532 if nf in dmap and matchfn(nf): | |
533 results[nf] = None | |
534 elif kind == regkind or kind == lnkkind: | |
535 if nf in dmap: | |
536 if matchfn(nf): | |
537 results[nf] = st | |
538 elif matchfn(nf) and not ignore(nf): | |
539 results[nf] = st | |
540 elif nf in dmap and matchfn(nf): | |
541 results[nf] = None | |
542 | |
543 # step 3: report unseen items in the dmap hash | |
544 if not skipstep3 and not exact: | |
545 visit = sorted([f for f in dmap if f not in results and matchfn(f)]) | |
546 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])): | |
547 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind): | |
548 st = None | |
549 results[nf] = st | |
550 | |
551 del results['.hg'] | |
552 return results | |
553 | |
554 def status(self, match, ignored, clean, unknown): | |
555 listignored, listclean, listunknown = ignored, clean, unknown | |
556 lookup, modified, added, unknown, ignored = [], [], [], [], [] | |
557 removed, deleted, clean = [], [], [] | |
558 | |
559 dmap = self._map | |
560 ladd = lookup.append | |
561 madd = modified.append | |
562 aadd = added.append | |
563 uadd = unknown.append | |
564 iadd = ignored.append | |
565 radd = removed.append | |
566 dadd = deleted.append | |
567 cadd = clean.append | |
568 | |
569 for fn, st in self.walk(match, listunknown, listignored).iteritems(): | |
570 if fn not in dmap: | |
571 if (listignored or match.exact(fn)) and self._dirignore(fn): | |
572 if listignored: | |
573 iadd(fn) | |
574 elif listunknown: | |
575 uadd(fn) | |
576 continue | |
577 | |
578 state, mode, size, time = dmap[fn] | |
579 | |
580 if not st and state in "nma": | |
581 dadd(fn) | |
582 elif state == 'n': | |
583 if (size >= 0 and | |
584 (size != st.st_size | |
585 or ((mode ^ st.st_mode) & 0100 and self._checkexec)) | |
586 or size == -2 | |
587 or fn in self._copymap): | |
588 madd(fn) | |
589 elif time != int(st.st_mtime): | |
590 ladd(fn) | |
591 elif listclean: | |
592 cadd(fn) | |
593 elif state == 'm': | |
594 madd(fn) | |
595 elif state == 'a': | |
596 aadd(fn) | |
597 elif state == 'r': | |
598 radd(fn) | |
599 | |
600 return (lookup, modified, added, removed, deleted, unknown, ignored, | |
601 clean) |