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)