##// END OF EJS Templates
dirstate: move _read into dirstatemap...
Durham Goode -
r34673:e159f217 default
parent child Browse files
Show More
@@ -1,1401 +1,1403 b''
1 # dirstate.py - working directory tracking for mercurial
1 # dirstate.py - working directory tracking for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import collections
10 import collections
11 import contextlib
11 import contextlib
12 import errno
12 import errno
13 import os
13 import os
14 import stat
14 import stat
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import nullid
17 from .node import nullid
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 error,
20 error,
21 match as matchmod,
21 match as matchmod,
22 pathutil,
22 pathutil,
23 policy,
23 policy,
24 pycompat,
24 pycompat,
25 scmutil,
25 scmutil,
26 txnutil,
26 txnutil,
27 util,
27 util,
28 )
28 )
29
29
30 parsers = policy.importmod(r'parsers')
30 parsers = policy.importmod(r'parsers')
31
31
32 propertycache = util.propertycache
32 propertycache = util.propertycache
33 filecache = scmutil.filecache
33 filecache = scmutil.filecache
34 _rangemask = 0x7fffffff
34 _rangemask = 0x7fffffff
35
35
36 dirstatetuple = parsers.dirstatetuple
36 dirstatetuple = parsers.dirstatetuple
37
37
38 class repocache(filecache):
38 class repocache(filecache):
39 """filecache for files in .hg/"""
39 """filecache for files in .hg/"""
40 def join(self, obj, fname):
40 def join(self, obj, fname):
41 return obj._opener.join(fname)
41 return obj._opener.join(fname)
42
42
43 class rootcache(filecache):
43 class rootcache(filecache):
44 """filecache for files in the repository root"""
44 """filecache for files in the repository root"""
45 def join(self, obj, fname):
45 def join(self, obj, fname):
46 return obj._join(fname)
46 return obj._join(fname)
47
47
48 def _getfsnow(vfs):
48 def _getfsnow(vfs):
49 '''Get "now" timestamp on filesystem'''
49 '''Get "now" timestamp on filesystem'''
50 tmpfd, tmpname = vfs.mkstemp()
50 tmpfd, tmpname = vfs.mkstemp()
51 try:
51 try:
52 return os.fstat(tmpfd).st_mtime
52 return os.fstat(tmpfd).st_mtime
53 finally:
53 finally:
54 os.close(tmpfd)
54 os.close(tmpfd)
55 vfs.unlink(tmpname)
55 vfs.unlink(tmpname)
56
56
57 class dirstate(object):
57 class dirstate(object):
58
58
59 def __init__(self, opener, ui, root, validate, sparsematchfn):
59 def __init__(self, opener, ui, root, validate, sparsematchfn):
60 '''Create a new dirstate object.
60 '''Create a new dirstate object.
61
61
62 opener is an open()-like callable that can be used to open the
62 opener is an open()-like callable that can be used to open the
63 dirstate file; root is the root of the directory tracked by
63 dirstate file; root is the root of the directory tracked by
64 the dirstate.
64 the dirstate.
65 '''
65 '''
66 self._opener = opener
66 self._opener = opener
67 self._validate = validate
67 self._validate = validate
68 self._root = root
68 self._root = root
69 self._sparsematchfn = sparsematchfn
69 self._sparsematchfn = sparsematchfn
70 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is
70 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is
71 # UNC path pointing to root share (issue4557)
71 # UNC path pointing to root share (issue4557)
72 self._rootdir = pathutil.normasprefix(root)
72 self._rootdir = pathutil.normasprefix(root)
73 self._dirty = False
73 self._dirty = False
74 self._lastnormaltime = 0
74 self._lastnormaltime = 0
75 self._ui = ui
75 self._ui = ui
76 self._filecache = {}
76 self._filecache = {}
77 self._parentwriters = 0
77 self._parentwriters = 0
78 self._filename = 'dirstate'
78 self._filename = 'dirstate'
79 self._pendingfilename = '%s.pending' % self._filename
79 self._pendingfilename = '%s.pending' % self._filename
80 self._plchangecallbacks = {}
80 self._plchangecallbacks = {}
81 self._origpl = None
81 self._origpl = None
82 self._updatedfiles = set()
82 self._updatedfiles = set()
83
83
84 @contextlib.contextmanager
84 @contextlib.contextmanager
85 def parentchange(self):
85 def parentchange(self):
86 '''Context manager for handling dirstate parents.
86 '''Context manager for handling dirstate parents.
87
87
88 If an exception occurs in the scope of the context manager,
88 If an exception occurs in the scope of the context manager,
89 the incoherent dirstate won't be written when wlock is
89 the incoherent dirstate won't be written when wlock is
90 released.
90 released.
91 '''
91 '''
92 self._parentwriters += 1
92 self._parentwriters += 1
93 yield
93 yield
94 # Typically we want the "undo" step of a context manager in a
94 # Typically we want the "undo" step of a context manager in a
95 # finally block so it happens even when an exception
95 # finally block so it happens even when an exception
96 # occurs. In this case, however, we only want to decrement
96 # occurs. In this case, however, we only want to decrement
97 # parentwriters if the code in the with statement exits
97 # parentwriters if the code in the with statement exits
98 # normally, so we don't have a try/finally here on purpose.
98 # normally, so we don't have a try/finally here on purpose.
99 self._parentwriters -= 1
99 self._parentwriters -= 1
100
100
101 def beginparentchange(self):
101 def beginparentchange(self):
102 '''Marks the beginning of a set of changes that involve changing
102 '''Marks the beginning of a set of changes that involve changing
103 the dirstate parents. If there is an exception during this time,
103 the dirstate parents. If there is an exception during this time,
104 the dirstate will not be written when the wlock is released. This
104 the dirstate will not be written when the wlock is released. This
105 prevents writing an incoherent dirstate where the parent doesn't
105 prevents writing an incoherent dirstate where the parent doesn't
106 match the contents.
106 match the contents.
107 '''
107 '''
108 self._ui.deprecwarn('beginparentchange is obsoleted by the '
108 self._ui.deprecwarn('beginparentchange is obsoleted by the '
109 'parentchange context manager.', '4.3')
109 'parentchange context manager.', '4.3')
110 self._parentwriters += 1
110 self._parentwriters += 1
111
111
112 def endparentchange(self):
112 def endparentchange(self):
113 '''Marks the end of a set of changes that involve changing the
113 '''Marks the end of a set of changes that involve changing the
114 dirstate parents. Once all parent changes have been marked done,
114 dirstate parents. Once all parent changes have been marked done,
115 the wlock will be free to write the dirstate on release.
115 the wlock will be free to write the dirstate on release.
116 '''
116 '''
117 self._ui.deprecwarn('endparentchange is obsoleted by the '
117 self._ui.deprecwarn('endparentchange is obsoleted by the '
118 'parentchange context manager.', '4.3')
118 'parentchange context manager.', '4.3')
119 if self._parentwriters > 0:
119 if self._parentwriters > 0:
120 self._parentwriters -= 1
120 self._parentwriters -= 1
121
121
122 def pendingparentchange(self):
122 def pendingparentchange(self):
123 '''Returns true if the dirstate is in the middle of a set of changes
123 '''Returns true if the dirstate is in the middle of a set of changes
124 that modify the dirstate parent.
124 that modify the dirstate parent.
125 '''
125 '''
126 return self._parentwriters > 0
126 return self._parentwriters > 0
127
127
128 @propertycache
128 @propertycache
129 def _map(self):
129 def _map(self):
130 '''Return the dirstate contents as a map from filename to
130 '''Return the dirstate contents as a map from filename to
131 (state, mode, size, time).'''
131 (state, mode, size, time).'''
132 self._read()
132 self._read()
133 return self._map
133 return self._map
134
134
135 @propertycache
135 @propertycache
136 def _identity(self):
136 def _identity(self):
137 self._read()
137 self._read()
138 return self._identity
138 return self._identity
139
139
140 @propertycache
140 @propertycache
141 def _nonnormalset(self):
141 def _nonnormalset(self):
142 nonnorm, otherparents = self._map.nonnormalentries()
142 nonnorm, otherparents = self._map.nonnormalentries()
143 self._otherparentset = otherparents
143 self._otherparentset = otherparents
144 return nonnorm
144 return nonnorm
145
145
146 @propertycache
146 @propertycache
147 def _otherparentset(self):
147 def _otherparentset(self):
148 nonnorm, otherparents = self._map.nonnormalentries()
148 nonnorm, otherparents = self._map.nonnormalentries()
149 self._nonnormalset = nonnorm
149 self._nonnormalset = nonnorm
150 return otherparents
150 return otherparents
151
151
152 @propertycache
152 @propertycache
153 def _filefoldmap(self):
153 def _filefoldmap(self):
154 return self._map.filefoldmap()
154 return self._map.filefoldmap()
155
155
156 @propertycache
156 @propertycache
157 def _dirfoldmap(self):
157 def _dirfoldmap(self):
158 f = {}
158 f = {}
159 normcase = util.normcase
159 normcase = util.normcase
160 for name in self._dirs:
160 for name in self._dirs:
161 f[normcase(name)] = name
161 f[normcase(name)] = name
162 return f
162 return f
163
163
164 @property
164 @property
165 def _sparsematcher(self):
165 def _sparsematcher(self):
166 """The matcher for the sparse checkout.
166 """The matcher for the sparse checkout.
167
167
168 The working directory may not include every file from a manifest. The
168 The working directory may not include every file from a manifest. The
169 matcher obtained by this property will match a path if it is to be
169 matcher obtained by this property will match a path if it is to be
170 included in the working directory.
170 included in the working directory.
171 """
171 """
172 # TODO there is potential to cache this property. For now, the matcher
172 # TODO there is potential to cache this property. For now, the matcher
173 # is resolved on every access. (But the called function does use a
173 # is resolved on every access. (But the called function does use a
174 # cache to keep the lookup fast.)
174 # cache to keep the lookup fast.)
175 return self._sparsematchfn()
175 return self._sparsematchfn()
176
176
177 @repocache('branch')
177 @repocache('branch')
178 def _branch(self):
178 def _branch(self):
179 try:
179 try:
180 return self._opener.read("branch").strip() or "default"
180 return self._opener.read("branch").strip() or "default"
181 except IOError as inst:
181 except IOError as inst:
182 if inst.errno != errno.ENOENT:
182 if inst.errno != errno.ENOENT:
183 raise
183 raise
184 return "default"
184 return "default"
185
185
186 @property
186 @property
187 def _pl(self):
187 def _pl(self):
188 return self._map.parents()
188 return self._map.parents()
189
189
190 @propertycache
190 @propertycache
191 def _dirs(self):
191 def _dirs(self):
192 return self._map.dirs()
192 return self._map.dirs()
193
193
194 def dirs(self):
194 def dirs(self):
195 return self._dirs
195 return self._dirs
196
196
197 @rootcache('.hgignore')
197 @rootcache('.hgignore')
198 def _ignore(self):
198 def _ignore(self):
199 files = self._ignorefiles()
199 files = self._ignorefiles()
200 if not files:
200 if not files:
201 return matchmod.never(self._root, '')
201 return matchmod.never(self._root, '')
202
202
203 pats = ['include:%s' % f for f in files]
203 pats = ['include:%s' % f for f in files]
204 return matchmod.match(self._root, '', [], pats, warn=self._ui.warn)
204 return matchmod.match(self._root, '', [], pats, warn=self._ui.warn)
205
205
206 @propertycache
206 @propertycache
207 def _slash(self):
207 def _slash(self):
208 return self._ui.configbool('ui', 'slash') and pycompat.ossep != '/'
208 return self._ui.configbool('ui', 'slash') and pycompat.ossep != '/'
209
209
210 @propertycache
210 @propertycache
211 def _checklink(self):
211 def _checklink(self):
212 return util.checklink(self._root)
212 return util.checklink(self._root)
213
213
214 @propertycache
214 @propertycache
215 def _checkexec(self):
215 def _checkexec(self):
216 return util.checkexec(self._root)
216 return util.checkexec(self._root)
217
217
218 @propertycache
218 @propertycache
219 def _checkcase(self):
219 def _checkcase(self):
220 return not util.fscasesensitive(self._join('.hg'))
220 return not util.fscasesensitive(self._join('.hg'))
221
221
222 def _join(self, f):
222 def _join(self, f):
223 # much faster than os.path.join()
223 # much faster than os.path.join()
224 # it's safe because f is always a relative path
224 # it's safe because f is always a relative path
225 return self._rootdir + f
225 return self._rootdir + f
226
226
227 def flagfunc(self, buildfallback):
227 def flagfunc(self, buildfallback):
228 if self._checklink and self._checkexec:
228 if self._checklink and self._checkexec:
229 def f(x):
229 def f(x):
230 try:
230 try:
231 st = os.lstat(self._join(x))
231 st = os.lstat(self._join(x))
232 if util.statislink(st):
232 if util.statislink(st):
233 return 'l'
233 return 'l'
234 if util.statisexec(st):
234 if util.statisexec(st):
235 return 'x'
235 return 'x'
236 except OSError:
236 except OSError:
237 pass
237 pass
238 return ''
238 return ''
239 return f
239 return f
240
240
241 fallback = buildfallback()
241 fallback = buildfallback()
242 if self._checklink:
242 if self._checklink:
243 def f(x):
243 def f(x):
244 if os.path.islink(self._join(x)):
244 if os.path.islink(self._join(x)):
245 return 'l'
245 return 'l'
246 if 'x' in fallback(x):
246 if 'x' in fallback(x):
247 return 'x'
247 return 'x'
248 return ''
248 return ''
249 return f
249 return f
250 if self._checkexec:
250 if self._checkexec:
251 def f(x):
251 def f(x):
252 if 'l' in fallback(x):
252 if 'l' in fallback(x):
253 return 'l'
253 return 'l'
254 if util.isexec(self._join(x)):
254 if util.isexec(self._join(x)):
255 return 'x'
255 return 'x'
256 return ''
256 return ''
257 return f
257 return f
258 else:
258 else:
259 return fallback
259 return fallback
260
260
261 @propertycache
261 @propertycache
262 def _cwd(self):
262 def _cwd(self):
263 # internal config: ui.forcecwd
263 # internal config: ui.forcecwd
264 forcecwd = self._ui.config('ui', 'forcecwd')
264 forcecwd = self._ui.config('ui', 'forcecwd')
265 if forcecwd:
265 if forcecwd:
266 return forcecwd
266 return forcecwd
267 return pycompat.getcwd()
267 return pycompat.getcwd()
268
268
269 def getcwd(self):
269 def getcwd(self):
270 '''Return the path from which a canonical path is calculated.
270 '''Return the path from which a canonical path is calculated.
271
271
272 This path should be used to resolve file patterns or to convert
272 This path should be used to resolve file patterns or to convert
273 canonical paths back to file paths for display. It shouldn't be
273 canonical paths back to file paths for display. It shouldn't be
274 used to get real file paths. Use vfs functions instead.
274 used to get real file paths. Use vfs functions instead.
275 '''
275 '''
276 cwd = self._cwd
276 cwd = self._cwd
277 if cwd == self._root:
277 if cwd == self._root:
278 return ''
278 return ''
279 # self._root ends with a path separator if self._root is '/' or 'C:\'
279 # self._root ends with a path separator if self._root is '/' or 'C:\'
280 rootsep = self._root
280 rootsep = self._root
281 if not util.endswithsep(rootsep):
281 if not util.endswithsep(rootsep):
282 rootsep += pycompat.ossep
282 rootsep += pycompat.ossep
283 if cwd.startswith(rootsep):
283 if cwd.startswith(rootsep):
284 return cwd[len(rootsep):]
284 return cwd[len(rootsep):]
285 else:
285 else:
286 # we're outside the repo. return an absolute path.
286 # we're outside the repo. return an absolute path.
287 return cwd
287 return cwd
288
288
289 def pathto(self, f, cwd=None):
289 def pathto(self, f, cwd=None):
290 if cwd is None:
290 if cwd is None:
291 cwd = self.getcwd()
291 cwd = self.getcwd()
292 path = util.pathto(self._root, cwd, f)
292 path = util.pathto(self._root, cwd, f)
293 if self._slash:
293 if self._slash:
294 return util.pconvert(path)
294 return util.pconvert(path)
295 return path
295 return path
296
296
297 def __getitem__(self, key):
297 def __getitem__(self, key):
298 '''Return the current state of key (a filename) in the dirstate.
298 '''Return the current state of key (a filename) in the dirstate.
299
299
300 States are:
300 States are:
301 n normal
301 n normal
302 m needs merging
302 m needs merging
303 r marked for removal
303 r marked for removal
304 a marked for addition
304 a marked for addition
305 ? not tracked
305 ? not tracked
306 '''
306 '''
307 return self._map.get(key, ("?",))[0]
307 return self._map.get(key, ("?",))[0]
308
308
309 def __contains__(self, key):
309 def __contains__(self, key):
310 return key in self._map
310 return key in self._map
311
311
312 def __iter__(self):
312 def __iter__(self):
313 return iter(sorted(self._map))
313 return iter(sorted(self._map))
314
314
315 def items(self):
315 def items(self):
316 return self._map.iteritems()
316 return self._map.iteritems()
317
317
318 iteritems = items
318 iteritems = items
319
319
320 def parents(self):
320 def parents(self):
321 return [self._validate(p) for p in self._pl]
321 return [self._validate(p) for p in self._pl]
322
322
323 def p1(self):
323 def p1(self):
324 return self._validate(self._pl[0])
324 return self._validate(self._pl[0])
325
325
326 def p2(self):
326 def p2(self):
327 return self._validate(self._pl[1])
327 return self._validate(self._pl[1])
328
328
329 def branch(self):
329 def branch(self):
330 return encoding.tolocal(self._branch)
330 return encoding.tolocal(self._branch)
331
331
332 def setparents(self, p1, p2=nullid):
332 def setparents(self, p1, p2=nullid):
333 """Set dirstate parents to p1 and p2.
333 """Set dirstate parents to p1 and p2.
334
334
335 When moving from two parents to one, 'm' merged entries a
335 When moving from two parents to one, 'm' merged entries a
336 adjusted to normal and previous copy records discarded and
336 adjusted to normal and previous copy records discarded and
337 returned by the call.
337 returned by the call.
338
338
339 See localrepo.setparents()
339 See localrepo.setparents()
340 """
340 """
341 if self._parentwriters == 0:
341 if self._parentwriters == 0:
342 raise ValueError("cannot set dirstate parent without "
342 raise ValueError("cannot set dirstate parent without "
343 "calling dirstate.beginparentchange")
343 "calling dirstate.beginparentchange")
344
344
345 self._dirty = True
345 self._dirty = True
346 oldp2 = self._pl[1]
346 oldp2 = self._pl[1]
347 if self._origpl is None:
347 if self._origpl is None:
348 self._origpl = self._pl
348 self._origpl = self._pl
349 self._map.setparents(p1, p2)
349 self._map.setparents(p1, p2)
350 copies = {}
350 copies = {}
351 if oldp2 != nullid and p2 == nullid:
351 if oldp2 != nullid and p2 == nullid:
352 candidatefiles = self._nonnormalset.union(self._otherparentset)
352 candidatefiles = self._nonnormalset.union(self._otherparentset)
353 for f in candidatefiles:
353 for f in candidatefiles:
354 s = self._map.get(f)
354 s = self._map.get(f)
355 if s is None:
355 if s is None:
356 continue
356 continue
357
357
358 # Discard 'm' markers when moving away from a merge state
358 # Discard 'm' markers when moving away from a merge state
359 if s[0] == 'm':
359 if s[0] == 'm':
360 source = self._map.copymap.get(f)
360 source = self._map.copymap.get(f)
361 if source:
361 if source:
362 copies[f] = source
362 copies[f] = source
363 self.normallookup(f)
363 self.normallookup(f)
364 # Also fix up otherparent markers
364 # Also fix up otherparent markers
365 elif s[0] == 'n' and s[2] == -2:
365 elif s[0] == 'n' and s[2] == -2:
366 source = self._map.copymap.get(f)
366 source = self._map.copymap.get(f)
367 if source:
367 if source:
368 copies[f] = source
368 copies[f] = source
369 self.add(f)
369 self.add(f)
370 return copies
370 return copies
371
371
372 def setbranch(self, branch):
372 def setbranch(self, branch):
373 self._branch = encoding.fromlocal(branch)
373 self._branch = encoding.fromlocal(branch)
374 f = self._opener('branch', 'w', atomictemp=True, checkambig=True)
374 f = self._opener('branch', 'w', atomictemp=True, checkambig=True)
375 try:
375 try:
376 f.write(self._branch + '\n')
376 f.write(self._branch + '\n')
377 f.close()
377 f.close()
378
378
379 # make sure filecache has the correct stat info for _branch after
379 # make sure filecache has the correct stat info for _branch after
380 # replacing the underlying file
380 # replacing the underlying file
381 ce = self._filecache['_branch']
381 ce = self._filecache['_branch']
382 if ce:
382 if ce:
383 ce.refresh()
383 ce.refresh()
384 except: # re-raises
384 except: # re-raises
385 f.discard()
385 f.discard()
386 raise
386 raise
387
387
388 def _read(self):
388 def _read(self):
389 self._map = dirstatemap(self._ui, self._opener, self._root)
390
391 # ignore HG_PENDING because identity is used only for writing
389 # ignore HG_PENDING because identity is used only for writing
392 self._identity = util.filestat.frompath(
390 self._identity = util.filestat.frompath(
393 self._opener.join(self._filename))
391 self._opener.join(self._filename))
394 try:
392 self._map = dirstatemap(self._ui, self._opener, self._root)
395 fp = self._map._opendirstatefile()
393 self._map.read()
396 try:
397 st = fp.read()
398 finally:
399 fp.close()
400 except IOError as err:
401 if err.errno != errno.ENOENT:
402 raise
403 return
404 if not st:
405 return
406
407 if util.safehasattr(parsers, 'dict_new_presized'):
408 # Make an estimate of the number of files in the dirstate based on
409 # its size. From a linear regression on a set of real-world repos,
410 # all over 10,000 files, the size of a dirstate entry is 85
411 # bytes. The cost of resizing is significantly higher than the cost
412 # of filling in a larger presized dict, so subtract 20% from the
413 # size.
414 #
415 # This heuristic is imperfect in many ways, so in a future dirstate
416 # format update it makes sense to just record the number of entries
417 # on write.
418 self._map._map = parsers.dict_new_presized(len(st) / 71)
419
420 # Python's garbage collector triggers a GC each time a certain number
421 # of container objects (the number being defined by
422 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
423 # for each file in the dirstate. The C version then immediately marks
424 # them as not to be tracked by the collector. However, this has no
425 # effect on when GCs are triggered, only on what objects the GC looks
426 # into. This means that O(number of files) GCs are unavoidable.
427 # Depending on when in the process's lifetime the dirstate is parsed,
428 # this can get very expensive. As a workaround, disable GC while
429 # parsing the dirstate.
430 #
431 # (we cannot decorate the function directly since it is in a C module)
432 parse_dirstate = util.nogc(parsers.parse_dirstate)
433 p = parse_dirstate(self._map._map, self._map.copymap, st)
434 if not self._map._dirtyparents:
435 self._map.setparents(*p)
436
394
437 def invalidate(self):
395 def invalidate(self):
438 '''Causes the next access to reread the dirstate.
396 '''Causes the next access to reread the dirstate.
439
397
440 This is different from localrepo.invalidatedirstate() because it always
398 This is different from localrepo.invalidatedirstate() because it always
441 rereads the dirstate. Use localrepo.invalidatedirstate() if you want to
399 rereads the dirstate. Use localrepo.invalidatedirstate() if you want to
442 check whether the dirstate has changed before rereading it.'''
400 check whether the dirstate has changed before rereading it.'''
443
401
444 for a in ("_map", "_identity",
402 for a in ("_map", "_identity",
445 "_filefoldmap", "_dirfoldmap", "_branch",
403 "_filefoldmap", "_dirfoldmap", "_branch",
446 "_dirs", "_ignore", "_nonnormalset",
404 "_dirs", "_ignore", "_nonnormalset",
447 "_otherparentset"):
405 "_otherparentset"):
448 if a in self.__dict__:
406 if a in self.__dict__:
449 delattr(self, a)
407 delattr(self, a)
450 self._lastnormaltime = 0
408 self._lastnormaltime = 0
451 self._dirty = False
409 self._dirty = False
452 self._updatedfiles.clear()
410 self._updatedfiles.clear()
453 self._parentwriters = 0
411 self._parentwriters = 0
454 self._origpl = None
412 self._origpl = None
455
413
456 def copy(self, source, dest):
414 def copy(self, source, dest):
457 """Mark dest as a copy of source. Unmark dest if source is None."""
415 """Mark dest as a copy of source. Unmark dest if source is None."""
458 if source == dest:
416 if source == dest:
459 return
417 return
460 self._dirty = True
418 self._dirty = True
461 if source is not None:
419 if source is not None:
462 self._map.copymap[dest] = source
420 self._map.copymap[dest] = source
463 self._updatedfiles.add(source)
421 self._updatedfiles.add(source)
464 self._updatedfiles.add(dest)
422 self._updatedfiles.add(dest)
465 elif self._map.copymap.pop(dest, None):
423 elif self._map.copymap.pop(dest, None):
466 self._updatedfiles.add(dest)
424 self._updatedfiles.add(dest)
467
425
468 def copied(self, file):
426 def copied(self, file):
469 return self._map.copymap.get(file, None)
427 return self._map.copymap.get(file, None)
470
428
471 def copies(self):
429 def copies(self):
472 return self._map.copymap
430 return self._map.copymap
473
431
474 def _droppath(self, f):
432 def _droppath(self, f):
475 if self[f] not in "?r" and "_dirs" in self.__dict__:
433 if self[f] not in "?r" and "_dirs" in self.__dict__:
476 self._dirs.delpath(f)
434 self._dirs.delpath(f)
477
435
478 if "_filefoldmap" in self.__dict__:
436 if "_filefoldmap" in self.__dict__:
479 normed = util.normcase(f)
437 normed = util.normcase(f)
480 if normed in self._filefoldmap:
438 if normed in self._filefoldmap:
481 del self._filefoldmap[normed]
439 del self._filefoldmap[normed]
482
440
483 self._updatedfiles.add(f)
441 self._updatedfiles.add(f)
484
442
485 def _addpath(self, f, state, mode, size, mtime):
443 def _addpath(self, f, state, mode, size, mtime):
486 oldstate = self[f]
444 oldstate = self[f]
487 if state == 'a' or oldstate == 'r':
445 if state == 'a' or oldstate == 'r':
488 scmutil.checkfilename(f)
446 scmutil.checkfilename(f)
489 if f in self._dirs:
447 if f in self._dirs:
490 raise error.Abort(_('directory %r already in dirstate') % f)
448 raise error.Abort(_('directory %r already in dirstate') % f)
491 # shadows
449 # shadows
492 for d in util.finddirs(f):
450 for d in util.finddirs(f):
493 if d in self._dirs:
451 if d in self._dirs:
494 break
452 break
495 entry = self._map.get(d)
453 entry = self._map.get(d)
496 if entry is not None and entry[0] != 'r':
454 if entry is not None and entry[0] != 'r':
497 raise error.Abort(
455 raise error.Abort(
498 _('file %r in dirstate clashes with %r') % (d, f))
456 _('file %r in dirstate clashes with %r') % (d, f))
499 if oldstate in "?r" and "_dirs" in self.__dict__:
457 if oldstate in "?r" and "_dirs" in self.__dict__:
500 self._dirs.addpath(f)
458 self._dirs.addpath(f)
501 self._dirty = True
459 self._dirty = True
502 self._updatedfiles.add(f)
460 self._updatedfiles.add(f)
503 self._map[f] = dirstatetuple(state, mode, size, mtime)
461 self._map[f] = dirstatetuple(state, mode, size, mtime)
504 if state != 'n' or mtime == -1:
462 if state != 'n' or mtime == -1:
505 self._nonnormalset.add(f)
463 self._nonnormalset.add(f)
506 if size == -2:
464 if size == -2:
507 self._otherparentset.add(f)
465 self._otherparentset.add(f)
508
466
509 def normal(self, f):
467 def normal(self, f):
510 '''Mark a file normal and clean.'''
468 '''Mark a file normal and clean.'''
511 s = os.lstat(self._join(f))
469 s = os.lstat(self._join(f))
512 mtime = s.st_mtime
470 mtime = s.st_mtime
513 self._addpath(f, 'n', s.st_mode,
471 self._addpath(f, 'n', s.st_mode,
514 s.st_size & _rangemask, mtime & _rangemask)
472 s.st_size & _rangemask, mtime & _rangemask)
515 self._map.copymap.pop(f, None)
473 self._map.copymap.pop(f, None)
516 if f in self._nonnormalset:
474 if f in self._nonnormalset:
517 self._nonnormalset.remove(f)
475 self._nonnormalset.remove(f)
518 if mtime > self._lastnormaltime:
476 if mtime > self._lastnormaltime:
519 # Remember the most recent modification timeslot for status(),
477 # Remember the most recent modification timeslot for status(),
520 # to make sure we won't miss future size-preserving file content
478 # to make sure we won't miss future size-preserving file content
521 # modifications that happen within the same timeslot.
479 # modifications that happen within the same timeslot.
522 self._lastnormaltime = mtime
480 self._lastnormaltime = mtime
523
481
524 def normallookup(self, f):
482 def normallookup(self, f):
525 '''Mark a file normal, but possibly dirty.'''
483 '''Mark a file normal, but possibly dirty.'''
526 if self._pl[1] != nullid:
484 if self._pl[1] != nullid:
527 # if there is a merge going on and the file was either
485 # if there is a merge going on and the file was either
528 # in state 'm' (-1) or coming from other parent (-2) before
486 # in state 'm' (-1) or coming from other parent (-2) before
529 # being removed, restore that state.
487 # being removed, restore that state.
530 entry = self._map.get(f)
488 entry = self._map.get(f)
531 if entry is not None:
489 if entry is not None:
532 if entry[0] == 'r' and entry[2] in (-1, -2):
490 if entry[0] == 'r' and entry[2] in (-1, -2):
533 source = self._map.copymap.get(f)
491 source = self._map.copymap.get(f)
534 if entry[2] == -1:
492 if entry[2] == -1:
535 self.merge(f)
493 self.merge(f)
536 elif entry[2] == -2:
494 elif entry[2] == -2:
537 self.otherparent(f)
495 self.otherparent(f)
538 if source:
496 if source:
539 self.copy(source, f)
497 self.copy(source, f)
540 return
498 return
541 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
499 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
542 return
500 return
543 self._addpath(f, 'n', 0, -1, -1)
501 self._addpath(f, 'n', 0, -1, -1)
544 self._map.copymap.pop(f, None)
502 self._map.copymap.pop(f, None)
545 if f in self._nonnormalset:
503 if f in self._nonnormalset:
546 self._nonnormalset.remove(f)
504 self._nonnormalset.remove(f)
547
505
548 def otherparent(self, f):
506 def otherparent(self, f):
549 '''Mark as coming from the other parent, always dirty.'''
507 '''Mark as coming from the other parent, always dirty.'''
550 if self._pl[1] == nullid:
508 if self._pl[1] == nullid:
551 raise error.Abort(_("setting %r to other parent "
509 raise error.Abort(_("setting %r to other parent "
552 "only allowed in merges") % f)
510 "only allowed in merges") % f)
553 if f in self and self[f] == 'n':
511 if f in self and self[f] == 'n':
554 # merge-like
512 # merge-like
555 self._addpath(f, 'm', 0, -2, -1)
513 self._addpath(f, 'm', 0, -2, -1)
556 else:
514 else:
557 # add-like
515 # add-like
558 self._addpath(f, 'n', 0, -2, -1)
516 self._addpath(f, 'n', 0, -2, -1)
559 self._map.copymap.pop(f, None)
517 self._map.copymap.pop(f, None)
560
518
561 def add(self, f):
519 def add(self, f):
562 '''Mark a file added.'''
520 '''Mark a file added.'''
563 self._addpath(f, 'a', 0, -1, -1)
521 self._addpath(f, 'a', 0, -1, -1)
564 self._map.copymap.pop(f, None)
522 self._map.copymap.pop(f, None)
565
523
566 def remove(self, f):
524 def remove(self, f):
567 '''Mark a file removed.'''
525 '''Mark a file removed.'''
568 self._dirty = True
526 self._dirty = True
569 self._droppath(f)
527 self._droppath(f)
570 size = 0
528 size = 0
571 if self._pl[1] != nullid:
529 if self._pl[1] != nullid:
572 entry = self._map.get(f)
530 entry = self._map.get(f)
573 if entry is not None:
531 if entry is not None:
574 # backup the previous state
532 # backup the previous state
575 if entry[0] == 'm': # merge
533 if entry[0] == 'm': # merge
576 size = -1
534 size = -1
577 elif entry[0] == 'n' and entry[2] == -2: # other parent
535 elif entry[0] == 'n' and entry[2] == -2: # other parent
578 size = -2
536 size = -2
579 self._otherparentset.add(f)
537 self._otherparentset.add(f)
580 self._map[f] = dirstatetuple('r', 0, size, 0)
538 self._map[f] = dirstatetuple('r', 0, size, 0)
581 self._nonnormalset.add(f)
539 self._nonnormalset.add(f)
582 if size == 0:
540 if size == 0:
583 self._map.copymap.pop(f, None)
541 self._map.copymap.pop(f, None)
584
542
585 def merge(self, f):
543 def merge(self, f):
586 '''Mark a file merged.'''
544 '''Mark a file merged.'''
587 if self._pl[1] == nullid:
545 if self._pl[1] == nullid:
588 return self.normallookup(f)
546 return self.normallookup(f)
589 return self.otherparent(f)
547 return self.otherparent(f)
590
548
591 def drop(self, f):
549 def drop(self, f):
592 '''Drop a file from the dirstate'''
550 '''Drop a file from the dirstate'''
593 if f in self._map:
551 if f in self._map:
594 self._dirty = True
552 self._dirty = True
595 self._droppath(f)
553 self._droppath(f)
596 del self._map[f]
554 del self._map[f]
597 if f in self._nonnormalset:
555 if f in self._nonnormalset:
598 self._nonnormalset.remove(f)
556 self._nonnormalset.remove(f)
599 self._map.copymap.pop(f, None)
557 self._map.copymap.pop(f, None)
600
558
601 def _discoverpath(self, path, normed, ignoremissing, exists, storemap):
559 def _discoverpath(self, path, normed, ignoremissing, exists, storemap):
602 if exists is None:
560 if exists is None:
603 exists = os.path.lexists(os.path.join(self._root, path))
561 exists = os.path.lexists(os.path.join(self._root, path))
604 if not exists:
562 if not exists:
605 # Maybe a path component exists
563 # Maybe a path component exists
606 if not ignoremissing and '/' in path:
564 if not ignoremissing and '/' in path:
607 d, f = path.rsplit('/', 1)
565 d, f = path.rsplit('/', 1)
608 d = self._normalize(d, False, ignoremissing, None)
566 d = self._normalize(d, False, ignoremissing, None)
609 folded = d + "/" + f
567 folded = d + "/" + f
610 else:
568 else:
611 # No path components, preserve original case
569 # No path components, preserve original case
612 folded = path
570 folded = path
613 else:
571 else:
614 # recursively normalize leading directory components
572 # recursively normalize leading directory components
615 # against dirstate
573 # against dirstate
616 if '/' in normed:
574 if '/' in normed:
617 d, f = normed.rsplit('/', 1)
575 d, f = normed.rsplit('/', 1)
618 d = self._normalize(d, False, ignoremissing, True)
576 d = self._normalize(d, False, ignoremissing, True)
619 r = self._root + "/" + d
577 r = self._root + "/" + d
620 folded = d + "/" + util.fspath(f, r)
578 folded = d + "/" + util.fspath(f, r)
621 else:
579 else:
622 folded = util.fspath(normed, self._root)
580 folded = util.fspath(normed, self._root)
623 storemap[normed] = folded
581 storemap[normed] = folded
624
582
625 return folded
583 return folded
626
584
627 def _normalizefile(self, path, isknown, ignoremissing=False, exists=None):
585 def _normalizefile(self, path, isknown, ignoremissing=False, exists=None):
628 normed = util.normcase(path)
586 normed = util.normcase(path)
629 folded = self._filefoldmap.get(normed, None)
587 folded = self._filefoldmap.get(normed, None)
630 if folded is None:
588 if folded is None:
631 if isknown:
589 if isknown:
632 folded = path
590 folded = path
633 else:
591 else:
634 folded = self._discoverpath(path, normed, ignoremissing, exists,
592 folded = self._discoverpath(path, normed, ignoremissing, exists,
635 self._filefoldmap)
593 self._filefoldmap)
636 return folded
594 return folded
637
595
638 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
596 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
639 normed = util.normcase(path)
597 normed = util.normcase(path)
640 folded = self._filefoldmap.get(normed, None)
598 folded = self._filefoldmap.get(normed, None)
641 if folded is None:
599 if folded is None:
642 folded = self._dirfoldmap.get(normed, None)
600 folded = self._dirfoldmap.get(normed, None)
643 if folded is None:
601 if folded is None:
644 if isknown:
602 if isknown:
645 folded = path
603 folded = path
646 else:
604 else:
647 # store discovered result in dirfoldmap so that future
605 # store discovered result in dirfoldmap so that future
648 # normalizefile calls don't start matching directories
606 # normalizefile calls don't start matching directories
649 folded = self._discoverpath(path, normed, ignoremissing, exists,
607 folded = self._discoverpath(path, normed, ignoremissing, exists,
650 self._dirfoldmap)
608 self._dirfoldmap)
651 return folded
609 return folded
652
610
653 def normalize(self, path, isknown=False, ignoremissing=False):
611 def normalize(self, path, isknown=False, ignoremissing=False):
654 '''
612 '''
655 normalize the case of a pathname when on a casefolding filesystem
613 normalize the case of a pathname when on a casefolding filesystem
656
614
657 isknown specifies whether the filename came from walking the
615 isknown specifies whether the filename came from walking the
658 disk, to avoid extra filesystem access.
616 disk, to avoid extra filesystem access.
659
617
660 If ignoremissing is True, missing path are returned
618 If ignoremissing is True, missing path are returned
661 unchanged. Otherwise, we try harder to normalize possibly
619 unchanged. Otherwise, we try harder to normalize possibly
662 existing path components.
620 existing path components.
663
621
664 The normalized case is determined based on the following precedence:
622 The normalized case is determined based on the following precedence:
665
623
666 - version of name already stored in the dirstate
624 - version of name already stored in the dirstate
667 - version of name stored on disk
625 - version of name stored on disk
668 - version provided via command arguments
626 - version provided via command arguments
669 '''
627 '''
670
628
671 if self._checkcase:
629 if self._checkcase:
672 return self._normalize(path, isknown, ignoremissing)
630 return self._normalize(path, isknown, ignoremissing)
673 return path
631 return path
674
632
675 def clear(self):
633 def clear(self):
676 self._map = dirstatemap(self._ui, self._opener, self._root)
634 self._map = dirstatemap(self._ui, self._opener, self._root)
677 self._nonnormalset = set()
635 self._nonnormalset = set()
678 self._otherparentset = set()
636 self._otherparentset = set()
679 if "_dirs" in self.__dict__:
637 if "_dirs" in self.__dict__:
680 delattr(self, "_dirs")
638 delattr(self, "_dirs")
681 self._map.setparents(nullid, nullid)
639 self._map.setparents(nullid, nullid)
682 self._lastnormaltime = 0
640 self._lastnormaltime = 0
683 self._updatedfiles.clear()
641 self._updatedfiles.clear()
684 self._dirty = True
642 self._dirty = True
685
643
686 def rebuild(self, parent, allfiles, changedfiles=None):
644 def rebuild(self, parent, allfiles, changedfiles=None):
687 if changedfiles is None:
645 if changedfiles is None:
688 # Rebuild entire dirstate
646 # Rebuild entire dirstate
689 changedfiles = allfiles
647 changedfiles = allfiles
690 lastnormaltime = self._lastnormaltime
648 lastnormaltime = self._lastnormaltime
691 self.clear()
649 self.clear()
692 self._lastnormaltime = lastnormaltime
650 self._lastnormaltime = lastnormaltime
693
651
694 if self._origpl is None:
652 if self._origpl is None:
695 self._origpl = self._pl
653 self._origpl = self._pl
696 self._map.setparents(parent, nullid)
654 self._map.setparents(parent, nullid)
697 for f in changedfiles:
655 for f in changedfiles:
698 if f in allfiles:
656 if f in allfiles:
699 self.normallookup(f)
657 self.normallookup(f)
700 else:
658 else:
701 self.drop(f)
659 self.drop(f)
702
660
703 self._dirty = True
661 self._dirty = True
704
662
705 def identity(self):
663 def identity(self):
706 '''Return identity of dirstate itself to detect changing in storage
664 '''Return identity of dirstate itself to detect changing in storage
707
665
708 If identity of previous dirstate is equal to this, writing
666 If identity of previous dirstate is equal to this, writing
709 changes based on the former dirstate out can keep consistency.
667 changes based on the former dirstate out can keep consistency.
710 '''
668 '''
711 return self._identity
669 return self._identity
712
670
713 def write(self, tr):
671 def write(self, tr):
714 if not self._dirty:
672 if not self._dirty:
715 return
673 return
716
674
717 filename = self._filename
675 filename = self._filename
718 if tr:
676 if tr:
719 # 'dirstate.write()' is not only for writing in-memory
677 # 'dirstate.write()' is not only for writing in-memory
720 # changes out, but also for dropping ambiguous timestamp.
678 # changes out, but also for dropping ambiguous timestamp.
721 # delayed writing re-raise "ambiguous timestamp issue".
679 # delayed writing re-raise "ambiguous timestamp issue".
722 # See also the wiki page below for detail:
680 # See also the wiki page below for detail:
723 # https://www.mercurial-scm.org/wiki/DirstateTransactionPlan
681 # https://www.mercurial-scm.org/wiki/DirstateTransactionPlan
724
682
725 # emulate dropping timestamp in 'parsers.pack_dirstate'
683 # emulate dropping timestamp in 'parsers.pack_dirstate'
726 now = _getfsnow(self._opener)
684 now = _getfsnow(self._opener)
727 dmap = self._map
685 dmap = self._map
728 for f in self._updatedfiles:
686 for f in self._updatedfiles:
729 e = dmap.get(f)
687 e = dmap.get(f)
730 if e is not None and e[0] == 'n' and e[3] == now:
688 if e is not None and e[0] == 'n' and e[3] == now:
731 dmap[f] = dirstatetuple(e[0], e[1], e[2], -1)
689 dmap[f] = dirstatetuple(e[0], e[1], e[2], -1)
732 self._nonnormalset.add(f)
690 self._nonnormalset.add(f)
733
691
734 # emulate that all 'dirstate.normal' results are written out
692 # emulate that all 'dirstate.normal' results are written out
735 self._lastnormaltime = 0
693 self._lastnormaltime = 0
736 self._updatedfiles.clear()
694 self._updatedfiles.clear()
737
695
738 # delay writing in-memory changes out
696 # delay writing in-memory changes out
739 tr.addfilegenerator('dirstate', (self._filename,),
697 tr.addfilegenerator('dirstate', (self._filename,),
740 self._writedirstate, location='plain')
698 self._writedirstate, location='plain')
741 return
699 return
742
700
743 st = self._opener(filename, "w", atomictemp=True, checkambig=True)
701 st = self._opener(filename, "w", atomictemp=True, checkambig=True)
744 self._writedirstate(st)
702 self._writedirstate(st)
745
703
746 def addparentchangecallback(self, category, callback):
704 def addparentchangecallback(self, category, callback):
747 """add a callback to be called when the wd parents are changed
705 """add a callback to be called when the wd parents are changed
748
706
749 Callback will be called with the following arguments:
707 Callback will be called with the following arguments:
750 dirstate, (oldp1, oldp2), (newp1, newp2)
708 dirstate, (oldp1, oldp2), (newp1, newp2)
751
709
752 Category is a unique identifier to allow overwriting an old callback
710 Category is a unique identifier to allow overwriting an old callback
753 with a newer callback.
711 with a newer callback.
754 """
712 """
755 self._plchangecallbacks[category] = callback
713 self._plchangecallbacks[category] = callback
756
714
757 def _writedirstate(self, st):
715 def _writedirstate(self, st):
758 # notify callbacks about parents change
716 # notify callbacks about parents change
759 if self._origpl is not None and self._origpl != self._pl:
717 if self._origpl is not None and self._origpl != self._pl:
760 for c, callback in sorted(self._plchangecallbacks.iteritems()):
718 for c, callback in sorted(self._plchangecallbacks.iteritems()):
761 callback(self, self._origpl, self._pl)
719 callback(self, self._origpl, self._pl)
762 self._origpl = None
720 self._origpl = None
763 # use the modification time of the newly created temporary file as the
721 # use the modification time of the newly created temporary file as the
764 # filesystem's notion of 'now'
722 # filesystem's notion of 'now'
765 now = util.fstat(st).st_mtime & _rangemask
723 now = util.fstat(st).st_mtime & _rangemask
766
724
767 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
725 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
768 # timestamp of each entries in dirstate, because of 'now > mtime'
726 # timestamp of each entries in dirstate, because of 'now > mtime'
769 delaywrite = self._ui.configint('debug', 'dirstate.delaywrite')
727 delaywrite = self._ui.configint('debug', 'dirstate.delaywrite')
770 if delaywrite > 0:
728 if delaywrite > 0:
771 # do we have any files to delay for?
729 # do we have any files to delay for?
772 for f, e in self._map.iteritems():
730 for f, e in self._map.iteritems():
773 if e[0] == 'n' and e[3] == now:
731 if e[0] == 'n' and e[3] == now:
774 import time # to avoid useless import
732 import time # to avoid useless import
775 # rather than sleep n seconds, sleep until the next
733 # rather than sleep n seconds, sleep until the next
776 # multiple of n seconds
734 # multiple of n seconds
777 clock = time.time()
735 clock = time.time()
778 start = int(clock) - (int(clock) % delaywrite)
736 start = int(clock) - (int(clock) % delaywrite)
779 end = start + delaywrite
737 end = start + delaywrite
780 time.sleep(end - clock)
738 time.sleep(end - clock)
781 now = end # trust our estimate that the end is near now
739 now = end # trust our estimate that the end is near now
782 break
740 break
783
741
784 st.write(parsers.pack_dirstate(self._map._map, self._map.copymap,
742 st.write(parsers.pack_dirstate(self._map._map, self._map.copymap,
785 self._pl, now))
743 self._pl, now))
786 self._nonnormalset, self._otherparentset = self._map.nonnormalentries()
744 self._nonnormalset, self._otherparentset = self._map.nonnormalentries()
787 st.close()
745 st.close()
788 self._lastnormaltime = 0
746 self._lastnormaltime = 0
789 self._dirty = self._map._dirtyparents = False
747 self._dirty = self._map._dirtyparents = False
790
748
791 def _dirignore(self, f):
749 def _dirignore(self, f):
792 if f == '.':
750 if f == '.':
793 return False
751 return False
794 if self._ignore(f):
752 if self._ignore(f):
795 return True
753 return True
796 for p in util.finddirs(f):
754 for p in util.finddirs(f):
797 if self._ignore(p):
755 if self._ignore(p):
798 return True
756 return True
799 return False
757 return False
800
758
801 def _ignorefiles(self):
759 def _ignorefiles(self):
802 files = []
760 files = []
803 if os.path.exists(self._join('.hgignore')):
761 if os.path.exists(self._join('.hgignore')):
804 files.append(self._join('.hgignore'))
762 files.append(self._join('.hgignore'))
805 for name, path in self._ui.configitems("ui"):
763 for name, path in self._ui.configitems("ui"):
806 if name == 'ignore' or name.startswith('ignore.'):
764 if name == 'ignore' or name.startswith('ignore.'):
807 # we need to use os.path.join here rather than self._join
765 # we need to use os.path.join here rather than self._join
808 # because path is arbitrary and user-specified
766 # because path is arbitrary and user-specified
809 files.append(os.path.join(self._rootdir, util.expandpath(path)))
767 files.append(os.path.join(self._rootdir, util.expandpath(path)))
810 return files
768 return files
811
769
812 def _ignorefileandline(self, f):
770 def _ignorefileandline(self, f):
813 files = collections.deque(self._ignorefiles())
771 files = collections.deque(self._ignorefiles())
814 visited = set()
772 visited = set()
815 while files:
773 while files:
816 i = files.popleft()
774 i = files.popleft()
817 patterns = matchmod.readpatternfile(i, self._ui.warn,
775 patterns = matchmod.readpatternfile(i, self._ui.warn,
818 sourceinfo=True)
776 sourceinfo=True)
819 for pattern, lineno, line in patterns:
777 for pattern, lineno, line in patterns:
820 kind, p = matchmod._patsplit(pattern, 'glob')
778 kind, p = matchmod._patsplit(pattern, 'glob')
821 if kind == "subinclude":
779 if kind == "subinclude":
822 if p not in visited:
780 if p not in visited:
823 files.append(p)
781 files.append(p)
824 continue
782 continue
825 m = matchmod.match(self._root, '', [], [pattern],
783 m = matchmod.match(self._root, '', [], [pattern],
826 warn=self._ui.warn)
784 warn=self._ui.warn)
827 if m(f):
785 if m(f):
828 return (i, lineno, line)
786 return (i, lineno, line)
829 visited.add(i)
787 visited.add(i)
830 return (None, -1, "")
788 return (None, -1, "")
831
789
832 def _walkexplicit(self, match, subrepos):
790 def _walkexplicit(self, match, subrepos):
833 '''Get stat data about the files explicitly specified by match.
791 '''Get stat data about the files explicitly specified by match.
834
792
835 Return a triple (results, dirsfound, dirsnotfound).
793 Return a triple (results, dirsfound, dirsnotfound).
836 - results is a mapping from filename to stat result. It also contains
794 - results is a mapping from filename to stat result. It also contains
837 listings mapping subrepos and .hg to None.
795 listings mapping subrepos and .hg to None.
838 - dirsfound is a list of files found to be directories.
796 - dirsfound is a list of files found to be directories.
839 - dirsnotfound is a list of files that the dirstate thinks are
797 - dirsnotfound is a list of files that the dirstate thinks are
840 directories and that were not found.'''
798 directories and that were not found.'''
841
799
842 def badtype(mode):
800 def badtype(mode):
843 kind = _('unknown')
801 kind = _('unknown')
844 if stat.S_ISCHR(mode):
802 if stat.S_ISCHR(mode):
845 kind = _('character device')
803 kind = _('character device')
846 elif stat.S_ISBLK(mode):
804 elif stat.S_ISBLK(mode):
847 kind = _('block device')
805 kind = _('block device')
848 elif stat.S_ISFIFO(mode):
806 elif stat.S_ISFIFO(mode):
849 kind = _('fifo')
807 kind = _('fifo')
850 elif stat.S_ISSOCK(mode):
808 elif stat.S_ISSOCK(mode):
851 kind = _('socket')
809 kind = _('socket')
852 elif stat.S_ISDIR(mode):
810 elif stat.S_ISDIR(mode):
853 kind = _('directory')
811 kind = _('directory')
854 return _('unsupported file type (type is %s)') % kind
812 return _('unsupported file type (type is %s)') % kind
855
813
856 matchedir = match.explicitdir
814 matchedir = match.explicitdir
857 badfn = match.bad
815 badfn = match.bad
858 dmap = self._map
816 dmap = self._map
859 lstat = os.lstat
817 lstat = os.lstat
860 getkind = stat.S_IFMT
818 getkind = stat.S_IFMT
861 dirkind = stat.S_IFDIR
819 dirkind = stat.S_IFDIR
862 regkind = stat.S_IFREG
820 regkind = stat.S_IFREG
863 lnkkind = stat.S_IFLNK
821 lnkkind = stat.S_IFLNK
864 join = self._join
822 join = self._join
865 dirsfound = []
823 dirsfound = []
866 foundadd = dirsfound.append
824 foundadd = dirsfound.append
867 dirsnotfound = []
825 dirsnotfound = []
868 notfoundadd = dirsnotfound.append
826 notfoundadd = dirsnotfound.append
869
827
870 if not match.isexact() and self._checkcase:
828 if not match.isexact() and self._checkcase:
871 normalize = self._normalize
829 normalize = self._normalize
872 else:
830 else:
873 normalize = None
831 normalize = None
874
832
875 files = sorted(match.files())
833 files = sorted(match.files())
876 subrepos.sort()
834 subrepos.sort()
877 i, j = 0, 0
835 i, j = 0, 0
878 while i < len(files) and j < len(subrepos):
836 while i < len(files) and j < len(subrepos):
879 subpath = subrepos[j] + "/"
837 subpath = subrepos[j] + "/"
880 if files[i] < subpath:
838 if files[i] < subpath:
881 i += 1
839 i += 1
882 continue
840 continue
883 while i < len(files) and files[i].startswith(subpath):
841 while i < len(files) and files[i].startswith(subpath):
884 del files[i]
842 del files[i]
885 j += 1
843 j += 1
886
844
887 if not files or '.' in files:
845 if not files or '.' in files:
888 files = ['.']
846 files = ['.']
889 results = dict.fromkeys(subrepos)
847 results = dict.fromkeys(subrepos)
890 results['.hg'] = None
848 results['.hg'] = None
891
849
892 alldirs = None
850 alldirs = None
893 for ff in files:
851 for ff in files:
894 # constructing the foldmap is expensive, so don't do it for the
852 # constructing the foldmap is expensive, so don't do it for the
895 # common case where files is ['.']
853 # common case where files is ['.']
896 if normalize and ff != '.':
854 if normalize and ff != '.':
897 nf = normalize(ff, False, True)
855 nf = normalize(ff, False, True)
898 else:
856 else:
899 nf = ff
857 nf = ff
900 if nf in results:
858 if nf in results:
901 continue
859 continue
902
860
903 try:
861 try:
904 st = lstat(join(nf))
862 st = lstat(join(nf))
905 kind = getkind(st.st_mode)
863 kind = getkind(st.st_mode)
906 if kind == dirkind:
864 if kind == dirkind:
907 if nf in dmap:
865 if nf in dmap:
908 # file replaced by dir on disk but still in dirstate
866 # file replaced by dir on disk but still in dirstate
909 results[nf] = None
867 results[nf] = None
910 if matchedir:
868 if matchedir:
911 matchedir(nf)
869 matchedir(nf)
912 foundadd((nf, ff))
870 foundadd((nf, ff))
913 elif kind == regkind or kind == lnkkind:
871 elif kind == regkind or kind == lnkkind:
914 results[nf] = st
872 results[nf] = st
915 else:
873 else:
916 badfn(ff, badtype(kind))
874 badfn(ff, badtype(kind))
917 if nf in dmap:
875 if nf in dmap:
918 results[nf] = None
876 results[nf] = None
919 except OSError as inst: # nf not found on disk - it is dirstate only
877 except OSError as inst: # nf not found on disk - it is dirstate only
920 if nf in dmap: # does it exactly match a missing file?
878 if nf in dmap: # does it exactly match a missing file?
921 results[nf] = None
879 results[nf] = None
922 else: # does it match a missing directory?
880 else: # does it match a missing directory?
923 if alldirs is None:
881 if alldirs is None:
924 alldirs = util.dirs(dmap._map)
882 alldirs = util.dirs(dmap._map)
925 if nf in alldirs:
883 if nf in alldirs:
926 if matchedir:
884 if matchedir:
927 matchedir(nf)
885 matchedir(nf)
928 notfoundadd(nf)
886 notfoundadd(nf)
929 else:
887 else:
930 badfn(ff, encoding.strtolocal(inst.strerror))
888 badfn(ff, encoding.strtolocal(inst.strerror))
931
889
932 # Case insensitive filesystems cannot rely on lstat() failing to detect
890 # Case insensitive filesystems cannot rely on lstat() failing to detect
933 # a case-only rename. Prune the stat object for any file that does not
891 # a case-only rename. Prune the stat object for any file that does not
934 # match the case in the filesystem, if there are multiple files that
892 # match the case in the filesystem, if there are multiple files that
935 # normalize to the same path.
893 # normalize to the same path.
936 if match.isexact() and self._checkcase:
894 if match.isexact() and self._checkcase:
937 normed = {}
895 normed = {}
938
896
939 for f, st in results.iteritems():
897 for f, st in results.iteritems():
940 if st is None:
898 if st is None:
941 continue
899 continue
942
900
943 nc = util.normcase(f)
901 nc = util.normcase(f)
944 paths = normed.get(nc)
902 paths = normed.get(nc)
945
903
946 if paths is None:
904 if paths is None:
947 paths = set()
905 paths = set()
948 normed[nc] = paths
906 normed[nc] = paths
949
907
950 paths.add(f)
908 paths.add(f)
951
909
952 for norm, paths in normed.iteritems():
910 for norm, paths in normed.iteritems():
953 if len(paths) > 1:
911 if len(paths) > 1:
954 for path in paths:
912 for path in paths:
955 folded = self._discoverpath(path, norm, True, None,
913 folded = self._discoverpath(path, norm, True, None,
956 self._dirfoldmap)
914 self._dirfoldmap)
957 if path != folded:
915 if path != folded:
958 results[path] = None
916 results[path] = None
959
917
960 return results, dirsfound, dirsnotfound
918 return results, dirsfound, dirsnotfound
961
919
962 def walk(self, match, subrepos, unknown, ignored, full=True):
920 def walk(self, match, subrepos, unknown, ignored, full=True):
963 '''
921 '''
964 Walk recursively through the directory tree, finding all files
922 Walk recursively through the directory tree, finding all files
965 matched by match.
923 matched by match.
966
924
967 If full is False, maybe skip some known-clean files.
925 If full is False, maybe skip some known-clean files.
968
926
969 Return a dict mapping filename to stat-like object (either
927 Return a dict mapping filename to stat-like object (either
970 mercurial.osutil.stat instance or return value of os.stat()).
928 mercurial.osutil.stat instance or return value of os.stat()).
971
929
972 '''
930 '''
973 # full is a flag that extensions that hook into walk can use -- this
931 # full is a flag that extensions that hook into walk can use -- this
974 # implementation doesn't use it at all. This satisfies the contract
932 # implementation doesn't use it at all. This satisfies the contract
975 # because we only guarantee a "maybe".
933 # because we only guarantee a "maybe".
976
934
977 if ignored:
935 if ignored:
978 ignore = util.never
936 ignore = util.never
979 dirignore = util.never
937 dirignore = util.never
980 elif unknown:
938 elif unknown:
981 ignore = self._ignore
939 ignore = self._ignore
982 dirignore = self._dirignore
940 dirignore = self._dirignore
983 else:
941 else:
984 # if not unknown and not ignored, drop dir recursion and step 2
942 # if not unknown and not ignored, drop dir recursion and step 2
985 ignore = util.always
943 ignore = util.always
986 dirignore = util.always
944 dirignore = util.always
987
945
988 matchfn = match.matchfn
946 matchfn = match.matchfn
989 matchalways = match.always()
947 matchalways = match.always()
990 matchtdir = match.traversedir
948 matchtdir = match.traversedir
991 dmap = self._map
949 dmap = self._map
992 listdir = util.listdir
950 listdir = util.listdir
993 lstat = os.lstat
951 lstat = os.lstat
994 dirkind = stat.S_IFDIR
952 dirkind = stat.S_IFDIR
995 regkind = stat.S_IFREG
953 regkind = stat.S_IFREG
996 lnkkind = stat.S_IFLNK
954 lnkkind = stat.S_IFLNK
997 join = self._join
955 join = self._join
998
956
999 exact = skipstep3 = False
957 exact = skipstep3 = False
1000 if match.isexact(): # match.exact
958 if match.isexact(): # match.exact
1001 exact = True
959 exact = True
1002 dirignore = util.always # skip step 2
960 dirignore = util.always # skip step 2
1003 elif match.prefix(): # match.match, no patterns
961 elif match.prefix(): # match.match, no patterns
1004 skipstep3 = True
962 skipstep3 = True
1005
963
1006 if not exact and self._checkcase:
964 if not exact and self._checkcase:
1007 normalize = self._normalize
965 normalize = self._normalize
1008 normalizefile = self._normalizefile
966 normalizefile = self._normalizefile
1009 skipstep3 = False
967 skipstep3 = False
1010 else:
968 else:
1011 normalize = self._normalize
969 normalize = self._normalize
1012 normalizefile = None
970 normalizefile = None
1013
971
1014 # step 1: find all explicit files
972 # step 1: find all explicit files
1015 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
973 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
1016
974
1017 skipstep3 = skipstep3 and not (work or dirsnotfound)
975 skipstep3 = skipstep3 and not (work or dirsnotfound)
1018 work = [d for d in work if not dirignore(d[0])]
976 work = [d for d in work if not dirignore(d[0])]
1019
977
1020 # step 2: visit subdirectories
978 # step 2: visit subdirectories
1021 def traverse(work, alreadynormed):
979 def traverse(work, alreadynormed):
1022 wadd = work.append
980 wadd = work.append
1023 while work:
981 while work:
1024 nd = work.pop()
982 nd = work.pop()
1025 if not match.visitdir(nd):
983 if not match.visitdir(nd):
1026 continue
984 continue
1027 skip = None
985 skip = None
1028 if nd == '.':
986 if nd == '.':
1029 nd = ''
987 nd = ''
1030 else:
988 else:
1031 skip = '.hg'
989 skip = '.hg'
1032 try:
990 try:
1033 entries = listdir(join(nd), stat=True, skip=skip)
991 entries = listdir(join(nd), stat=True, skip=skip)
1034 except OSError as inst:
992 except OSError as inst:
1035 if inst.errno in (errno.EACCES, errno.ENOENT):
993 if inst.errno in (errno.EACCES, errno.ENOENT):
1036 match.bad(self.pathto(nd),
994 match.bad(self.pathto(nd),
1037 encoding.strtolocal(inst.strerror))
995 encoding.strtolocal(inst.strerror))
1038 continue
996 continue
1039 raise
997 raise
1040 for f, kind, st in entries:
998 for f, kind, st in entries:
1041 if normalizefile:
999 if normalizefile:
1042 # even though f might be a directory, we're only
1000 # even though f might be a directory, we're only
1043 # interested in comparing it to files currently in the
1001 # interested in comparing it to files currently in the
1044 # dmap -- therefore normalizefile is enough
1002 # dmap -- therefore normalizefile is enough
1045 nf = normalizefile(nd and (nd + "/" + f) or f, True,
1003 nf = normalizefile(nd and (nd + "/" + f) or f, True,
1046 True)
1004 True)
1047 else:
1005 else:
1048 nf = nd and (nd + "/" + f) or f
1006 nf = nd and (nd + "/" + f) or f
1049 if nf not in results:
1007 if nf not in results:
1050 if kind == dirkind:
1008 if kind == dirkind:
1051 if not ignore(nf):
1009 if not ignore(nf):
1052 if matchtdir:
1010 if matchtdir:
1053 matchtdir(nf)
1011 matchtdir(nf)
1054 wadd(nf)
1012 wadd(nf)
1055 if nf in dmap and (matchalways or matchfn(nf)):
1013 if nf in dmap and (matchalways or matchfn(nf)):
1056 results[nf] = None
1014 results[nf] = None
1057 elif kind == regkind or kind == lnkkind:
1015 elif kind == regkind or kind == lnkkind:
1058 if nf in dmap:
1016 if nf in dmap:
1059 if matchalways or matchfn(nf):
1017 if matchalways or matchfn(nf):
1060 results[nf] = st
1018 results[nf] = st
1061 elif ((matchalways or matchfn(nf))
1019 elif ((matchalways or matchfn(nf))
1062 and not ignore(nf)):
1020 and not ignore(nf)):
1063 # unknown file -- normalize if necessary
1021 # unknown file -- normalize if necessary
1064 if not alreadynormed:
1022 if not alreadynormed:
1065 nf = normalize(nf, False, True)
1023 nf = normalize(nf, False, True)
1066 results[nf] = st
1024 results[nf] = st
1067 elif nf in dmap and (matchalways or matchfn(nf)):
1025 elif nf in dmap and (matchalways or matchfn(nf)):
1068 results[nf] = None
1026 results[nf] = None
1069
1027
1070 for nd, d in work:
1028 for nd, d in work:
1071 # alreadynormed means that processwork doesn't have to do any
1029 # alreadynormed means that processwork doesn't have to do any
1072 # expensive directory normalization
1030 # expensive directory normalization
1073 alreadynormed = not normalize or nd == d
1031 alreadynormed = not normalize or nd == d
1074 traverse([d], alreadynormed)
1032 traverse([d], alreadynormed)
1075
1033
1076 for s in subrepos:
1034 for s in subrepos:
1077 del results[s]
1035 del results[s]
1078 del results['.hg']
1036 del results['.hg']
1079
1037
1080 # step 3: visit remaining files from dmap
1038 # step 3: visit remaining files from dmap
1081 if not skipstep3 and not exact:
1039 if not skipstep3 and not exact:
1082 # If a dmap file is not in results yet, it was either
1040 # If a dmap file is not in results yet, it was either
1083 # a) not matching matchfn b) ignored, c) missing, or d) under a
1041 # a) not matching matchfn b) ignored, c) missing, or d) under a
1084 # symlink directory.
1042 # symlink directory.
1085 if not results and matchalways:
1043 if not results and matchalways:
1086 visit = [f for f in dmap]
1044 visit = [f for f in dmap]
1087 else:
1045 else:
1088 visit = [f for f in dmap if f not in results and matchfn(f)]
1046 visit = [f for f in dmap if f not in results and matchfn(f)]
1089 visit.sort()
1047 visit.sort()
1090
1048
1091 if unknown:
1049 if unknown:
1092 # unknown == True means we walked all dirs under the roots
1050 # unknown == True means we walked all dirs under the roots
1093 # that wasn't ignored, and everything that matched was stat'ed
1051 # that wasn't ignored, and everything that matched was stat'ed
1094 # and is already in results.
1052 # and is already in results.
1095 # The rest must thus be ignored or under a symlink.
1053 # The rest must thus be ignored or under a symlink.
1096 audit_path = pathutil.pathauditor(self._root, cached=True)
1054 audit_path = pathutil.pathauditor(self._root, cached=True)
1097
1055
1098 for nf in iter(visit):
1056 for nf in iter(visit):
1099 # If a stat for the same file was already added with a
1057 # If a stat for the same file was already added with a
1100 # different case, don't add one for this, since that would
1058 # different case, don't add one for this, since that would
1101 # make it appear as if the file exists under both names
1059 # make it appear as if the file exists under both names
1102 # on disk.
1060 # on disk.
1103 if (normalizefile and
1061 if (normalizefile and
1104 normalizefile(nf, True, True) in results):
1062 normalizefile(nf, True, True) in results):
1105 results[nf] = None
1063 results[nf] = None
1106 # Report ignored items in the dmap as long as they are not
1064 # Report ignored items in the dmap as long as they are not
1107 # under a symlink directory.
1065 # under a symlink directory.
1108 elif audit_path.check(nf):
1066 elif audit_path.check(nf):
1109 try:
1067 try:
1110 results[nf] = lstat(join(nf))
1068 results[nf] = lstat(join(nf))
1111 # file was just ignored, no links, and exists
1069 # file was just ignored, no links, and exists
1112 except OSError:
1070 except OSError:
1113 # file doesn't exist
1071 # file doesn't exist
1114 results[nf] = None
1072 results[nf] = None
1115 else:
1073 else:
1116 # It's either missing or under a symlink directory
1074 # It's either missing or under a symlink directory
1117 # which we in this case report as missing
1075 # which we in this case report as missing
1118 results[nf] = None
1076 results[nf] = None
1119 else:
1077 else:
1120 # We may not have walked the full directory tree above,
1078 # We may not have walked the full directory tree above,
1121 # so stat and check everything we missed.
1079 # so stat and check everything we missed.
1122 iv = iter(visit)
1080 iv = iter(visit)
1123 for st in util.statfiles([join(i) for i in visit]):
1081 for st in util.statfiles([join(i) for i in visit]):
1124 results[next(iv)] = st
1082 results[next(iv)] = st
1125 return results
1083 return results
1126
1084
1127 def status(self, match, subrepos, ignored, clean, unknown):
1085 def status(self, match, subrepos, ignored, clean, unknown):
1128 '''Determine the status of the working copy relative to the
1086 '''Determine the status of the working copy relative to the
1129 dirstate and return a pair of (unsure, status), where status is of type
1087 dirstate and return a pair of (unsure, status), where status is of type
1130 scmutil.status and:
1088 scmutil.status and:
1131
1089
1132 unsure:
1090 unsure:
1133 files that might have been modified since the dirstate was
1091 files that might have been modified since the dirstate was
1134 written, but need to be read to be sure (size is the same
1092 written, but need to be read to be sure (size is the same
1135 but mtime differs)
1093 but mtime differs)
1136 status.modified:
1094 status.modified:
1137 files that have definitely been modified since the dirstate
1095 files that have definitely been modified since the dirstate
1138 was written (different size or mode)
1096 was written (different size or mode)
1139 status.clean:
1097 status.clean:
1140 files that have definitely not been modified since the
1098 files that have definitely not been modified since the
1141 dirstate was written
1099 dirstate was written
1142 '''
1100 '''
1143 listignored, listclean, listunknown = ignored, clean, unknown
1101 listignored, listclean, listunknown = ignored, clean, unknown
1144 lookup, modified, added, unknown, ignored = [], [], [], [], []
1102 lookup, modified, added, unknown, ignored = [], [], [], [], []
1145 removed, deleted, clean = [], [], []
1103 removed, deleted, clean = [], [], []
1146
1104
1147 dmap = self._map
1105 dmap = self._map
1148 ladd = lookup.append # aka "unsure"
1106 ladd = lookup.append # aka "unsure"
1149 madd = modified.append
1107 madd = modified.append
1150 aadd = added.append
1108 aadd = added.append
1151 uadd = unknown.append
1109 uadd = unknown.append
1152 iadd = ignored.append
1110 iadd = ignored.append
1153 radd = removed.append
1111 radd = removed.append
1154 dadd = deleted.append
1112 dadd = deleted.append
1155 cadd = clean.append
1113 cadd = clean.append
1156 mexact = match.exact
1114 mexact = match.exact
1157 dirignore = self._dirignore
1115 dirignore = self._dirignore
1158 checkexec = self._checkexec
1116 checkexec = self._checkexec
1159 copymap = self._map.copymap
1117 copymap = self._map.copymap
1160 lastnormaltime = self._lastnormaltime
1118 lastnormaltime = self._lastnormaltime
1161
1119
1162 # We need to do full walks when either
1120 # We need to do full walks when either
1163 # - we're listing all clean files, or
1121 # - we're listing all clean files, or
1164 # - match.traversedir does something, because match.traversedir should
1122 # - match.traversedir does something, because match.traversedir should
1165 # be called for every dir in the working dir
1123 # be called for every dir in the working dir
1166 full = listclean or match.traversedir is not None
1124 full = listclean or match.traversedir is not None
1167 for fn, st in self.walk(match, subrepos, listunknown, listignored,
1125 for fn, st in self.walk(match, subrepos, listunknown, listignored,
1168 full=full).iteritems():
1126 full=full).iteritems():
1169 if fn not in dmap:
1127 if fn not in dmap:
1170 if (listignored or mexact(fn)) and dirignore(fn):
1128 if (listignored or mexact(fn)) and dirignore(fn):
1171 if listignored:
1129 if listignored:
1172 iadd(fn)
1130 iadd(fn)
1173 else:
1131 else:
1174 uadd(fn)
1132 uadd(fn)
1175 continue
1133 continue
1176
1134
1177 # This is equivalent to 'state, mode, size, time = dmap[fn]' but not
1135 # This is equivalent to 'state, mode, size, time = dmap[fn]' but not
1178 # written like that for performance reasons. dmap[fn] is not a
1136 # written like that for performance reasons. dmap[fn] is not a
1179 # Python tuple in compiled builds. The CPython UNPACK_SEQUENCE
1137 # Python tuple in compiled builds. The CPython UNPACK_SEQUENCE
1180 # opcode has fast paths when the value to be unpacked is a tuple or
1138 # opcode has fast paths when the value to be unpacked is a tuple or
1181 # a list, but falls back to creating a full-fledged iterator in
1139 # a list, but falls back to creating a full-fledged iterator in
1182 # general. That is much slower than simply accessing and storing the
1140 # general. That is much slower than simply accessing and storing the
1183 # tuple members one by one.
1141 # tuple members one by one.
1184 t = dmap[fn]
1142 t = dmap[fn]
1185 state = t[0]
1143 state = t[0]
1186 mode = t[1]
1144 mode = t[1]
1187 size = t[2]
1145 size = t[2]
1188 time = t[3]
1146 time = t[3]
1189
1147
1190 if not st and state in "nma":
1148 if not st and state in "nma":
1191 dadd(fn)
1149 dadd(fn)
1192 elif state == 'n':
1150 elif state == 'n':
1193 if (size >= 0 and
1151 if (size >= 0 and
1194 ((size != st.st_size and size != st.st_size & _rangemask)
1152 ((size != st.st_size and size != st.st_size & _rangemask)
1195 or ((mode ^ st.st_mode) & 0o100 and checkexec))
1153 or ((mode ^ st.st_mode) & 0o100 and checkexec))
1196 or size == -2 # other parent
1154 or size == -2 # other parent
1197 or fn in copymap):
1155 or fn in copymap):
1198 madd(fn)
1156 madd(fn)
1199 elif time != st.st_mtime and time != st.st_mtime & _rangemask:
1157 elif time != st.st_mtime and time != st.st_mtime & _rangemask:
1200 ladd(fn)
1158 ladd(fn)
1201 elif st.st_mtime == lastnormaltime:
1159 elif st.st_mtime == lastnormaltime:
1202 # fn may have just been marked as normal and it may have
1160 # fn may have just been marked as normal and it may have
1203 # changed in the same second without changing its size.
1161 # changed in the same second without changing its size.
1204 # This can happen if we quickly do multiple commits.
1162 # This can happen if we quickly do multiple commits.
1205 # Force lookup, so we don't miss such a racy file change.
1163 # Force lookup, so we don't miss such a racy file change.
1206 ladd(fn)
1164 ladd(fn)
1207 elif listclean:
1165 elif listclean:
1208 cadd(fn)
1166 cadd(fn)
1209 elif state == 'm':
1167 elif state == 'm':
1210 madd(fn)
1168 madd(fn)
1211 elif state == 'a':
1169 elif state == 'a':
1212 aadd(fn)
1170 aadd(fn)
1213 elif state == 'r':
1171 elif state == 'r':
1214 radd(fn)
1172 radd(fn)
1215
1173
1216 return (lookup, scmutil.status(modified, added, removed, deleted,
1174 return (lookup, scmutil.status(modified, added, removed, deleted,
1217 unknown, ignored, clean))
1175 unknown, ignored, clean))
1218
1176
1219 def matches(self, match):
1177 def matches(self, match):
1220 '''
1178 '''
1221 return files in the dirstate (in whatever state) filtered by match
1179 return files in the dirstate (in whatever state) filtered by match
1222 '''
1180 '''
1223 dmap = self._map
1181 dmap = self._map
1224 if match.always():
1182 if match.always():
1225 return dmap.keys()
1183 return dmap.keys()
1226 files = match.files()
1184 files = match.files()
1227 if match.isexact():
1185 if match.isexact():
1228 # fast path -- filter the other way around, since typically files is
1186 # fast path -- filter the other way around, since typically files is
1229 # much smaller than dmap
1187 # much smaller than dmap
1230 return [f for f in files if f in dmap]
1188 return [f for f in files if f in dmap]
1231 if match.prefix() and all(fn in dmap for fn in files):
1189 if match.prefix() and all(fn in dmap for fn in files):
1232 # fast path -- all the values are known to be files, so just return
1190 # fast path -- all the values are known to be files, so just return
1233 # that
1191 # that
1234 return list(files)
1192 return list(files)
1235 return [f for f in dmap if match(f)]
1193 return [f for f in dmap if match(f)]
1236
1194
1237 def _actualfilename(self, tr):
1195 def _actualfilename(self, tr):
1238 if tr:
1196 if tr:
1239 return self._pendingfilename
1197 return self._pendingfilename
1240 else:
1198 else:
1241 return self._filename
1199 return self._filename
1242
1200
1243 def savebackup(self, tr, backupname):
1201 def savebackup(self, tr, backupname):
1244 '''Save current dirstate into backup file'''
1202 '''Save current dirstate into backup file'''
1245 filename = self._actualfilename(tr)
1203 filename = self._actualfilename(tr)
1246 assert backupname != filename
1204 assert backupname != filename
1247
1205
1248 # use '_writedirstate' instead of 'write' to write changes certainly,
1206 # use '_writedirstate' instead of 'write' to write changes certainly,
1249 # because the latter omits writing out if transaction is running.
1207 # because the latter omits writing out if transaction is running.
1250 # output file will be used to create backup of dirstate at this point.
1208 # output file will be used to create backup of dirstate at this point.
1251 if self._dirty or not self._opener.exists(filename):
1209 if self._dirty or not self._opener.exists(filename):
1252 self._writedirstate(self._opener(filename, "w", atomictemp=True,
1210 self._writedirstate(self._opener(filename, "w", atomictemp=True,
1253 checkambig=True))
1211 checkambig=True))
1254
1212
1255 if tr:
1213 if tr:
1256 # ensure that subsequent tr.writepending returns True for
1214 # ensure that subsequent tr.writepending returns True for
1257 # changes written out above, even if dirstate is never
1215 # changes written out above, even if dirstate is never
1258 # changed after this
1216 # changed after this
1259 tr.addfilegenerator('dirstate', (self._filename,),
1217 tr.addfilegenerator('dirstate', (self._filename,),
1260 self._writedirstate, location='plain')
1218 self._writedirstate, location='plain')
1261
1219
1262 # ensure that pending file written above is unlinked at
1220 # ensure that pending file written above is unlinked at
1263 # failure, even if tr.writepending isn't invoked until the
1221 # failure, even if tr.writepending isn't invoked until the
1264 # end of this transaction
1222 # end of this transaction
1265 tr.registertmp(filename, location='plain')
1223 tr.registertmp(filename, location='plain')
1266
1224
1267 self._opener.tryunlink(backupname)
1225 self._opener.tryunlink(backupname)
1268 # hardlink backup is okay because _writedirstate is always called
1226 # hardlink backup is okay because _writedirstate is always called
1269 # with an "atomictemp=True" file.
1227 # with an "atomictemp=True" file.
1270 util.copyfile(self._opener.join(filename),
1228 util.copyfile(self._opener.join(filename),
1271 self._opener.join(backupname), hardlink=True)
1229 self._opener.join(backupname), hardlink=True)
1272
1230
1273 def restorebackup(self, tr, backupname):
1231 def restorebackup(self, tr, backupname):
1274 '''Restore dirstate by backup file'''
1232 '''Restore dirstate by backup file'''
1275 # this "invalidate()" prevents "wlock.release()" from writing
1233 # this "invalidate()" prevents "wlock.release()" from writing
1276 # changes of dirstate out after restoring from backup file
1234 # changes of dirstate out after restoring from backup file
1277 self.invalidate()
1235 self.invalidate()
1278 filename = self._actualfilename(tr)
1236 filename = self._actualfilename(tr)
1279 self._opener.rename(backupname, filename, checkambig=True)
1237 self._opener.rename(backupname, filename, checkambig=True)
1280
1238
1281 def clearbackup(self, tr, backupname):
1239 def clearbackup(self, tr, backupname):
1282 '''Clear backup file'''
1240 '''Clear backup file'''
1283 self._opener.unlink(backupname)
1241 self._opener.unlink(backupname)
1284
1242
1285 class dirstatemap(object):
1243 class dirstatemap(object):
1286 def __init__(self, ui, opener, root):
1244 def __init__(self, ui, opener, root):
1287 self._ui = ui
1245 self._ui = ui
1288 self._opener = opener
1246 self._opener = opener
1289 self._root = root
1247 self._root = root
1290 self._filename = 'dirstate'
1248 self._filename = 'dirstate'
1291
1249
1292 self._map = {}
1250 self._map = {}
1293 self.copymap = {}
1251 self.copymap = {}
1294 self._parents = None
1252 self._parents = None
1295 self._dirtyparents = False
1253 self._dirtyparents = False
1296
1254
1297 # for consistent view between _pl() and _read() invocations
1255 # for consistent view between _pl() and _read() invocations
1298 self._pendingmode = None
1256 self._pendingmode = None
1299
1257
1300 def iteritems(self):
1258 def iteritems(self):
1301 return self._map.iteritems()
1259 return self._map.iteritems()
1302
1260
1303 def __len__(self):
1261 def __len__(self):
1304 return len(self._map)
1262 return len(self._map)
1305
1263
1306 def __iter__(self):
1264 def __iter__(self):
1307 return iter(self._map)
1265 return iter(self._map)
1308
1266
1309 def get(self, key, default=None):
1267 def get(self, key, default=None):
1310 return self._map.get(key, default)
1268 return self._map.get(key, default)
1311
1269
1312 def __contains__(self, key):
1270 def __contains__(self, key):
1313 return key in self._map
1271 return key in self._map
1314
1272
1315 def __setitem__(self, key, value):
1273 def __setitem__(self, key, value):
1316 self._map[key] = value
1274 self._map[key] = value
1317
1275
1318 def __getitem__(self, key):
1276 def __getitem__(self, key):
1319 return self._map[key]
1277 return self._map[key]
1320
1278
1321 def __delitem__(self, key):
1279 def __delitem__(self, key):
1322 del self._map[key]
1280 del self._map[key]
1323
1281
1324 def keys(self):
1282 def keys(self):
1325 return self._map.keys()
1283 return self._map.keys()
1326
1284
1327 def nonnormalentries(self):
1285 def nonnormalentries(self):
1328 '''Compute the nonnormal dirstate entries from the dmap'''
1286 '''Compute the nonnormal dirstate entries from the dmap'''
1329 try:
1287 try:
1330 return parsers.nonnormalotherparententries(self._map)
1288 return parsers.nonnormalotherparententries(self._map)
1331 except AttributeError:
1289 except AttributeError:
1332 nonnorm = set()
1290 nonnorm = set()
1333 otherparent = set()
1291 otherparent = set()
1334 for fname, e in self._map.iteritems():
1292 for fname, e in self._map.iteritems():
1335 if e[0] != 'n' or e[3] == -1:
1293 if e[0] != 'n' or e[3] == -1:
1336 nonnorm.add(fname)
1294 nonnorm.add(fname)
1337 if e[0] == 'n' and e[2] == -2:
1295 if e[0] == 'n' and e[2] == -2:
1338 otherparent.add(fname)
1296 otherparent.add(fname)
1339 return nonnorm, otherparent
1297 return nonnorm, otherparent
1340
1298
1341 def filefoldmap(self):
1299 def filefoldmap(self):
1342 """Returns a dictionary mapping normalized case paths to their
1300 """Returns a dictionary mapping normalized case paths to their
1343 non-normalized versions.
1301 non-normalized versions.
1344 """
1302 """
1345 try:
1303 try:
1346 makefilefoldmap = parsers.make_file_foldmap
1304 makefilefoldmap = parsers.make_file_foldmap
1347 except AttributeError:
1305 except AttributeError:
1348 pass
1306 pass
1349 else:
1307 else:
1350 return makefilefoldmap(self._map, util.normcasespec,
1308 return makefilefoldmap(self._map, util.normcasespec,
1351 util.normcasefallback)
1309 util.normcasefallback)
1352
1310
1353 f = {}
1311 f = {}
1354 normcase = util.normcase
1312 normcase = util.normcase
1355 for name, s in self._map.iteritems():
1313 for name, s in self._map.iteritems():
1356 if s[0] != 'r':
1314 if s[0] != 'r':
1357 f[normcase(name)] = name
1315 f[normcase(name)] = name
1358 f['.'] = '.' # prevents useless util.fspath() invocation
1316 f['.'] = '.' # prevents useless util.fspath() invocation
1359 return f
1317 return f
1360
1318
1361 def dirs(self):
1319 def dirs(self):
1362 """Returns a set-like object containing all the directories in the
1320 """Returns a set-like object containing all the directories in the
1363 current dirstate.
1321 current dirstate.
1364 """
1322 """
1365 return util.dirs(self._map, 'r')
1323 return util.dirs(self._map, 'r')
1366
1324
1367 def _opendirstatefile(self):
1325 def _opendirstatefile(self):
1368 fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
1326 fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
1369 if self._pendingmode is not None and self._pendingmode != mode:
1327 if self._pendingmode is not None and self._pendingmode != mode:
1370 fp.close()
1328 fp.close()
1371 raise error.Abort(_('working directory state may be '
1329 raise error.Abort(_('working directory state may be '
1372 'changed parallelly'))
1330 'changed parallelly'))
1373 self._pendingmode = mode
1331 self._pendingmode = mode
1374 return fp
1332 return fp
1375
1333
1376 def parents(self):
1334 def parents(self):
1377 if not self._parents:
1335 if not self._parents:
1378 try:
1336 try:
1379 fp = self._opendirstatefile()
1337 fp = self._opendirstatefile()
1380 st = fp.read(40)
1338 st = fp.read(40)
1381 fp.close()
1339 fp.close()
1382 except IOError as err:
1340 except IOError as err:
1383 if err.errno != errno.ENOENT:
1341 if err.errno != errno.ENOENT:
1384 raise
1342 raise
1385 # File doesn't exist, so the current state is empty
1343 # File doesn't exist, so the current state is empty
1386 st = ''
1344 st = ''
1387
1345
1388 l = len(st)
1346 l = len(st)
1389 if l == 40:
1347 if l == 40:
1390 self._parents = st[:20], st[20:40]
1348 self._parents = st[:20], st[20:40]
1391 elif l == 0:
1349 elif l == 0:
1392 self._parents = [nullid, nullid]
1350 self._parents = [nullid, nullid]
1393 else:
1351 else:
1394 raise error.Abort(_('working directory state appears '
1352 raise error.Abort(_('working directory state appears '
1395 'damaged!'))
1353 'damaged!'))
1396
1354
1397 return self._parents
1355 return self._parents
1398
1356
1399 def setparents(self, p1, p2):
1357 def setparents(self, p1, p2):
1400 self._parents = (p1, p2)
1358 self._parents = (p1, p2)
1401 self._dirtyparents = True
1359 self._dirtyparents = True
1360
1361 def read(self):
1362 try:
1363 fp = self._opendirstatefile()
1364 try:
1365 st = fp.read()
1366 finally:
1367 fp.close()
1368 except IOError as err:
1369 if err.errno != errno.ENOENT:
1370 raise
1371 return
1372 if not st:
1373 return
1374
1375 if util.safehasattr(parsers, 'dict_new_presized'):
1376 # Make an estimate of the number of files in the dirstate based on
1377 # its size. From a linear regression on a set of real-world repos,
1378 # all over 10,000 files, the size of a dirstate entry is 85
1379 # bytes. The cost of resizing is significantly higher than the cost
1380 # of filling in a larger presized dict, so subtract 20% from the
1381 # size.
1382 #
1383 # This heuristic is imperfect in many ways, so in a future dirstate
1384 # format update it makes sense to just record the number of entries
1385 # on write.
1386 self._map = parsers.dict_new_presized(len(st) / 71)
1387
1388 # Python's garbage collector triggers a GC each time a certain number
1389 # of container objects (the number being defined by
1390 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
1391 # for each file in the dirstate. The C version then immediately marks
1392 # them as not to be tracked by the collector. However, this has no
1393 # effect on when GCs are triggered, only on what objects the GC looks
1394 # into. This means that O(number of files) GCs are unavoidable.
1395 # Depending on when in the process's lifetime the dirstate is parsed,
1396 # this can get very expensive. As a workaround, disable GC while
1397 # parsing the dirstate.
1398 #
1399 # (we cannot decorate the function directly since it is in a C module)
1400 parse_dirstate = util.nogc(parsers.parse_dirstate)
1401 p = parse_dirstate(self._map, self.copymap, st)
1402 if not self._dirtyparents:
1403 self.setparents(*p)
General Comments 0
You need to be logged in to leave comments. Login now