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