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