##// END OF EJS Templates
addremove: don't audit the path for paths already in the dirstate...
Durham Goode -
r18626:b114e41c default
parent child Browse files
Show More
@@ -1,1006 +1,1003
1 # scmutil.py - Mercurial core utility functions
1 # scmutil.py - Mercurial core utility functions
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright 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 i18n import _
8 from i18n import _
9 from mercurial.node import nullrev
9 from mercurial.node import nullrev
10 import util, error, osutil, revset, similar, encoding, phases
10 import util, error, osutil, revset, similar, encoding, phases
11 import match as matchmod
11 import match as matchmod
12 import os, errno, re, stat, sys, glob
12 import os, errno, re, stat, sys, glob
13
13
14 def nochangesfound(ui, repo, excluded=None):
14 def nochangesfound(ui, repo, excluded=None):
15 '''Report no changes for push/pull, excluded is None or a list of
15 '''Report no changes for push/pull, excluded is None or a list of
16 nodes excluded from the push/pull.
16 nodes excluded from the push/pull.
17 '''
17 '''
18 secretlist = []
18 secretlist = []
19 if excluded:
19 if excluded:
20 for n in excluded:
20 for n in excluded:
21 if n not in repo:
21 if n not in repo:
22 # discovery should not have included the filtered revision,
22 # discovery should not have included the filtered revision,
23 # we have to explicitly exclude it until discovery is cleanup.
23 # we have to explicitly exclude it until discovery is cleanup.
24 continue
24 continue
25 ctx = repo[n]
25 ctx = repo[n]
26 if ctx.phase() >= phases.secret and not ctx.extinct():
26 if ctx.phase() >= phases.secret and not ctx.extinct():
27 secretlist.append(n)
27 secretlist.append(n)
28
28
29 if secretlist:
29 if secretlist:
30 ui.status(_("no changes found (ignored %d secret changesets)\n")
30 ui.status(_("no changes found (ignored %d secret changesets)\n")
31 % len(secretlist))
31 % len(secretlist))
32 else:
32 else:
33 ui.status(_("no changes found\n"))
33 ui.status(_("no changes found\n"))
34
34
35 def checknewlabel(repo, lbl, kind):
35 def checknewlabel(repo, lbl, kind):
36 if lbl in ['tip', '.', 'null']:
36 if lbl in ['tip', '.', 'null']:
37 raise util.Abort(_("the name '%s' is reserved") % lbl)
37 raise util.Abort(_("the name '%s' is reserved") % lbl)
38 for c in (':', '\0', '\n', '\r'):
38 for c in (':', '\0', '\n', '\r'):
39 if c in lbl:
39 if c in lbl:
40 raise util.Abort(_("%r cannot be used in a name") % c)
40 raise util.Abort(_("%r cannot be used in a name") % c)
41 try:
41 try:
42 int(lbl)
42 int(lbl)
43 raise util.Abort(_("a %s cannot have an integer as its name") % kind)
43 raise util.Abort(_("a %s cannot have an integer as its name") % kind)
44 except ValueError:
44 except ValueError:
45 pass
45 pass
46
46
47 def checkfilename(f):
47 def checkfilename(f):
48 '''Check that the filename f is an acceptable filename for a tracked file'''
48 '''Check that the filename f is an acceptable filename for a tracked file'''
49 if '\r' in f or '\n' in f:
49 if '\r' in f or '\n' in f:
50 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
50 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
51
51
52 def checkportable(ui, f):
52 def checkportable(ui, f):
53 '''Check if filename f is portable and warn or abort depending on config'''
53 '''Check if filename f is portable and warn or abort depending on config'''
54 checkfilename(f)
54 checkfilename(f)
55 abort, warn = checkportabilityalert(ui)
55 abort, warn = checkportabilityalert(ui)
56 if abort or warn:
56 if abort or warn:
57 msg = util.checkwinfilename(f)
57 msg = util.checkwinfilename(f)
58 if msg:
58 if msg:
59 msg = "%s: %r" % (msg, f)
59 msg = "%s: %r" % (msg, f)
60 if abort:
60 if abort:
61 raise util.Abort(msg)
61 raise util.Abort(msg)
62 ui.warn(_("warning: %s\n") % msg)
62 ui.warn(_("warning: %s\n") % msg)
63
63
64 def checkportabilityalert(ui):
64 def checkportabilityalert(ui):
65 '''check if the user's config requests nothing, a warning, or abort for
65 '''check if the user's config requests nothing, a warning, or abort for
66 non-portable filenames'''
66 non-portable filenames'''
67 val = ui.config('ui', 'portablefilenames', 'warn')
67 val = ui.config('ui', 'portablefilenames', 'warn')
68 lval = val.lower()
68 lval = val.lower()
69 bval = util.parsebool(val)
69 bval = util.parsebool(val)
70 abort = os.name == 'nt' or lval == 'abort'
70 abort = os.name == 'nt' or lval == 'abort'
71 warn = bval or lval == 'warn'
71 warn = bval or lval == 'warn'
72 if bval is None and not (warn or abort or lval == 'ignore'):
72 if bval is None and not (warn or abort or lval == 'ignore'):
73 raise error.ConfigError(
73 raise error.ConfigError(
74 _("ui.portablefilenames value is invalid ('%s')") % val)
74 _("ui.portablefilenames value is invalid ('%s')") % val)
75 return abort, warn
75 return abort, warn
76
76
77 class casecollisionauditor(object):
77 class casecollisionauditor(object):
78 def __init__(self, ui, abort, dirstate):
78 def __init__(self, ui, abort, dirstate):
79 self._ui = ui
79 self._ui = ui
80 self._abort = abort
80 self._abort = abort
81 allfiles = '\0'.join(dirstate._map)
81 allfiles = '\0'.join(dirstate._map)
82 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
82 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
83 self._dirstate = dirstate
83 self._dirstate = dirstate
84 # The purpose of _newfiles is so that we don't complain about
84 # The purpose of _newfiles is so that we don't complain about
85 # case collisions if someone were to call this object with the
85 # case collisions if someone were to call this object with the
86 # same filename twice.
86 # same filename twice.
87 self._newfiles = set()
87 self._newfiles = set()
88
88
89 def __call__(self, f):
89 def __call__(self, f):
90 fl = encoding.lower(f)
90 fl = encoding.lower(f)
91 if (fl in self._loweredfiles and f not in self._dirstate and
91 if (fl in self._loweredfiles and f not in self._dirstate and
92 f not in self._newfiles):
92 f not in self._newfiles):
93 msg = _('possible case-folding collision for %s') % f
93 msg = _('possible case-folding collision for %s') % f
94 if self._abort:
94 if self._abort:
95 raise util.Abort(msg)
95 raise util.Abort(msg)
96 self._ui.warn(_("warning: %s\n") % msg)
96 self._ui.warn(_("warning: %s\n") % msg)
97 self._loweredfiles.add(fl)
97 self._loweredfiles.add(fl)
98 self._newfiles.add(f)
98 self._newfiles.add(f)
99
99
100 class pathauditor(object):
100 class pathauditor(object):
101 '''ensure that a filesystem path contains no banned components.
101 '''ensure that a filesystem path contains no banned components.
102 the following properties of a path are checked:
102 the following properties of a path are checked:
103
103
104 - ends with a directory separator
104 - ends with a directory separator
105 - under top-level .hg
105 - under top-level .hg
106 - starts at the root of a windows drive
106 - starts at the root of a windows drive
107 - contains ".."
107 - contains ".."
108 - traverses a symlink (e.g. a/symlink_here/b)
108 - traverses a symlink (e.g. a/symlink_here/b)
109 - inside a nested repository (a callback can be used to approve
109 - inside a nested repository (a callback can be used to approve
110 some nested repositories, e.g., subrepositories)
110 some nested repositories, e.g., subrepositories)
111 '''
111 '''
112
112
113 def __init__(self, root, callback=None):
113 def __init__(self, root, callback=None):
114 self.audited = set()
114 self.audited = set()
115 self.auditeddir = set()
115 self.auditeddir = set()
116 self.root = root
116 self.root = root
117 self.callback = callback
117 self.callback = callback
118 if os.path.lexists(root) and not util.checkcase(root):
118 if os.path.lexists(root) and not util.checkcase(root):
119 self.normcase = util.normcase
119 self.normcase = util.normcase
120 else:
120 else:
121 self.normcase = lambda x: x
121 self.normcase = lambda x: x
122
122
123 def __call__(self, path):
123 def __call__(self, path):
124 '''Check the relative path.
124 '''Check the relative path.
125 path may contain a pattern (e.g. foodir/**.txt)'''
125 path may contain a pattern (e.g. foodir/**.txt)'''
126
126
127 path = util.localpath(path)
127 path = util.localpath(path)
128 normpath = self.normcase(path)
128 normpath = self.normcase(path)
129 if normpath in self.audited:
129 if normpath in self.audited:
130 return
130 return
131 # AIX ignores "/" at end of path, others raise EISDIR.
131 # AIX ignores "/" at end of path, others raise EISDIR.
132 if util.endswithsep(path):
132 if util.endswithsep(path):
133 raise util.Abort(_("path ends in directory separator: %s") % path)
133 raise util.Abort(_("path ends in directory separator: %s") % path)
134 parts = util.splitpath(path)
134 parts = util.splitpath(path)
135 if (os.path.splitdrive(path)[0]
135 if (os.path.splitdrive(path)[0]
136 or parts[0].lower() in ('.hg', '.hg.', '')
136 or parts[0].lower() in ('.hg', '.hg.', '')
137 or os.pardir in parts):
137 or os.pardir in parts):
138 raise util.Abort(_("path contains illegal component: %s") % path)
138 raise util.Abort(_("path contains illegal component: %s") % path)
139 if '.hg' in path.lower():
139 if '.hg' in path.lower():
140 lparts = [p.lower() for p in parts]
140 lparts = [p.lower() for p in parts]
141 for p in '.hg', '.hg.':
141 for p in '.hg', '.hg.':
142 if p in lparts[1:]:
142 if p in lparts[1:]:
143 pos = lparts.index(p)
143 pos = lparts.index(p)
144 base = os.path.join(*parts[:pos])
144 base = os.path.join(*parts[:pos])
145 raise util.Abort(_("path '%s' is inside nested repo %r")
145 raise util.Abort(_("path '%s' is inside nested repo %r")
146 % (path, base))
146 % (path, base))
147
147
148 normparts = util.splitpath(normpath)
148 normparts = util.splitpath(normpath)
149 assert len(parts) == len(normparts)
149 assert len(parts) == len(normparts)
150
150
151 parts.pop()
151 parts.pop()
152 normparts.pop()
152 normparts.pop()
153 prefixes = []
153 prefixes = []
154 while parts:
154 while parts:
155 prefix = os.sep.join(parts)
155 prefix = os.sep.join(parts)
156 normprefix = os.sep.join(normparts)
156 normprefix = os.sep.join(normparts)
157 if normprefix in self.auditeddir:
157 if normprefix in self.auditeddir:
158 break
158 break
159 curpath = os.path.join(self.root, prefix)
159 curpath = os.path.join(self.root, prefix)
160 try:
160 try:
161 st = os.lstat(curpath)
161 st = os.lstat(curpath)
162 except OSError, err:
162 except OSError, err:
163 # EINVAL can be raised as invalid path syntax under win32.
163 # EINVAL can be raised as invalid path syntax under win32.
164 # They must be ignored for patterns can be checked too.
164 # They must be ignored for patterns can be checked too.
165 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
165 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
166 raise
166 raise
167 else:
167 else:
168 if stat.S_ISLNK(st.st_mode):
168 if stat.S_ISLNK(st.st_mode):
169 raise util.Abort(
169 raise util.Abort(
170 _('path %r traverses symbolic link %r')
170 _('path %r traverses symbolic link %r')
171 % (path, prefix))
171 % (path, prefix))
172 elif (stat.S_ISDIR(st.st_mode) and
172 elif (stat.S_ISDIR(st.st_mode) and
173 os.path.isdir(os.path.join(curpath, '.hg'))):
173 os.path.isdir(os.path.join(curpath, '.hg'))):
174 if not self.callback or not self.callback(curpath):
174 if not self.callback or not self.callback(curpath):
175 raise util.Abort(_("path '%s' is inside nested "
175 raise util.Abort(_("path '%s' is inside nested "
176 "repo %r")
176 "repo %r")
177 % (path, prefix))
177 % (path, prefix))
178 prefixes.append(normprefix)
178 prefixes.append(normprefix)
179 parts.pop()
179 parts.pop()
180 normparts.pop()
180 normparts.pop()
181
181
182 self.audited.add(normpath)
182 self.audited.add(normpath)
183 # only add prefixes to the cache after checking everything: we don't
183 # only add prefixes to the cache after checking everything: we don't
184 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
184 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
185 self.auditeddir.update(prefixes)
185 self.auditeddir.update(prefixes)
186
186
187 def check(self, path):
187 def check(self, path):
188 try:
188 try:
189 self(path)
189 self(path)
190 return True
190 return True
191 except (OSError, util.Abort):
191 except (OSError, util.Abort):
192 return False
192 return False
193
193
194 class abstractvfs(object):
194 class abstractvfs(object):
195 """Abstract base class; cannot be instantiated"""
195 """Abstract base class; cannot be instantiated"""
196
196
197 def __init__(self, *args, **kwargs):
197 def __init__(self, *args, **kwargs):
198 '''Prevent instantiation; don't call this from subclasses.'''
198 '''Prevent instantiation; don't call this from subclasses.'''
199 raise NotImplementedError('attempted instantiating ' + str(type(self)))
199 raise NotImplementedError('attempted instantiating ' + str(type(self)))
200
200
201 def tryread(self, path):
201 def tryread(self, path):
202 '''gracefully return an empty string for missing files'''
202 '''gracefully return an empty string for missing files'''
203 try:
203 try:
204 return self.read(path)
204 return self.read(path)
205 except IOError, inst:
205 except IOError, inst:
206 if inst.errno != errno.ENOENT:
206 if inst.errno != errno.ENOENT:
207 raise
207 raise
208 return ""
208 return ""
209
209
210 def read(self, path):
210 def read(self, path):
211 fp = self(path, 'rb')
211 fp = self(path, 'rb')
212 try:
212 try:
213 return fp.read()
213 return fp.read()
214 finally:
214 finally:
215 fp.close()
215 fp.close()
216
216
217 def write(self, path, data):
217 def write(self, path, data):
218 fp = self(path, 'wb')
218 fp = self(path, 'wb')
219 try:
219 try:
220 return fp.write(data)
220 return fp.write(data)
221 finally:
221 finally:
222 fp.close()
222 fp.close()
223
223
224 def append(self, path, data):
224 def append(self, path, data):
225 fp = self(path, 'ab')
225 fp = self(path, 'ab')
226 try:
226 try:
227 return fp.write(data)
227 return fp.write(data)
228 finally:
228 finally:
229 fp.close()
229 fp.close()
230
230
231 def exists(self, path=None):
231 def exists(self, path=None):
232 return os.path.exists(self.join(path))
232 return os.path.exists(self.join(path))
233
233
234 def isdir(self, path=None):
234 def isdir(self, path=None):
235 return os.path.isdir(self.join(path))
235 return os.path.isdir(self.join(path))
236
236
237 def makedir(self, path=None, notindexed=True):
237 def makedir(self, path=None, notindexed=True):
238 return util.makedir(self.join(path), notindexed)
238 return util.makedir(self.join(path), notindexed)
239
239
240 def makedirs(self, path=None, mode=None):
240 def makedirs(self, path=None, mode=None):
241 return util.makedirs(self.join(path), mode)
241 return util.makedirs(self.join(path), mode)
242
242
243 def mkdir(self, path=None):
243 def mkdir(self, path=None):
244 return os.mkdir(self.join(path))
244 return os.mkdir(self.join(path))
245
245
246 def readdir(self, path=None, stat=None, skip=None):
246 def readdir(self, path=None, stat=None, skip=None):
247 return osutil.listdir(self.join(path), stat, skip)
247 return osutil.listdir(self.join(path), stat, skip)
248
248
249 def stat(self, path=None):
249 def stat(self, path=None):
250 return os.stat(self.join(path))
250 return os.stat(self.join(path))
251
251
252 class vfs(abstractvfs):
252 class vfs(abstractvfs):
253 '''Operate files relative to a base directory
253 '''Operate files relative to a base directory
254
254
255 This class is used to hide the details of COW semantics and
255 This class is used to hide the details of COW semantics and
256 remote file access from higher level code.
256 remote file access from higher level code.
257 '''
257 '''
258 def __init__(self, base, audit=True, expand=False):
258 def __init__(self, base, audit=True, expand=False):
259 if expand:
259 if expand:
260 base = os.path.realpath(util.expandpath(base))
260 base = os.path.realpath(util.expandpath(base))
261 self.base = base
261 self.base = base
262 self._setmustaudit(audit)
262 self._setmustaudit(audit)
263 self.createmode = None
263 self.createmode = None
264 self._trustnlink = None
264 self._trustnlink = None
265
265
266 def _getmustaudit(self):
266 def _getmustaudit(self):
267 return self._audit
267 return self._audit
268
268
269 def _setmustaudit(self, onoff):
269 def _setmustaudit(self, onoff):
270 self._audit = onoff
270 self._audit = onoff
271 if onoff:
271 if onoff:
272 self.audit = pathauditor(self.base)
272 self.audit = pathauditor(self.base)
273 else:
273 else:
274 self.audit = util.always
274 self.audit = util.always
275
275
276 mustaudit = property(_getmustaudit, _setmustaudit)
276 mustaudit = property(_getmustaudit, _setmustaudit)
277
277
278 @util.propertycache
278 @util.propertycache
279 def _cansymlink(self):
279 def _cansymlink(self):
280 return util.checklink(self.base)
280 return util.checklink(self.base)
281
281
282 @util.propertycache
282 @util.propertycache
283 def _chmod(self):
283 def _chmod(self):
284 return util.checkexec(self.base)
284 return util.checkexec(self.base)
285
285
286 def _fixfilemode(self, name):
286 def _fixfilemode(self, name):
287 if self.createmode is None or not self._chmod:
287 if self.createmode is None or not self._chmod:
288 return
288 return
289 os.chmod(name, self.createmode & 0666)
289 os.chmod(name, self.createmode & 0666)
290
290
291 def __call__(self, path, mode="r", text=False, atomictemp=False):
291 def __call__(self, path, mode="r", text=False, atomictemp=False):
292 if self._audit:
292 if self._audit:
293 r = util.checkosfilename(path)
293 r = util.checkosfilename(path)
294 if r:
294 if r:
295 raise util.Abort("%s: %r" % (r, path))
295 raise util.Abort("%s: %r" % (r, path))
296 self.audit(path)
296 self.audit(path)
297 f = self.join(path)
297 f = self.join(path)
298
298
299 if not text and "b" not in mode:
299 if not text and "b" not in mode:
300 mode += "b" # for that other OS
300 mode += "b" # for that other OS
301
301
302 nlink = -1
302 nlink = -1
303 if mode not in ('r', 'rb'):
303 if mode not in ('r', 'rb'):
304 dirname, basename = util.split(f)
304 dirname, basename = util.split(f)
305 # If basename is empty, then the path is malformed because it points
305 # If basename is empty, then the path is malformed because it points
306 # to a directory. Let the posixfile() call below raise IOError.
306 # to a directory. Let the posixfile() call below raise IOError.
307 if basename:
307 if basename:
308 if atomictemp:
308 if atomictemp:
309 if not os.path.isdir(dirname):
309 if not os.path.isdir(dirname):
310 util.makedirs(dirname, self.createmode)
310 util.makedirs(dirname, self.createmode)
311 return util.atomictempfile(f, mode, self.createmode)
311 return util.atomictempfile(f, mode, self.createmode)
312 try:
312 try:
313 if 'w' in mode:
313 if 'w' in mode:
314 util.unlink(f)
314 util.unlink(f)
315 nlink = 0
315 nlink = 0
316 else:
316 else:
317 # nlinks() may behave differently for files on Windows
317 # nlinks() may behave differently for files on Windows
318 # shares if the file is open.
318 # shares if the file is open.
319 fd = util.posixfile(f)
319 fd = util.posixfile(f)
320 nlink = util.nlinks(f)
320 nlink = util.nlinks(f)
321 if nlink < 1:
321 if nlink < 1:
322 nlink = 2 # force mktempcopy (issue1922)
322 nlink = 2 # force mktempcopy (issue1922)
323 fd.close()
323 fd.close()
324 except (OSError, IOError), e:
324 except (OSError, IOError), e:
325 if e.errno != errno.ENOENT:
325 if e.errno != errno.ENOENT:
326 raise
326 raise
327 nlink = 0
327 nlink = 0
328 if not os.path.isdir(dirname):
328 if not os.path.isdir(dirname):
329 util.makedirs(dirname, self.createmode)
329 util.makedirs(dirname, self.createmode)
330 if nlink > 0:
330 if nlink > 0:
331 if self._trustnlink is None:
331 if self._trustnlink is None:
332 self._trustnlink = nlink > 1 or util.checknlink(f)
332 self._trustnlink = nlink > 1 or util.checknlink(f)
333 if nlink > 1 or not self._trustnlink:
333 if nlink > 1 or not self._trustnlink:
334 util.rename(util.mktempcopy(f), f)
334 util.rename(util.mktempcopy(f), f)
335 fp = util.posixfile(f, mode)
335 fp = util.posixfile(f, mode)
336 if nlink == 0:
336 if nlink == 0:
337 self._fixfilemode(f)
337 self._fixfilemode(f)
338 return fp
338 return fp
339
339
340 def symlink(self, src, dst):
340 def symlink(self, src, dst):
341 self.audit(dst)
341 self.audit(dst)
342 linkname = self.join(dst)
342 linkname = self.join(dst)
343 try:
343 try:
344 os.unlink(linkname)
344 os.unlink(linkname)
345 except OSError:
345 except OSError:
346 pass
346 pass
347
347
348 dirname = os.path.dirname(linkname)
348 dirname = os.path.dirname(linkname)
349 if not os.path.exists(dirname):
349 if not os.path.exists(dirname):
350 util.makedirs(dirname, self.createmode)
350 util.makedirs(dirname, self.createmode)
351
351
352 if self._cansymlink:
352 if self._cansymlink:
353 try:
353 try:
354 os.symlink(src, linkname)
354 os.symlink(src, linkname)
355 except OSError, err:
355 except OSError, err:
356 raise OSError(err.errno, _('could not symlink to %r: %s') %
356 raise OSError(err.errno, _('could not symlink to %r: %s') %
357 (src, err.strerror), linkname)
357 (src, err.strerror), linkname)
358 else:
358 else:
359 self.write(dst, src)
359 self.write(dst, src)
360
360
361 def join(self, path):
361 def join(self, path):
362 if path:
362 if path:
363 return os.path.join(self.base, path)
363 return os.path.join(self.base, path)
364 else:
364 else:
365 return self.base
365 return self.base
366
366
367 opener = vfs
367 opener = vfs
368
368
369 class auditvfs(object):
369 class auditvfs(object):
370 def __init__(self, vfs):
370 def __init__(self, vfs):
371 self.vfs = vfs
371 self.vfs = vfs
372
372
373 def _getmustaudit(self):
373 def _getmustaudit(self):
374 return self.vfs.mustaudit
374 return self.vfs.mustaudit
375
375
376 def _setmustaudit(self, onoff):
376 def _setmustaudit(self, onoff):
377 self.vfs.mustaudit = onoff
377 self.vfs.mustaudit = onoff
378
378
379 mustaudit = property(_getmustaudit, _setmustaudit)
379 mustaudit = property(_getmustaudit, _setmustaudit)
380
380
381 class filtervfs(abstractvfs, auditvfs):
381 class filtervfs(abstractvfs, auditvfs):
382 '''Wrapper vfs for filtering filenames with a function.'''
382 '''Wrapper vfs for filtering filenames with a function.'''
383
383
384 def __init__(self, vfs, filter):
384 def __init__(self, vfs, filter):
385 auditvfs.__init__(self, vfs)
385 auditvfs.__init__(self, vfs)
386 self._filter = filter
386 self._filter = filter
387
387
388 def __call__(self, path, *args, **kwargs):
388 def __call__(self, path, *args, **kwargs):
389 return self.vfs(self._filter(path), *args, **kwargs)
389 return self.vfs(self._filter(path), *args, **kwargs)
390
390
391 def join(self, path):
391 def join(self, path):
392 if path:
392 if path:
393 return self.vfs.join(self._filter(path))
393 return self.vfs.join(self._filter(path))
394 else:
394 else:
395 return self.vfs.join(path)
395 return self.vfs.join(path)
396
396
397 filteropener = filtervfs
397 filteropener = filtervfs
398
398
399 class readonlyvfs(abstractvfs, auditvfs):
399 class readonlyvfs(abstractvfs, auditvfs):
400 '''Wrapper vfs preventing any writing.'''
400 '''Wrapper vfs preventing any writing.'''
401
401
402 def __init__(self, vfs):
402 def __init__(self, vfs):
403 auditvfs.__init__(self, vfs)
403 auditvfs.__init__(self, vfs)
404
404
405 def __call__(self, path, mode='r', *args, **kw):
405 def __call__(self, path, mode='r', *args, **kw):
406 if mode not in ('r', 'rb'):
406 if mode not in ('r', 'rb'):
407 raise util.Abort('this vfs is read only')
407 raise util.Abort('this vfs is read only')
408 return self.vfs(path, mode, *args, **kw)
408 return self.vfs(path, mode, *args, **kw)
409
409
410
410
411 def canonpath(root, cwd, myname, auditor=None):
411 def canonpath(root, cwd, myname, auditor=None):
412 '''return the canonical path of myname, given cwd and root'''
412 '''return the canonical path of myname, given cwd and root'''
413 if util.endswithsep(root):
413 if util.endswithsep(root):
414 rootsep = root
414 rootsep = root
415 else:
415 else:
416 rootsep = root + os.sep
416 rootsep = root + os.sep
417 name = myname
417 name = myname
418 if not os.path.isabs(name):
418 if not os.path.isabs(name):
419 name = os.path.join(root, cwd, name)
419 name = os.path.join(root, cwd, name)
420 name = os.path.normpath(name)
420 name = os.path.normpath(name)
421 if auditor is None:
421 if auditor is None:
422 auditor = pathauditor(root)
422 auditor = pathauditor(root)
423 if name != rootsep and name.startswith(rootsep):
423 if name != rootsep and name.startswith(rootsep):
424 name = name[len(rootsep):]
424 name = name[len(rootsep):]
425 auditor(name)
425 auditor(name)
426 return util.pconvert(name)
426 return util.pconvert(name)
427 elif name == root:
427 elif name == root:
428 return ''
428 return ''
429 else:
429 else:
430 # Determine whether `name' is in the hierarchy at or beneath `root',
430 # Determine whether `name' is in the hierarchy at or beneath `root',
431 # by iterating name=dirname(name) until that causes no change (can't
431 # by iterating name=dirname(name) until that causes no change (can't
432 # check name == '/', because that doesn't work on windows). The list
432 # check name == '/', because that doesn't work on windows). The list
433 # `rel' holds the reversed list of components making up the relative
433 # `rel' holds the reversed list of components making up the relative
434 # file name we want.
434 # file name we want.
435 rel = []
435 rel = []
436 while True:
436 while True:
437 try:
437 try:
438 s = util.samefile(name, root)
438 s = util.samefile(name, root)
439 except OSError:
439 except OSError:
440 s = False
440 s = False
441 if s:
441 if s:
442 if not rel:
442 if not rel:
443 # name was actually the same as root (maybe a symlink)
443 # name was actually the same as root (maybe a symlink)
444 return ''
444 return ''
445 rel.reverse()
445 rel.reverse()
446 name = os.path.join(*rel)
446 name = os.path.join(*rel)
447 auditor(name)
447 auditor(name)
448 return util.pconvert(name)
448 return util.pconvert(name)
449 dirname, basename = util.split(name)
449 dirname, basename = util.split(name)
450 rel.append(basename)
450 rel.append(basename)
451 if dirname == name:
451 if dirname == name:
452 break
452 break
453 name = dirname
453 name = dirname
454
454
455 raise util.Abort(_("%s not under root '%s'") % (myname, root))
455 raise util.Abort(_("%s not under root '%s'") % (myname, root))
456
456
457 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
457 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
458 '''yield every hg repository under path, always recursively.
458 '''yield every hg repository under path, always recursively.
459 The recurse flag will only control recursion into repo working dirs'''
459 The recurse flag will only control recursion into repo working dirs'''
460 def errhandler(err):
460 def errhandler(err):
461 if err.filename == path:
461 if err.filename == path:
462 raise err
462 raise err
463 samestat = getattr(os.path, 'samestat', None)
463 samestat = getattr(os.path, 'samestat', None)
464 if followsym and samestat is not None:
464 if followsym and samestat is not None:
465 def adddir(dirlst, dirname):
465 def adddir(dirlst, dirname):
466 match = False
466 match = False
467 dirstat = os.stat(dirname)
467 dirstat = os.stat(dirname)
468 for lstdirstat in dirlst:
468 for lstdirstat in dirlst:
469 if samestat(dirstat, lstdirstat):
469 if samestat(dirstat, lstdirstat):
470 match = True
470 match = True
471 break
471 break
472 if not match:
472 if not match:
473 dirlst.append(dirstat)
473 dirlst.append(dirstat)
474 return not match
474 return not match
475 else:
475 else:
476 followsym = False
476 followsym = False
477
477
478 if (seen_dirs is None) and followsym:
478 if (seen_dirs is None) and followsym:
479 seen_dirs = []
479 seen_dirs = []
480 adddir(seen_dirs, path)
480 adddir(seen_dirs, path)
481 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
481 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
482 dirs.sort()
482 dirs.sort()
483 if '.hg' in dirs:
483 if '.hg' in dirs:
484 yield root # found a repository
484 yield root # found a repository
485 qroot = os.path.join(root, '.hg', 'patches')
485 qroot = os.path.join(root, '.hg', 'patches')
486 if os.path.isdir(os.path.join(qroot, '.hg')):
486 if os.path.isdir(os.path.join(qroot, '.hg')):
487 yield qroot # we have a patch queue repo here
487 yield qroot # we have a patch queue repo here
488 if recurse:
488 if recurse:
489 # avoid recursing inside the .hg directory
489 # avoid recursing inside the .hg directory
490 dirs.remove('.hg')
490 dirs.remove('.hg')
491 else:
491 else:
492 dirs[:] = [] # don't descend further
492 dirs[:] = [] # don't descend further
493 elif followsym:
493 elif followsym:
494 newdirs = []
494 newdirs = []
495 for d in dirs:
495 for d in dirs:
496 fname = os.path.join(root, d)
496 fname = os.path.join(root, d)
497 if adddir(seen_dirs, fname):
497 if adddir(seen_dirs, fname):
498 if os.path.islink(fname):
498 if os.path.islink(fname):
499 for hgname in walkrepos(fname, True, seen_dirs):
499 for hgname in walkrepos(fname, True, seen_dirs):
500 yield hgname
500 yield hgname
501 else:
501 else:
502 newdirs.append(d)
502 newdirs.append(d)
503 dirs[:] = newdirs
503 dirs[:] = newdirs
504
504
505 def osrcpath():
505 def osrcpath():
506 '''return default os-specific hgrc search path'''
506 '''return default os-specific hgrc search path'''
507 path = systemrcpath()
507 path = systemrcpath()
508 path.extend(userrcpath())
508 path.extend(userrcpath())
509 path = [os.path.normpath(f) for f in path]
509 path = [os.path.normpath(f) for f in path]
510 return path
510 return path
511
511
512 _rcpath = None
512 _rcpath = None
513
513
514 def rcpath():
514 def rcpath():
515 '''return hgrc search path. if env var HGRCPATH is set, use it.
515 '''return hgrc search path. if env var HGRCPATH is set, use it.
516 for each item in path, if directory, use files ending in .rc,
516 for each item in path, if directory, use files ending in .rc,
517 else use item.
517 else use item.
518 make HGRCPATH empty to only look in .hg/hgrc of current repo.
518 make HGRCPATH empty to only look in .hg/hgrc of current repo.
519 if no HGRCPATH, use default os-specific path.'''
519 if no HGRCPATH, use default os-specific path.'''
520 global _rcpath
520 global _rcpath
521 if _rcpath is None:
521 if _rcpath is None:
522 if 'HGRCPATH' in os.environ:
522 if 'HGRCPATH' in os.environ:
523 _rcpath = []
523 _rcpath = []
524 for p in os.environ['HGRCPATH'].split(os.pathsep):
524 for p in os.environ['HGRCPATH'].split(os.pathsep):
525 if not p:
525 if not p:
526 continue
526 continue
527 p = util.expandpath(p)
527 p = util.expandpath(p)
528 if os.path.isdir(p):
528 if os.path.isdir(p):
529 for f, kind in osutil.listdir(p):
529 for f, kind in osutil.listdir(p):
530 if f.endswith('.rc'):
530 if f.endswith('.rc'):
531 _rcpath.append(os.path.join(p, f))
531 _rcpath.append(os.path.join(p, f))
532 else:
532 else:
533 _rcpath.append(p)
533 _rcpath.append(p)
534 else:
534 else:
535 _rcpath = osrcpath()
535 _rcpath = osrcpath()
536 return _rcpath
536 return _rcpath
537
537
538 if os.name != 'nt':
538 if os.name != 'nt':
539
539
540 def rcfiles(path):
540 def rcfiles(path):
541 rcs = [os.path.join(path, 'hgrc')]
541 rcs = [os.path.join(path, 'hgrc')]
542 rcdir = os.path.join(path, 'hgrc.d')
542 rcdir = os.path.join(path, 'hgrc.d')
543 try:
543 try:
544 rcs.extend([os.path.join(rcdir, f)
544 rcs.extend([os.path.join(rcdir, f)
545 for f, kind in osutil.listdir(rcdir)
545 for f, kind in osutil.listdir(rcdir)
546 if f.endswith(".rc")])
546 if f.endswith(".rc")])
547 except OSError:
547 except OSError:
548 pass
548 pass
549 return rcs
549 return rcs
550
550
551 def systemrcpath():
551 def systemrcpath():
552 path = []
552 path = []
553 if sys.platform == 'plan9':
553 if sys.platform == 'plan9':
554 root = 'lib/mercurial'
554 root = 'lib/mercurial'
555 else:
555 else:
556 root = 'etc/mercurial'
556 root = 'etc/mercurial'
557 # old mod_python does not set sys.argv
557 # old mod_python does not set sys.argv
558 if len(getattr(sys, 'argv', [])) > 0:
558 if len(getattr(sys, 'argv', [])) > 0:
559 p = os.path.dirname(os.path.dirname(sys.argv[0]))
559 p = os.path.dirname(os.path.dirname(sys.argv[0]))
560 path.extend(rcfiles(os.path.join(p, root)))
560 path.extend(rcfiles(os.path.join(p, root)))
561 path.extend(rcfiles('/' + root))
561 path.extend(rcfiles('/' + root))
562 return path
562 return path
563
563
564 def userrcpath():
564 def userrcpath():
565 if sys.platform == 'plan9':
565 if sys.platform == 'plan9':
566 return [os.environ['home'] + '/lib/hgrc']
566 return [os.environ['home'] + '/lib/hgrc']
567 else:
567 else:
568 return [os.path.expanduser('~/.hgrc')]
568 return [os.path.expanduser('~/.hgrc')]
569
569
570 else:
570 else:
571
571
572 import _winreg
572 import _winreg
573
573
574 def systemrcpath():
574 def systemrcpath():
575 '''return default os-specific hgrc search path'''
575 '''return default os-specific hgrc search path'''
576 rcpath = []
576 rcpath = []
577 filename = util.executablepath()
577 filename = util.executablepath()
578 # Use mercurial.ini found in directory with hg.exe
578 # Use mercurial.ini found in directory with hg.exe
579 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
579 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
580 if os.path.isfile(progrc):
580 if os.path.isfile(progrc):
581 rcpath.append(progrc)
581 rcpath.append(progrc)
582 return rcpath
582 return rcpath
583 # Use hgrc.d found in directory with hg.exe
583 # Use hgrc.d found in directory with hg.exe
584 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
584 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
585 if os.path.isdir(progrcd):
585 if os.path.isdir(progrcd):
586 for f, kind in osutil.listdir(progrcd):
586 for f, kind in osutil.listdir(progrcd):
587 if f.endswith('.rc'):
587 if f.endswith('.rc'):
588 rcpath.append(os.path.join(progrcd, f))
588 rcpath.append(os.path.join(progrcd, f))
589 return rcpath
589 return rcpath
590 # else look for a system rcpath in the registry
590 # else look for a system rcpath in the registry
591 value = util.lookupreg('SOFTWARE\\Mercurial', None,
591 value = util.lookupreg('SOFTWARE\\Mercurial', None,
592 _winreg.HKEY_LOCAL_MACHINE)
592 _winreg.HKEY_LOCAL_MACHINE)
593 if not isinstance(value, str) or not value:
593 if not isinstance(value, str) or not value:
594 return rcpath
594 return rcpath
595 value = util.localpath(value)
595 value = util.localpath(value)
596 for p in value.split(os.pathsep):
596 for p in value.split(os.pathsep):
597 if p.lower().endswith('mercurial.ini'):
597 if p.lower().endswith('mercurial.ini'):
598 rcpath.append(p)
598 rcpath.append(p)
599 elif os.path.isdir(p):
599 elif os.path.isdir(p):
600 for f, kind in osutil.listdir(p):
600 for f, kind in osutil.listdir(p):
601 if f.endswith('.rc'):
601 if f.endswith('.rc'):
602 rcpath.append(os.path.join(p, f))
602 rcpath.append(os.path.join(p, f))
603 return rcpath
603 return rcpath
604
604
605 def userrcpath():
605 def userrcpath():
606 '''return os-specific hgrc search path to the user dir'''
606 '''return os-specific hgrc search path to the user dir'''
607 home = os.path.expanduser('~')
607 home = os.path.expanduser('~')
608 path = [os.path.join(home, 'mercurial.ini'),
608 path = [os.path.join(home, 'mercurial.ini'),
609 os.path.join(home, '.hgrc')]
609 os.path.join(home, '.hgrc')]
610 userprofile = os.environ.get('USERPROFILE')
610 userprofile = os.environ.get('USERPROFILE')
611 if userprofile:
611 if userprofile:
612 path.append(os.path.join(userprofile, 'mercurial.ini'))
612 path.append(os.path.join(userprofile, 'mercurial.ini'))
613 path.append(os.path.join(userprofile, '.hgrc'))
613 path.append(os.path.join(userprofile, '.hgrc'))
614 return path
614 return path
615
615
616 def revsingle(repo, revspec, default='.'):
616 def revsingle(repo, revspec, default='.'):
617 if not revspec:
617 if not revspec:
618 return repo[default]
618 return repo[default]
619
619
620 l = revrange(repo, [revspec])
620 l = revrange(repo, [revspec])
621 if len(l) < 1:
621 if len(l) < 1:
622 raise util.Abort(_('empty revision set'))
622 raise util.Abort(_('empty revision set'))
623 return repo[l[-1]]
623 return repo[l[-1]]
624
624
625 def revpair(repo, revs):
625 def revpair(repo, revs):
626 if not revs:
626 if not revs:
627 return repo.dirstate.p1(), None
627 return repo.dirstate.p1(), None
628
628
629 l = revrange(repo, revs)
629 l = revrange(repo, revs)
630
630
631 if len(l) == 0:
631 if len(l) == 0:
632 if revs:
632 if revs:
633 raise util.Abort(_('empty revision range'))
633 raise util.Abort(_('empty revision range'))
634 return repo.dirstate.p1(), None
634 return repo.dirstate.p1(), None
635
635
636 if len(l) == 1 and len(revs) == 1 and _revrangesep not in revs[0]:
636 if len(l) == 1 and len(revs) == 1 and _revrangesep not in revs[0]:
637 return repo.lookup(l[0]), None
637 return repo.lookup(l[0]), None
638
638
639 return repo.lookup(l[0]), repo.lookup(l[-1])
639 return repo.lookup(l[0]), repo.lookup(l[-1])
640
640
641 _revrangesep = ':'
641 _revrangesep = ':'
642
642
643 def revrange(repo, revs):
643 def revrange(repo, revs):
644 """Yield revision as strings from a list of revision specifications."""
644 """Yield revision as strings from a list of revision specifications."""
645
645
646 def revfix(repo, val, defval):
646 def revfix(repo, val, defval):
647 if not val and val != 0 and defval is not None:
647 if not val and val != 0 and defval is not None:
648 return defval
648 return defval
649 return repo[val].rev()
649 return repo[val].rev()
650
650
651 seen, l = set(), []
651 seen, l = set(), []
652 for spec in revs:
652 for spec in revs:
653 if l and not seen:
653 if l and not seen:
654 seen = set(l)
654 seen = set(l)
655 # attempt to parse old-style ranges first to deal with
655 # attempt to parse old-style ranges first to deal with
656 # things like old-tag which contain query metacharacters
656 # things like old-tag which contain query metacharacters
657 try:
657 try:
658 if isinstance(spec, int):
658 if isinstance(spec, int):
659 seen.add(spec)
659 seen.add(spec)
660 l.append(spec)
660 l.append(spec)
661 continue
661 continue
662
662
663 if _revrangesep in spec:
663 if _revrangesep in spec:
664 start, end = spec.split(_revrangesep, 1)
664 start, end = spec.split(_revrangesep, 1)
665 start = revfix(repo, start, 0)
665 start = revfix(repo, start, 0)
666 end = revfix(repo, end, len(repo) - 1)
666 end = revfix(repo, end, len(repo) - 1)
667 if end == nullrev and start <= 0:
667 if end == nullrev and start <= 0:
668 start = nullrev
668 start = nullrev
669 rangeiter = repo.changelog.revs(start, end)
669 rangeiter = repo.changelog.revs(start, end)
670 if not seen and not l:
670 if not seen and not l:
671 # by far the most common case: revs = ["-1:0"]
671 # by far the most common case: revs = ["-1:0"]
672 l = list(rangeiter)
672 l = list(rangeiter)
673 # defer syncing seen until next iteration
673 # defer syncing seen until next iteration
674 continue
674 continue
675 newrevs = set(rangeiter)
675 newrevs = set(rangeiter)
676 if seen:
676 if seen:
677 newrevs.difference_update(seen)
677 newrevs.difference_update(seen)
678 seen.update(newrevs)
678 seen.update(newrevs)
679 else:
679 else:
680 seen = newrevs
680 seen = newrevs
681 l.extend(sorted(newrevs, reverse=start > end))
681 l.extend(sorted(newrevs, reverse=start > end))
682 continue
682 continue
683 elif spec and spec in repo: # single unquoted rev
683 elif spec and spec in repo: # single unquoted rev
684 rev = revfix(repo, spec, None)
684 rev = revfix(repo, spec, None)
685 if rev in seen:
685 if rev in seen:
686 continue
686 continue
687 seen.add(rev)
687 seen.add(rev)
688 l.append(rev)
688 l.append(rev)
689 continue
689 continue
690 except error.RepoLookupError:
690 except error.RepoLookupError:
691 pass
691 pass
692
692
693 # fall through to new-style queries if old-style fails
693 # fall through to new-style queries if old-style fails
694 m = revset.match(repo.ui, spec)
694 m = revset.match(repo.ui, spec)
695 dl = [r for r in m(repo, list(repo)) if r not in seen]
695 dl = [r for r in m(repo, list(repo)) if r not in seen]
696 l.extend(dl)
696 l.extend(dl)
697 seen.update(dl)
697 seen.update(dl)
698
698
699 return l
699 return l
700
700
701 def expandpats(pats):
701 def expandpats(pats):
702 if not util.expandglobs:
702 if not util.expandglobs:
703 return list(pats)
703 return list(pats)
704 ret = []
704 ret = []
705 for p in pats:
705 for p in pats:
706 kind, name = matchmod._patsplit(p, None)
706 kind, name = matchmod._patsplit(p, None)
707 if kind is None:
707 if kind is None:
708 try:
708 try:
709 globbed = glob.glob(name)
709 globbed = glob.glob(name)
710 except re.error:
710 except re.error:
711 globbed = [name]
711 globbed = [name]
712 if globbed:
712 if globbed:
713 ret.extend(globbed)
713 ret.extend(globbed)
714 continue
714 continue
715 ret.append(p)
715 ret.append(p)
716 return ret
716 return ret
717
717
718 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath'):
718 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath'):
719 if pats == ("",):
719 if pats == ("",):
720 pats = []
720 pats = []
721 if not globbed and default == 'relpath':
721 if not globbed and default == 'relpath':
722 pats = expandpats(pats or [])
722 pats = expandpats(pats or [])
723
723
724 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
724 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
725 default)
725 default)
726 def badfn(f, msg):
726 def badfn(f, msg):
727 ctx._repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
727 ctx._repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
728 m.bad = badfn
728 m.bad = badfn
729 return m, pats
729 return m, pats
730
730
731 def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
731 def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
732 return matchandpats(ctx, pats, opts, globbed, default)[0]
732 return matchandpats(ctx, pats, opts, globbed, default)[0]
733
733
734 def matchall(repo):
734 def matchall(repo):
735 return matchmod.always(repo.root, repo.getcwd())
735 return matchmod.always(repo.root, repo.getcwd())
736
736
737 def matchfiles(repo, files):
737 def matchfiles(repo, files):
738 return matchmod.exact(repo.root, repo.getcwd(), files)
738 return matchmod.exact(repo.root, repo.getcwd(), files)
739
739
740 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
740 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
741 if dry_run is None:
741 if dry_run is None:
742 dry_run = opts.get('dry_run')
742 dry_run = opts.get('dry_run')
743 if similarity is None:
743 if similarity is None:
744 similarity = float(opts.get('similarity') or 0)
744 similarity = float(opts.get('similarity') or 0)
745 # we'd use status here, except handling of symlinks and ignore is tricky
745 # we'd use status here, except handling of symlinks and ignore is tricky
746 added, unknown, deleted, removed = [], [], [], []
746 added, unknown, deleted, removed = [], [], [], []
747 audit_path = pathauditor(repo.root)
747 audit_path = pathauditor(repo.root)
748 m = match(repo[None], pats, opts)
748 m = match(repo[None], pats, opts)
749 rejected = []
749 rejected = []
750 m.bad = lambda x, y: rejected.append(x)
750 m.bad = lambda x, y: rejected.append(x)
751
751
752 ctx = repo[None]
752 ctx = repo[None]
753 walkresults = repo.dirstate.walk(m, sorted(ctx.substate), True, False)
753 walkresults = repo.dirstate.walk(m, sorted(ctx.substate), True, False)
754 for abs in sorted(walkresults):
754 for abs in sorted(walkresults):
755 good = audit_path.check(abs)
756
757 st = walkresults[abs]
755 st = walkresults[abs]
758 dstate = repo.dirstate[abs]
756 dstate = repo.dirstate[abs]
759 if good and dstate == '?':
757 if dstate == '?' and audit_path.check(abs):
760 unknown.append(abs)
758 unknown.append(abs)
761 if repo.ui.verbose or not m.exact(abs):
759 if repo.ui.verbose or not m.exact(abs):
762 rel = m.rel(abs)
760 rel = m.rel(abs)
763 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
761 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
764 elif (dstate != 'r' and
762 elif (dstate != 'r' and (not st or
765 (not good or not st or
766 (stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode)))):
763 (stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode)))):
767 deleted.append(abs)
764 deleted.append(abs)
768 if repo.ui.verbose or not m.exact(abs):
765 if repo.ui.verbose or not m.exact(abs):
769 rel = m.rel(abs)
766 rel = m.rel(abs)
770 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
767 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
771 # for finding renames
768 # for finding renames
772 elif dstate == 'r':
769 elif dstate == 'r':
773 removed.append(abs)
770 removed.append(abs)
774 elif dstate == 'a':
771 elif dstate == 'a':
775 added.append(abs)
772 added.append(abs)
776 copies = {}
773 copies = {}
777 if similarity > 0:
774 if similarity > 0:
778 for old, new, score in similar.findrenames(repo,
775 for old, new, score in similar.findrenames(repo,
779 added + unknown, removed + deleted, similarity):
776 added + unknown, removed + deleted, similarity):
780 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
777 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
781 repo.ui.status(_('recording removal of %s as rename to %s '
778 repo.ui.status(_('recording removal of %s as rename to %s '
782 '(%d%% similar)\n') %
779 '(%d%% similar)\n') %
783 (m.rel(old), m.rel(new), score * 100))
780 (m.rel(old), m.rel(new), score * 100))
784 copies[new] = old
781 copies[new] = old
785
782
786 if not dry_run:
783 if not dry_run:
787 wctx = repo[None]
784 wctx = repo[None]
788 wlock = repo.wlock()
785 wlock = repo.wlock()
789 try:
786 try:
790 wctx.forget(deleted)
787 wctx.forget(deleted)
791 wctx.add(unknown)
788 wctx.add(unknown)
792 for new, old in copies.iteritems():
789 for new, old in copies.iteritems():
793 wctx.copy(old, new)
790 wctx.copy(old, new)
794 finally:
791 finally:
795 wlock.release()
792 wlock.release()
796
793
797 for f in rejected:
794 for f in rejected:
798 if f in m.files():
795 if f in m.files():
799 return 1
796 return 1
800 return 0
797 return 0
801
798
802 def updatedir(ui, repo, patches, similarity=0):
799 def updatedir(ui, repo, patches, similarity=0):
803 '''Update dirstate after patch application according to metadata'''
800 '''Update dirstate after patch application according to metadata'''
804 if not patches:
801 if not patches:
805 return []
802 return []
806 copies = []
803 copies = []
807 removes = set()
804 removes = set()
808 cfiles = patches.keys()
805 cfiles = patches.keys()
809 cwd = repo.getcwd()
806 cwd = repo.getcwd()
810 if cwd:
807 if cwd:
811 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
808 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
812 for f in patches:
809 for f in patches:
813 gp = patches[f]
810 gp = patches[f]
814 if not gp:
811 if not gp:
815 continue
812 continue
816 if gp.op == 'RENAME':
813 if gp.op == 'RENAME':
817 copies.append((gp.oldpath, gp.path))
814 copies.append((gp.oldpath, gp.path))
818 removes.add(gp.oldpath)
815 removes.add(gp.oldpath)
819 elif gp.op == 'COPY':
816 elif gp.op == 'COPY':
820 copies.append((gp.oldpath, gp.path))
817 copies.append((gp.oldpath, gp.path))
821 elif gp.op == 'DELETE':
818 elif gp.op == 'DELETE':
822 removes.add(gp.path)
819 removes.add(gp.path)
823
820
824 wctx = repo[None]
821 wctx = repo[None]
825 for src, dst in copies:
822 for src, dst in copies:
826 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
823 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
827 if (not similarity) and removes:
824 if (not similarity) and removes:
828 wctx.remove(sorted(removes), True)
825 wctx.remove(sorted(removes), True)
829
826
830 for f in patches:
827 for f in patches:
831 gp = patches[f]
828 gp = patches[f]
832 if gp and gp.mode:
829 if gp and gp.mode:
833 islink, isexec = gp.mode
830 islink, isexec = gp.mode
834 dst = repo.wjoin(gp.path)
831 dst = repo.wjoin(gp.path)
835 # patch won't create empty files
832 # patch won't create empty files
836 if gp.op == 'ADD' and not os.path.lexists(dst):
833 if gp.op == 'ADD' and not os.path.lexists(dst):
837 flags = (isexec and 'x' or '') + (islink and 'l' or '')
834 flags = (isexec and 'x' or '') + (islink and 'l' or '')
838 repo.wwrite(gp.path, '', flags)
835 repo.wwrite(gp.path, '', flags)
839 util.setflags(dst, islink, isexec)
836 util.setflags(dst, islink, isexec)
840 addremove(repo, cfiles, similarity=similarity)
837 addremove(repo, cfiles, similarity=similarity)
841 files = patches.keys()
838 files = patches.keys()
842 files.extend([r for r in removes if r not in files])
839 files.extend([r for r in removes if r not in files])
843 return sorted(files)
840 return sorted(files)
844
841
845 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
842 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
846 """Update the dirstate to reflect the intent of copying src to dst. For
843 """Update the dirstate to reflect the intent of copying src to dst. For
847 different reasons it might not end with dst being marked as copied from src.
844 different reasons it might not end with dst being marked as copied from src.
848 """
845 """
849 origsrc = repo.dirstate.copied(src) or src
846 origsrc = repo.dirstate.copied(src) or src
850 if dst == origsrc: # copying back a copy?
847 if dst == origsrc: # copying back a copy?
851 if repo.dirstate[dst] not in 'mn' and not dryrun:
848 if repo.dirstate[dst] not in 'mn' and not dryrun:
852 repo.dirstate.normallookup(dst)
849 repo.dirstate.normallookup(dst)
853 else:
850 else:
854 if repo.dirstate[origsrc] == 'a' and origsrc == src:
851 if repo.dirstate[origsrc] == 'a' and origsrc == src:
855 if not ui.quiet:
852 if not ui.quiet:
856 ui.warn(_("%s has not been committed yet, so no copy "
853 ui.warn(_("%s has not been committed yet, so no copy "
857 "data will be stored for %s.\n")
854 "data will be stored for %s.\n")
858 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
855 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
859 if repo.dirstate[dst] in '?r' and not dryrun:
856 if repo.dirstate[dst] in '?r' and not dryrun:
860 wctx.add([dst])
857 wctx.add([dst])
861 elif not dryrun:
858 elif not dryrun:
862 wctx.copy(origsrc, dst)
859 wctx.copy(origsrc, dst)
863
860
864 def readrequires(opener, supported):
861 def readrequires(opener, supported):
865 '''Reads and parses .hg/requires and checks if all entries found
862 '''Reads and parses .hg/requires and checks if all entries found
866 are in the list of supported features.'''
863 are in the list of supported features.'''
867 requirements = set(opener.read("requires").splitlines())
864 requirements = set(opener.read("requires").splitlines())
868 missings = []
865 missings = []
869 for r in requirements:
866 for r in requirements:
870 if r not in supported:
867 if r not in supported:
871 if not r or not r[0].isalnum():
868 if not r or not r[0].isalnum():
872 raise error.RequirementError(_(".hg/requires file is corrupt"))
869 raise error.RequirementError(_(".hg/requires file is corrupt"))
873 missings.append(r)
870 missings.append(r)
874 missings.sort()
871 missings.sort()
875 if missings:
872 if missings:
876 raise error.RequirementError(
873 raise error.RequirementError(
877 _("unknown repository format: requires features '%s' (upgrade "
874 _("unknown repository format: requires features '%s' (upgrade "
878 "Mercurial)") % "', '".join(missings))
875 "Mercurial)") % "', '".join(missings))
879 return requirements
876 return requirements
880
877
881 class filecacheentry(object):
878 class filecacheentry(object):
882 def __init__(self, path, stat=True):
879 def __init__(self, path, stat=True):
883 self.path = path
880 self.path = path
884 self.cachestat = None
881 self.cachestat = None
885 self._cacheable = None
882 self._cacheable = None
886
883
887 if stat:
884 if stat:
888 self.cachestat = filecacheentry.stat(self.path)
885 self.cachestat = filecacheentry.stat(self.path)
889
886
890 if self.cachestat:
887 if self.cachestat:
891 self._cacheable = self.cachestat.cacheable()
888 self._cacheable = self.cachestat.cacheable()
892 else:
889 else:
893 # None means we don't know yet
890 # None means we don't know yet
894 self._cacheable = None
891 self._cacheable = None
895
892
896 def refresh(self):
893 def refresh(self):
897 if self.cacheable():
894 if self.cacheable():
898 self.cachestat = filecacheentry.stat(self.path)
895 self.cachestat = filecacheentry.stat(self.path)
899
896
900 def cacheable(self):
897 def cacheable(self):
901 if self._cacheable is not None:
898 if self._cacheable is not None:
902 return self._cacheable
899 return self._cacheable
903
900
904 # we don't know yet, assume it is for now
901 # we don't know yet, assume it is for now
905 return True
902 return True
906
903
907 def changed(self):
904 def changed(self):
908 # no point in going further if we can't cache it
905 # no point in going further if we can't cache it
909 if not self.cacheable():
906 if not self.cacheable():
910 return True
907 return True
911
908
912 newstat = filecacheentry.stat(self.path)
909 newstat = filecacheentry.stat(self.path)
913
910
914 # we may not know if it's cacheable yet, check again now
911 # we may not know if it's cacheable yet, check again now
915 if newstat and self._cacheable is None:
912 if newstat and self._cacheable is None:
916 self._cacheable = newstat.cacheable()
913 self._cacheable = newstat.cacheable()
917
914
918 # check again
915 # check again
919 if not self._cacheable:
916 if not self._cacheable:
920 return True
917 return True
921
918
922 if self.cachestat != newstat:
919 if self.cachestat != newstat:
923 self.cachestat = newstat
920 self.cachestat = newstat
924 return True
921 return True
925 else:
922 else:
926 return False
923 return False
927
924
928 @staticmethod
925 @staticmethod
929 def stat(path):
926 def stat(path):
930 try:
927 try:
931 return util.cachestat(path)
928 return util.cachestat(path)
932 except OSError, e:
929 except OSError, e:
933 if e.errno != errno.ENOENT:
930 if e.errno != errno.ENOENT:
934 raise
931 raise
935
932
936 class filecache(object):
933 class filecache(object):
937 '''A property like decorator that tracks a file under .hg/ for updates.
934 '''A property like decorator that tracks a file under .hg/ for updates.
938
935
939 Records stat info when called in _filecache.
936 Records stat info when called in _filecache.
940
937
941 On subsequent calls, compares old stat info with new info, and recreates
938 On subsequent calls, compares old stat info with new info, and recreates
942 the object when needed, updating the new stat info in _filecache.
939 the object when needed, updating the new stat info in _filecache.
943
940
944 Mercurial either atomic renames or appends for files under .hg,
941 Mercurial either atomic renames or appends for files under .hg,
945 so to ensure the cache is reliable we need the filesystem to be able
942 so to ensure the cache is reliable we need the filesystem to be able
946 to tell us if a file has been replaced. If it can't, we fallback to
943 to tell us if a file has been replaced. If it can't, we fallback to
947 recreating the object on every call (essentially the same behaviour as
944 recreating the object on every call (essentially the same behaviour as
948 propertycache).'''
945 propertycache).'''
949 def __init__(self, path):
946 def __init__(self, path):
950 self.path = path
947 self.path = path
951
948
952 def join(self, obj, fname):
949 def join(self, obj, fname):
953 """Used to compute the runtime path of the cached file.
950 """Used to compute the runtime path of the cached file.
954
951
955 Users should subclass filecache and provide their own version of this
952 Users should subclass filecache and provide their own version of this
956 function to call the appropriate join function on 'obj' (an instance
953 function to call the appropriate join function on 'obj' (an instance
957 of the class that its member function was decorated).
954 of the class that its member function was decorated).
958 """
955 """
959 return obj.join(fname)
956 return obj.join(fname)
960
957
961 def __call__(self, func):
958 def __call__(self, func):
962 self.func = func
959 self.func = func
963 self.name = func.__name__
960 self.name = func.__name__
964 return self
961 return self
965
962
966 def __get__(self, obj, type=None):
963 def __get__(self, obj, type=None):
967 # do we need to check if the file changed?
964 # do we need to check if the file changed?
968 if self.name in obj.__dict__:
965 if self.name in obj.__dict__:
969 assert self.name in obj._filecache, self.name
966 assert self.name in obj._filecache, self.name
970 return obj.__dict__[self.name]
967 return obj.__dict__[self.name]
971
968
972 entry = obj._filecache.get(self.name)
969 entry = obj._filecache.get(self.name)
973
970
974 if entry:
971 if entry:
975 if entry.changed():
972 if entry.changed():
976 entry.obj = self.func(obj)
973 entry.obj = self.func(obj)
977 else:
974 else:
978 path = self.join(obj, self.path)
975 path = self.join(obj, self.path)
979
976
980 # We stat -before- creating the object so our cache doesn't lie if
977 # We stat -before- creating the object so our cache doesn't lie if
981 # a writer modified between the time we read and stat
978 # a writer modified between the time we read and stat
982 entry = filecacheentry(path)
979 entry = filecacheentry(path)
983 entry.obj = self.func(obj)
980 entry.obj = self.func(obj)
984
981
985 obj._filecache[self.name] = entry
982 obj._filecache[self.name] = entry
986
983
987 obj.__dict__[self.name] = entry.obj
984 obj.__dict__[self.name] = entry.obj
988 return entry.obj
985 return entry.obj
989
986
990 def __set__(self, obj, value):
987 def __set__(self, obj, value):
991 if self.name not in obj._filecache:
988 if self.name not in obj._filecache:
992 # we add an entry for the missing value because X in __dict__
989 # we add an entry for the missing value because X in __dict__
993 # implies X in _filecache
990 # implies X in _filecache
994 ce = filecacheentry(self.join(obj, self.path), False)
991 ce = filecacheentry(self.join(obj, self.path), False)
995 obj._filecache[self.name] = ce
992 obj._filecache[self.name] = ce
996 else:
993 else:
997 ce = obj._filecache[self.name]
994 ce = obj._filecache[self.name]
998
995
999 ce.obj = value # update cached copy
996 ce.obj = value # update cached copy
1000 obj.__dict__[self.name] = value # update copy returned by obj.x
997 obj.__dict__[self.name] = value # update copy returned by obj.x
1001
998
1002 def __delete__(self, obj):
999 def __delete__(self, obj):
1003 try:
1000 try:
1004 del obj.__dict__[self.name]
1001 del obj.__dict__[self.name]
1005 except KeyError:
1002 except KeyError:
1006 raise AttributeError(self.name)
1003 raise AttributeError(self.name)
General Comments 0
You need to be logged in to leave comments. Login now