##// END OF EJS Templates
Enable path validation for copy, rename, debugwalk and other canonpath users....
Thomas Arendsen Hein -
r1976:df841634 default
parent child Browse files
Show More
@@ -1,800 +1,802 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8
8
9 This contains helper routines that are independent of the SCM core and hide
9 This contains helper routines that are independent of the SCM core and hide
10 platform-specific details from the core.
10 platform-specific details from the core.
11 """
11 """
12
12
13 import os, errno
13 import os, errno
14 from i18n import gettext as _
14 from i18n import gettext as _
15 from demandload import *
15 from demandload import *
16 demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile")
16 demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile")
17 demandload(globals(), "threading time")
17 demandload(globals(), "threading time")
18
18
19 def pipefilter(s, cmd):
19 def pipefilter(s, cmd):
20 '''filter string S through command CMD, returning its output'''
20 '''filter string S through command CMD, returning its output'''
21 (pout, pin) = popen2.popen2(cmd, -1, 'b')
21 (pout, pin) = popen2.popen2(cmd, -1, 'b')
22 def writer():
22 def writer():
23 pin.write(s)
23 pin.write(s)
24 pin.close()
24 pin.close()
25
25
26 # we should use select instead on UNIX, but this will work on most
26 # we should use select instead on UNIX, but this will work on most
27 # systems, including Windows
27 # systems, including Windows
28 w = threading.Thread(target=writer)
28 w = threading.Thread(target=writer)
29 w.start()
29 w.start()
30 f = pout.read()
30 f = pout.read()
31 pout.close()
31 pout.close()
32 w.join()
32 w.join()
33 return f
33 return f
34
34
35 def tempfilter(s, cmd):
35 def tempfilter(s, cmd):
36 '''filter string S through a pair of temporary files with CMD.
36 '''filter string S through a pair of temporary files with CMD.
37 CMD is used as a template to create the real command to be run,
37 CMD is used as a template to create the real command to be run,
38 with the strings INFILE and OUTFILE replaced by the real names of
38 with the strings INFILE and OUTFILE replaced by the real names of
39 the temporary files generated.'''
39 the temporary files generated.'''
40 inname, outname = None, None
40 inname, outname = None, None
41 try:
41 try:
42 infd, inname = tempfile.mkstemp(prefix='hgfin')
42 infd, inname = tempfile.mkstemp(prefix='hgfin')
43 fp = os.fdopen(infd, 'wb')
43 fp = os.fdopen(infd, 'wb')
44 fp.write(s)
44 fp.write(s)
45 fp.close()
45 fp.close()
46 outfd, outname = tempfile.mkstemp(prefix='hgfout')
46 outfd, outname = tempfile.mkstemp(prefix='hgfout')
47 os.close(outfd)
47 os.close(outfd)
48 cmd = cmd.replace('INFILE', inname)
48 cmd = cmd.replace('INFILE', inname)
49 cmd = cmd.replace('OUTFILE', outname)
49 cmd = cmd.replace('OUTFILE', outname)
50 code = os.system(cmd)
50 code = os.system(cmd)
51 if code: raise Abort(_("command '%s' failed: %s") %
51 if code: raise Abort(_("command '%s' failed: %s") %
52 (cmd, explain_exit(code)))
52 (cmd, explain_exit(code)))
53 return open(outname, 'rb').read()
53 return open(outname, 'rb').read()
54 finally:
54 finally:
55 try:
55 try:
56 if inname: os.unlink(inname)
56 if inname: os.unlink(inname)
57 except: pass
57 except: pass
58 try:
58 try:
59 if outname: os.unlink(outname)
59 if outname: os.unlink(outname)
60 except: pass
60 except: pass
61
61
62 filtertable = {
62 filtertable = {
63 'tempfile:': tempfilter,
63 'tempfile:': tempfilter,
64 'pipe:': pipefilter,
64 'pipe:': pipefilter,
65 }
65 }
66
66
67 def filter(s, cmd):
67 def filter(s, cmd):
68 "filter a string through a command that transforms its input to its output"
68 "filter a string through a command that transforms its input to its output"
69 for name, fn in filtertable.iteritems():
69 for name, fn in filtertable.iteritems():
70 if cmd.startswith(name):
70 if cmd.startswith(name):
71 return fn(s, cmd[len(name):].lstrip())
71 return fn(s, cmd[len(name):].lstrip())
72 return pipefilter(s, cmd)
72 return pipefilter(s, cmd)
73
73
74 def patch(strip, patchname, ui):
74 def patch(strip, patchname, ui):
75 """apply the patch <patchname> to the working directory.
75 """apply the patch <patchname> to the working directory.
76 a list of patched files is returned"""
76 a list of patched files is returned"""
77 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
77 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
78 files = {}
78 files = {}
79 for line in fp:
79 for line in fp:
80 line = line.rstrip()
80 line = line.rstrip()
81 ui.status("%s\n" % line)
81 ui.status("%s\n" % line)
82 if line.startswith('patching file '):
82 if line.startswith('patching file '):
83 pf = parse_patch_output(line)
83 pf = parse_patch_output(line)
84 files.setdefault(pf, 1)
84 files.setdefault(pf, 1)
85 code = fp.close()
85 code = fp.close()
86 if code:
86 if code:
87 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
87 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
88 return files.keys()
88 return files.keys()
89
89
90 def binary(s):
90 def binary(s):
91 """return true if a string is binary data using diff's heuristic"""
91 """return true if a string is binary data using diff's heuristic"""
92 if s and '\0' in s[:4096]:
92 if s and '\0' in s[:4096]:
93 return True
93 return True
94 return False
94 return False
95
95
96 def unique(g):
96 def unique(g):
97 """return the uniq elements of iterable g"""
97 """return the uniq elements of iterable g"""
98 seen = {}
98 seen = {}
99 for f in g:
99 for f in g:
100 if f not in seen:
100 if f not in seen:
101 seen[f] = 1
101 seen[f] = 1
102 yield f
102 yield f
103
103
104 class Abort(Exception):
104 class Abort(Exception):
105 """Raised if a command needs to print an error and exit."""
105 """Raised if a command needs to print an error and exit."""
106
106
107 def always(fn): return True
107 def always(fn): return True
108 def never(fn): return False
108 def never(fn): return False
109
109
110 def patkind(name, dflt_pat='glob'):
110 def patkind(name, dflt_pat='glob'):
111 """Split a string into an optional pattern kind prefix and the
111 """Split a string into an optional pattern kind prefix and the
112 actual pattern."""
112 actual pattern."""
113 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
113 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
114 if name.startswith(prefix + ':'): return name.split(':', 1)
114 if name.startswith(prefix + ':'): return name.split(':', 1)
115 return dflt_pat, name
115 return dflt_pat, name
116
116
117 def globre(pat, head='^', tail='$'):
117 def globre(pat, head='^', tail='$'):
118 "convert a glob pattern into a regexp"
118 "convert a glob pattern into a regexp"
119 i, n = 0, len(pat)
119 i, n = 0, len(pat)
120 res = ''
120 res = ''
121 group = False
121 group = False
122 def peek(): return i < n and pat[i]
122 def peek(): return i < n and pat[i]
123 while i < n:
123 while i < n:
124 c = pat[i]
124 c = pat[i]
125 i = i+1
125 i = i+1
126 if c == '*':
126 if c == '*':
127 if peek() == '*':
127 if peek() == '*':
128 i += 1
128 i += 1
129 res += '.*'
129 res += '.*'
130 else:
130 else:
131 res += '[^/]*'
131 res += '[^/]*'
132 elif c == '?':
132 elif c == '?':
133 res += '.'
133 res += '.'
134 elif c == '[':
134 elif c == '[':
135 j = i
135 j = i
136 if j < n and pat[j] in '!]':
136 if j < n and pat[j] in '!]':
137 j += 1
137 j += 1
138 while j < n and pat[j] != ']':
138 while j < n and pat[j] != ']':
139 j += 1
139 j += 1
140 if j >= n:
140 if j >= n:
141 res += '\\['
141 res += '\\['
142 else:
142 else:
143 stuff = pat[i:j].replace('\\','\\\\')
143 stuff = pat[i:j].replace('\\','\\\\')
144 i = j + 1
144 i = j + 1
145 if stuff[0] == '!':
145 if stuff[0] == '!':
146 stuff = '^' + stuff[1:]
146 stuff = '^' + stuff[1:]
147 elif stuff[0] == '^':
147 elif stuff[0] == '^':
148 stuff = '\\' + stuff
148 stuff = '\\' + stuff
149 res = '%s[%s]' % (res, stuff)
149 res = '%s[%s]' % (res, stuff)
150 elif c == '{':
150 elif c == '{':
151 group = True
151 group = True
152 res += '(?:'
152 res += '(?:'
153 elif c == '}' and group:
153 elif c == '}' and group:
154 res += ')'
154 res += ')'
155 group = False
155 group = False
156 elif c == ',' and group:
156 elif c == ',' and group:
157 res += '|'
157 res += '|'
158 else:
158 else:
159 res += re.escape(c)
159 res += re.escape(c)
160 return head + res + tail
160 return head + res + tail
161
161
162 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
162 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
163
163
164 def pathto(n1, n2):
164 def pathto(n1, n2):
165 '''return the relative path from one place to another.
165 '''return the relative path from one place to another.
166 this returns a path in the form used by the local filesystem, not hg.'''
166 this returns a path in the form used by the local filesystem, not hg.'''
167 if not n1: return localpath(n2)
167 if not n1: return localpath(n2)
168 a, b = n1.split('/'), n2.split('/')
168 a, b = n1.split('/'), n2.split('/')
169 a.reverse()
169 a.reverse()
170 b.reverse()
170 b.reverse()
171 while a and b and a[-1] == b[-1]:
171 while a and b and a[-1] == b[-1]:
172 a.pop()
172 a.pop()
173 b.pop()
173 b.pop()
174 b.reverse()
174 b.reverse()
175 return os.sep.join((['..'] * len(a)) + b)
175 return os.sep.join((['..'] * len(a)) + b)
176
176
177 def canonpath(root, cwd, myname):
177 def canonpath(root, cwd, myname):
178 """return the canonical path of myname, given cwd and root"""
178 """return the canonical path of myname, given cwd and root"""
179 if root == os.sep:
179 if root == os.sep:
180 rootsep = os.sep
180 rootsep = os.sep
181 else:
181 else:
182 rootsep = root + os.sep
182 rootsep = root + os.sep
183 name = myname
183 name = myname
184 if not name.startswith(os.sep):
184 if not name.startswith(os.sep):
185 name = os.path.join(root, cwd, name)
185 name = os.path.join(root, cwd, name)
186 name = os.path.normpath(name)
186 name = os.path.normpath(name)
187 if name.startswith(rootsep):
187 if name.startswith(rootsep):
188 return pconvert(name[len(rootsep):])
188 name = name[len(rootsep):]
189 audit_path(name)
190 return pconvert(name)
189 elif name == root:
191 elif name == root:
190 return ''
192 return ''
191 else:
193 else:
192 raise Abort('%s not under root' % myname)
194 raise Abort('%s not under root' % myname)
193
195
194 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
196 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
195 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
197 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
196
198
197 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
199 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
198 if os.name == 'nt':
200 if os.name == 'nt':
199 dflt_pat = 'glob'
201 dflt_pat = 'glob'
200 else:
202 else:
201 dflt_pat = 'relpath'
203 dflt_pat = 'relpath'
202 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
204 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
203
205
204 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
206 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
205 """build a function to match a set of file patterns
207 """build a function to match a set of file patterns
206
208
207 arguments:
209 arguments:
208 canonroot - the canonical root of the tree you're matching against
210 canonroot - the canonical root of the tree you're matching against
209 cwd - the current working directory, if relevant
211 cwd - the current working directory, if relevant
210 names - patterns to find
212 names - patterns to find
211 inc - patterns to include
213 inc - patterns to include
212 exc - patterns to exclude
214 exc - patterns to exclude
213 head - a regex to prepend to patterns to control whether a match is rooted
215 head - a regex to prepend to patterns to control whether a match is rooted
214
216
215 a pattern is one of:
217 a pattern is one of:
216 'glob:<rooted glob>'
218 'glob:<rooted glob>'
217 're:<rooted regexp>'
219 're:<rooted regexp>'
218 'path:<rooted path>'
220 'path:<rooted path>'
219 'relglob:<relative glob>'
221 'relglob:<relative glob>'
220 'relpath:<relative path>'
222 'relpath:<relative path>'
221 'relre:<relative regexp>'
223 'relre:<relative regexp>'
222 '<rooted path or regexp>'
224 '<rooted path or regexp>'
223
225
224 returns:
226 returns:
225 a 3-tuple containing
227 a 3-tuple containing
226 - list of explicit non-pattern names passed in
228 - list of explicit non-pattern names passed in
227 - a bool match(filename) function
229 - a bool match(filename) function
228 - a bool indicating if any patterns were passed in
230 - a bool indicating if any patterns were passed in
229
231
230 todo:
232 todo:
231 make head regex a rooted bool
233 make head regex a rooted bool
232 """
234 """
233
235
234 def contains_glob(name):
236 def contains_glob(name):
235 for c in name:
237 for c in name:
236 if c in _globchars: return True
238 if c in _globchars: return True
237 return False
239 return False
238
240
239 def regex(kind, name, tail):
241 def regex(kind, name, tail):
240 '''convert a pattern into a regular expression'''
242 '''convert a pattern into a regular expression'''
241 if kind == 're':
243 if kind == 're':
242 return name
244 return name
243 elif kind == 'path':
245 elif kind == 'path':
244 return '^' + re.escape(name) + '(?:/|$)'
246 return '^' + re.escape(name) + '(?:/|$)'
245 elif kind == 'relglob':
247 elif kind == 'relglob':
246 return head + globre(name, '(?:|.*/)', tail)
248 return head + globre(name, '(?:|.*/)', tail)
247 elif kind == 'relpath':
249 elif kind == 'relpath':
248 return head + re.escape(name) + tail
250 return head + re.escape(name) + tail
249 elif kind == 'relre':
251 elif kind == 'relre':
250 if name.startswith('^'):
252 if name.startswith('^'):
251 return name
253 return name
252 return '.*' + name
254 return '.*' + name
253 return head + globre(name, '', tail)
255 return head + globre(name, '', tail)
254
256
255 def matchfn(pats, tail):
257 def matchfn(pats, tail):
256 """build a matching function from a set of patterns"""
258 """build a matching function from a set of patterns"""
257 if not pats:
259 if not pats:
258 return
260 return
259 matches = []
261 matches = []
260 for k, p in pats:
262 for k, p in pats:
261 try:
263 try:
262 pat = '(?:%s)' % regex(k, p, tail)
264 pat = '(?:%s)' % regex(k, p, tail)
263 matches.append(re.compile(pat).match)
265 matches.append(re.compile(pat).match)
264 except re.error:
266 except re.error:
265 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
267 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
266 else: raise Abort("invalid pattern (%s): %s" % (k, p))
268 else: raise Abort("invalid pattern (%s): %s" % (k, p))
267
269
268 def buildfn(text):
270 def buildfn(text):
269 for m in matches:
271 for m in matches:
270 r = m(text)
272 r = m(text)
271 if r:
273 if r:
272 return r
274 return r
273
275
274 return buildfn
276 return buildfn
275
277
276 def globprefix(pat):
278 def globprefix(pat):
277 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
279 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
278 root = []
280 root = []
279 for p in pat.split(os.sep):
281 for p in pat.split(os.sep):
280 if contains_glob(p): break
282 if contains_glob(p): break
281 root.append(p)
283 root.append(p)
282 return '/'.join(root)
284 return '/'.join(root)
283
285
284 pats = []
286 pats = []
285 files = []
287 files = []
286 roots = []
288 roots = []
287 for kind, name in [patkind(p, dflt_pat) for p in names]:
289 for kind, name in [patkind(p, dflt_pat) for p in names]:
288 if kind in ('glob', 'relpath'):
290 if kind in ('glob', 'relpath'):
289 name = canonpath(canonroot, cwd, name)
291 name = canonpath(canonroot, cwd, name)
290 if name == '':
292 if name == '':
291 kind, name = 'glob', '**'
293 kind, name = 'glob', '**'
292 if kind in ('glob', 'path', 're'):
294 if kind in ('glob', 'path', 're'):
293 pats.append((kind, name))
295 pats.append((kind, name))
294 if kind == 'glob':
296 if kind == 'glob':
295 root = globprefix(name)
297 root = globprefix(name)
296 if root: roots.append(root)
298 if root: roots.append(root)
297 elif kind == 'relpath':
299 elif kind == 'relpath':
298 files.append((kind, name))
300 files.append((kind, name))
299 roots.append(name)
301 roots.append(name)
300
302
301 patmatch = matchfn(pats, '$') or always
303 patmatch = matchfn(pats, '$') or always
302 filematch = matchfn(files, '(?:/|$)') or always
304 filematch = matchfn(files, '(?:/|$)') or always
303 incmatch = always
305 incmatch = always
304 if inc:
306 if inc:
305 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
307 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
306 excmatch = lambda fn: False
308 excmatch = lambda fn: False
307 if exc:
309 if exc:
308 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
310 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
309
311
310 return (roots,
312 return (roots,
311 lambda fn: (incmatch(fn) and not excmatch(fn) and
313 lambda fn: (incmatch(fn) and not excmatch(fn) and
312 (fn.endswith('/') or
314 (fn.endswith('/') or
313 (not pats and not files) or
315 (not pats and not files) or
314 (pats and patmatch(fn)) or
316 (pats and patmatch(fn)) or
315 (files and filematch(fn)))),
317 (files and filematch(fn)))),
316 (inc or exc or (pats and pats != [('glob', '**')])) and True)
318 (inc or exc or (pats and pats != [('glob', '**')])) and True)
317
319
318 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
320 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
319 '''enhanced shell command execution.
321 '''enhanced shell command execution.
320 run with environment maybe modified, maybe in different dir.
322 run with environment maybe modified, maybe in different dir.
321
323
322 if command fails and onerr is None, return status. if ui object,
324 if command fails and onerr is None, return status. if ui object,
323 print error message and return status, else raise onerr object as
325 print error message and return status, else raise onerr object as
324 exception.'''
326 exception.'''
325 oldenv = {}
327 oldenv = {}
326 for k in environ:
328 for k in environ:
327 oldenv[k] = os.environ.get(k)
329 oldenv[k] = os.environ.get(k)
328 if cwd is not None:
330 if cwd is not None:
329 oldcwd = os.getcwd()
331 oldcwd = os.getcwd()
330 try:
332 try:
331 for k, v in environ.iteritems():
333 for k, v in environ.iteritems():
332 os.environ[k] = str(v)
334 os.environ[k] = str(v)
333 if cwd is not None and oldcwd != cwd:
335 if cwd is not None and oldcwd != cwd:
334 os.chdir(cwd)
336 os.chdir(cwd)
335 rc = os.system(cmd)
337 rc = os.system(cmd)
336 if rc and onerr:
338 if rc and onerr:
337 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
339 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
338 explain_exit(rc)[0])
340 explain_exit(rc)[0])
339 if errprefix:
341 if errprefix:
340 errmsg = '%s: %s' % (errprefix, errmsg)
342 errmsg = '%s: %s' % (errprefix, errmsg)
341 try:
343 try:
342 onerr.warn(errmsg + '\n')
344 onerr.warn(errmsg + '\n')
343 except AttributeError:
345 except AttributeError:
344 raise onerr(errmsg)
346 raise onerr(errmsg)
345 return rc
347 return rc
346 finally:
348 finally:
347 for k, v in oldenv.iteritems():
349 for k, v in oldenv.iteritems():
348 if v is None:
350 if v is None:
349 del os.environ[k]
351 del os.environ[k]
350 else:
352 else:
351 os.environ[k] = v
353 os.environ[k] = v
352 if cwd is not None and oldcwd != cwd:
354 if cwd is not None and oldcwd != cwd:
353 os.chdir(oldcwd)
355 os.chdir(oldcwd)
354
356
355 def rename(src, dst):
357 def rename(src, dst):
356 """forcibly rename a file"""
358 """forcibly rename a file"""
357 try:
359 try:
358 os.rename(src, dst)
360 os.rename(src, dst)
359 except:
361 except:
360 os.unlink(dst)
362 os.unlink(dst)
361 os.rename(src, dst)
363 os.rename(src, dst)
362
364
363 def unlink(f):
365 def unlink(f):
364 """unlink and remove the directory if it is empty"""
366 """unlink and remove the directory if it is empty"""
365 os.unlink(f)
367 os.unlink(f)
366 # try removing directories that might now be empty
368 # try removing directories that might now be empty
367 try: os.removedirs(os.path.dirname(f))
369 try: os.removedirs(os.path.dirname(f))
368 except: pass
370 except: pass
369
371
370 def copyfiles(src, dst, hardlink=None):
372 def copyfiles(src, dst, hardlink=None):
371 """Copy a directory tree using hardlinks if possible"""
373 """Copy a directory tree using hardlinks if possible"""
372
374
373 if hardlink is None:
375 if hardlink is None:
374 hardlink = (os.stat(src).st_dev ==
376 hardlink = (os.stat(src).st_dev ==
375 os.stat(os.path.dirname(dst)).st_dev)
377 os.stat(os.path.dirname(dst)).st_dev)
376
378
377 if os.path.isdir(src):
379 if os.path.isdir(src):
378 os.mkdir(dst)
380 os.mkdir(dst)
379 for name in os.listdir(src):
381 for name in os.listdir(src):
380 srcname = os.path.join(src, name)
382 srcname = os.path.join(src, name)
381 dstname = os.path.join(dst, name)
383 dstname = os.path.join(dst, name)
382 copyfiles(srcname, dstname, hardlink)
384 copyfiles(srcname, dstname, hardlink)
383 else:
385 else:
384 if hardlink:
386 if hardlink:
385 try:
387 try:
386 os_link(src, dst)
388 os_link(src, dst)
387 except:
389 except:
388 hardlink = False
390 hardlink = False
389 shutil.copy(src, dst)
391 shutil.copy(src, dst)
390 else:
392 else:
391 shutil.copy(src, dst)
393 shutil.copy(src, dst)
392
394
393 def audit_path(path):
395 def audit_path(path):
394 """Abort if path contains dangerous components"""
396 """Abort if path contains dangerous components"""
395 parts = os.path.normcase(path).split(os.sep)
397 parts = os.path.normcase(path).split(os.sep)
396 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
398 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
397 or os.pardir in parts):
399 or os.pardir in parts):
398 raise Abort(_("path contains illegal component: %s\n") % path)
400 raise Abort(_("path contains illegal component: %s\n") % path)
399
401
400 def opener(base, audit=True):
402 def opener(base, audit=True):
401 """
403 """
402 return a function that opens files relative to base
404 return a function that opens files relative to base
403
405
404 this function is used to hide the details of COW semantics and
406 this function is used to hide the details of COW semantics and
405 remote file access from higher level code.
407 remote file access from higher level code.
406 """
408 """
407 p = base
409 p = base
408 audit_p = audit
410 audit_p = audit
409
411
410 def mktempcopy(name):
412 def mktempcopy(name):
411 d, fn = os.path.split(name)
413 d, fn = os.path.split(name)
412 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
414 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
413 fp = os.fdopen(fd, "wb")
415 fp = os.fdopen(fd, "wb")
414 try:
416 try:
415 fp.write(file(name, "rb").read())
417 fp.write(file(name, "rb").read())
416 except:
418 except:
417 try: os.unlink(temp)
419 try: os.unlink(temp)
418 except: pass
420 except: pass
419 raise
421 raise
420 fp.close()
422 fp.close()
421 st = os.lstat(name)
423 st = os.lstat(name)
422 os.chmod(temp, st.st_mode)
424 os.chmod(temp, st.st_mode)
423 return temp
425 return temp
424
426
425 class atomicfile(file):
427 class atomicfile(file):
426 """the file will only be copied on close"""
428 """the file will only be copied on close"""
427 def __init__(self, name, mode, atomic=False):
429 def __init__(self, name, mode, atomic=False):
428 self.__name = name
430 self.__name = name
429 self.temp = mktempcopy(name)
431 self.temp = mktempcopy(name)
430 file.__init__(self, self.temp, mode)
432 file.__init__(self, self.temp, mode)
431 def close(self):
433 def close(self):
432 if not self.closed:
434 if not self.closed:
433 file.close(self)
435 file.close(self)
434 rename(self.temp, self.__name)
436 rename(self.temp, self.__name)
435 def __del__(self):
437 def __del__(self):
436 self.close()
438 self.close()
437
439
438 def o(path, mode="r", text=False, atomic=False):
440 def o(path, mode="r", text=False, atomic=False):
439 if audit_p:
441 if audit_p:
440 audit_path(path)
442 audit_path(path)
441 f = os.path.join(p, path)
443 f = os.path.join(p, path)
442
444
443 if not text:
445 if not text:
444 mode += "b" # for that other OS
446 mode += "b" # for that other OS
445
447
446 if mode[0] != "r":
448 if mode[0] != "r":
447 try:
449 try:
448 nlink = nlinks(f)
450 nlink = nlinks(f)
449 except OSError:
451 except OSError:
450 d = os.path.dirname(f)
452 d = os.path.dirname(f)
451 if not os.path.isdir(d):
453 if not os.path.isdir(d):
452 os.makedirs(d)
454 os.makedirs(d)
453 else:
455 else:
454 if atomic:
456 if atomic:
455 return atomicfile(f, mode)
457 return atomicfile(f, mode)
456 if nlink > 1:
458 if nlink > 1:
457 rename(mktempcopy(f), f)
459 rename(mktempcopy(f), f)
458 return file(f, mode)
460 return file(f, mode)
459
461
460 return o
462 return o
461
463
462 def _makelock_file(info, pathname):
464 def _makelock_file(info, pathname):
463 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
465 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
464 os.write(ld, info)
466 os.write(ld, info)
465 os.close(ld)
467 os.close(ld)
466
468
467 def _readlock_file(pathname):
469 def _readlock_file(pathname):
468 return file(pathname).read()
470 return file(pathname).read()
469
471
470 def nlinks(pathname):
472 def nlinks(pathname):
471 """Return number of hardlinks for the given file."""
473 """Return number of hardlinks for the given file."""
472 return os.stat(pathname).st_nlink
474 return os.stat(pathname).st_nlink
473
475
474 if hasattr(os, 'link'):
476 if hasattr(os, 'link'):
475 os_link = os.link
477 os_link = os.link
476 else:
478 else:
477 def os_link(src, dst):
479 def os_link(src, dst):
478 raise OSError(0, _("Hardlinks not supported"))
480 raise OSError(0, _("Hardlinks not supported"))
479
481
480 # Platform specific variants
482 # Platform specific variants
481 if os.name == 'nt':
483 if os.name == 'nt':
482 demandload(globals(), "msvcrt")
484 demandload(globals(), "msvcrt")
483 nulldev = 'NUL:'
485 nulldev = 'NUL:'
484
486
485 class winstdout:
487 class winstdout:
486 '''stdout on windows misbehaves if sent through a pipe'''
488 '''stdout on windows misbehaves if sent through a pipe'''
487
489
488 def __init__(self, fp):
490 def __init__(self, fp):
489 self.fp = fp
491 self.fp = fp
490
492
491 def __getattr__(self, key):
493 def __getattr__(self, key):
492 return getattr(self.fp, key)
494 return getattr(self.fp, key)
493
495
494 def close(self):
496 def close(self):
495 try:
497 try:
496 self.fp.close()
498 self.fp.close()
497 except: pass
499 except: pass
498
500
499 def write(self, s):
501 def write(self, s):
500 try:
502 try:
501 return self.fp.write(s)
503 return self.fp.write(s)
502 except IOError, inst:
504 except IOError, inst:
503 if inst.errno != 0: raise
505 if inst.errno != 0: raise
504 self.close()
506 self.close()
505 raise IOError(errno.EPIPE, 'Broken pipe')
507 raise IOError(errno.EPIPE, 'Broken pipe')
506
508
507 sys.stdout = winstdout(sys.stdout)
509 sys.stdout = winstdout(sys.stdout)
508
510
509 def os_rcpath():
511 def os_rcpath():
510 '''return default os-specific hgrc search path'''
512 '''return default os-specific hgrc search path'''
511 try:
513 try:
512 import win32api, win32process
514 import win32api, win32process
513 proc = win32api.GetCurrentProcess()
515 proc = win32api.GetCurrentProcess()
514 filename = win32process.GetModuleFileNameEx(proc, 0)
516 filename = win32process.GetModuleFileNameEx(proc, 0)
515 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
517 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
516 except ImportError:
518 except ImportError:
517 systemrc = r'c:\mercurial\mercurial.ini'
519 systemrc = r'c:\mercurial\mercurial.ini'
518
520
519 return [systemrc,
521 return [systemrc,
520 os.path.join(os.path.expanduser('~'), 'mercurial.ini')]
522 os.path.join(os.path.expanduser('~'), 'mercurial.ini')]
521
523
522 def parse_patch_output(output_line):
524 def parse_patch_output(output_line):
523 """parses the output produced by patch and returns the file name"""
525 """parses the output produced by patch and returns the file name"""
524 pf = output_line[14:]
526 pf = output_line[14:]
525 if pf[0] == '`':
527 if pf[0] == '`':
526 pf = pf[1:-1] # Remove the quotes
528 pf = pf[1:-1] # Remove the quotes
527 return pf
529 return pf
528
530
529 try: # ActivePython can create hard links using win32file module
531 try: # ActivePython can create hard links using win32file module
530 import win32api, win32con, win32file
532 import win32api, win32con, win32file
531
533
532 def os_link(src, dst): # NB will only succeed on NTFS
534 def os_link(src, dst): # NB will only succeed on NTFS
533 win32file.CreateHardLink(dst, src)
535 win32file.CreateHardLink(dst, src)
534
536
535 def nlinks(pathname):
537 def nlinks(pathname):
536 """Return number of hardlinks for the given file."""
538 """Return number of hardlinks for the given file."""
537 try:
539 try:
538 fh = win32file.CreateFile(pathname,
540 fh = win32file.CreateFile(pathname,
539 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
541 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
540 None, win32file.OPEN_EXISTING, 0, None)
542 None, win32file.OPEN_EXISTING, 0, None)
541 res = win32file.GetFileInformationByHandle(fh)
543 res = win32file.GetFileInformationByHandle(fh)
542 fh.Close()
544 fh.Close()
543 return res[7]
545 return res[7]
544 except:
546 except:
545 return os.stat(pathname).st_nlink
547 return os.stat(pathname).st_nlink
546
548
547 def testpid(pid):
549 def testpid(pid):
548 '''return False if pid is dead, True if running or not known'''
550 '''return False if pid is dead, True if running or not known'''
549 try:
551 try:
550 win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
552 win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
551 False, pid)
553 False, pid)
552 except:
554 except:
553 return True
555 return True
554
556
555 except ImportError:
557 except ImportError:
556 def testpid(pid):
558 def testpid(pid):
557 '''return False if pid dead, True if running or not known'''
559 '''return False if pid dead, True if running or not known'''
558 return True
560 return True
559
561
560 def is_exec(f, last):
562 def is_exec(f, last):
561 return last
563 return last
562
564
563 def set_exec(f, mode):
565 def set_exec(f, mode):
564 pass
566 pass
565
567
566 def set_binary(fd):
568 def set_binary(fd):
567 msvcrt.setmode(fd.fileno(), os.O_BINARY)
569 msvcrt.setmode(fd.fileno(), os.O_BINARY)
568
570
569 def pconvert(path):
571 def pconvert(path):
570 return path.replace("\\", "/")
572 return path.replace("\\", "/")
571
573
572 def localpath(path):
574 def localpath(path):
573 return path.replace('/', '\\')
575 return path.replace('/', '\\')
574
576
575 def normpath(path):
577 def normpath(path):
576 return pconvert(os.path.normpath(path))
578 return pconvert(os.path.normpath(path))
577
579
578 makelock = _makelock_file
580 makelock = _makelock_file
579 readlock = _readlock_file
581 readlock = _readlock_file
580
582
581 def explain_exit(code):
583 def explain_exit(code):
582 return _("exited with status %d") % code, code
584 return _("exited with status %d") % code, code
583
585
584 else:
586 else:
585 nulldev = '/dev/null'
587 nulldev = '/dev/null'
586
588
587 def rcfiles(path):
589 def rcfiles(path):
588 rcs = [os.path.join(path, 'hgrc')]
590 rcs = [os.path.join(path, 'hgrc')]
589 rcdir = os.path.join(path, 'hgrc.d')
591 rcdir = os.path.join(path, 'hgrc.d')
590 try:
592 try:
591 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
593 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
592 if f.endswith(".rc")])
594 if f.endswith(".rc")])
593 except OSError, inst: pass
595 except OSError, inst: pass
594 return rcs
596 return rcs
595
597
596 def os_rcpath():
598 def os_rcpath():
597 '''return default os-specific hgrc search path'''
599 '''return default os-specific hgrc search path'''
598 path = []
600 path = []
599 if len(sys.argv) > 0:
601 if len(sys.argv) > 0:
600 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
602 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
601 '/../etc/mercurial'))
603 '/../etc/mercurial'))
602 path.extend(rcfiles('/etc/mercurial'))
604 path.extend(rcfiles('/etc/mercurial'))
603 path.append(os.path.expanduser('~/.hgrc'))
605 path.append(os.path.expanduser('~/.hgrc'))
604 path = [os.path.normpath(f) for f in path]
606 path = [os.path.normpath(f) for f in path]
605 return path
607 return path
606
608
607 def parse_patch_output(output_line):
609 def parse_patch_output(output_line):
608 """parses the output produced by patch and returns the file name"""
610 """parses the output produced by patch and returns the file name"""
609 pf = output_line[14:]
611 pf = output_line[14:]
610 if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0:
612 if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0:
611 pf = pf[1:-1] # Remove the quotes
613 pf = pf[1:-1] # Remove the quotes
612 return pf
614 return pf
613
615
614 def is_exec(f, last):
616 def is_exec(f, last):
615 """check whether a file is executable"""
617 """check whether a file is executable"""
616 return (os.stat(f).st_mode & 0100 != 0)
618 return (os.stat(f).st_mode & 0100 != 0)
617
619
618 def set_exec(f, mode):
620 def set_exec(f, mode):
619 s = os.stat(f).st_mode
621 s = os.stat(f).st_mode
620 if (s & 0100 != 0) == mode:
622 if (s & 0100 != 0) == mode:
621 return
623 return
622 if mode:
624 if mode:
623 # Turn on +x for every +r bit when making a file executable
625 # Turn on +x for every +r bit when making a file executable
624 # and obey umask.
626 # and obey umask.
625 umask = os.umask(0)
627 umask = os.umask(0)
626 os.umask(umask)
628 os.umask(umask)
627 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
629 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
628 else:
630 else:
629 os.chmod(f, s & 0666)
631 os.chmod(f, s & 0666)
630
632
631 def set_binary(fd):
633 def set_binary(fd):
632 pass
634 pass
633
635
634 def pconvert(path):
636 def pconvert(path):
635 return path
637 return path
636
638
637 def localpath(path):
639 def localpath(path):
638 return path
640 return path
639
641
640 normpath = os.path.normpath
642 normpath = os.path.normpath
641
643
642 def makelock(info, pathname):
644 def makelock(info, pathname):
643 try:
645 try:
644 os.symlink(info, pathname)
646 os.symlink(info, pathname)
645 except OSError, why:
647 except OSError, why:
646 if why.errno == errno.EEXIST:
648 if why.errno == errno.EEXIST:
647 raise
649 raise
648 else:
650 else:
649 _makelock_file(info, pathname)
651 _makelock_file(info, pathname)
650
652
651 def readlock(pathname):
653 def readlock(pathname):
652 try:
654 try:
653 return os.readlink(pathname)
655 return os.readlink(pathname)
654 except OSError, why:
656 except OSError, why:
655 if why.errno == errno.EINVAL:
657 if why.errno == errno.EINVAL:
656 return _readlock_file(pathname)
658 return _readlock_file(pathname)
657 else:
659 else:
658 raise
660 raise
659
661
660 def testpid(pid):
662 def testpid(pid):
661 '''return False if pid dead, True if running or not sure'''
663 '''return False if pid dead, True if running or not sure'''
662 try:
664 try:
663 os.kill(pid, 0)
665 os.kill(pid, 0)
664 return True
666 return True
665 except OSError, inst:
667 except OSError, inst:
666 return inst.errno != errno.ESRCH
668 return inst.errno != errno.ESRCH
667
669
668 def explain_exit(code):
670 def explain_exit(code):
669 """return a 2-tuple (desc, code) describing a process's status"""
671 """return a 2-tuple (desc, code) describing a process's status"""
670 if os.WIFEXITED(code):
672 if os.WIFEXITED(code):
671 val = os.WEXITSTATUS(code)
673 val = os.WEXITSTATUS(code)
672 return _("exited with status %d") % val, val
674 return _("exited with status %d") % val, val
673 elif os.WIFSIGNALED(code):
675 elif os.WIFSIGNALED(code):
674 val = os.WTERMSIG(code)
676 val = os.WTERMSIG(code)
675 return _("killed by signal %d") % val, val
677 return _("killed by signal %d") % val, val
676 elif os.WIFSTOPPED(code):
678 elif os.WIFSTOPPED(code):
677 val = os.WSTOPSIG(code)
679 val = os.WSTOPSIG(code)
678 return _("stopped by signal %d") % val, val
680 return _("stopped by signal %d") % val, val
679 raise ValueError(_("invalid exit code"))
681 raise ValueError(_("invalid exit code"))
680
682
681 class chunkbuffer(object):
683 class chunkbuffer(object):
682 """Allow arbitrary sized chunks of data to be efficiently read from an
684 """Allow arbitrary sized chunks of data to be efficiently read from an
683 iterator over chunks of arbitrary size."""
685 iterator over chunks of arbitrary size."""
684
686
685 def __init__(self, in_iter, targetsize = 2**16):
687 def __init__(self, in_iter, targetsize = 2**16):
686 """in_iter is the iterator that's iterating over the input chunks.
688 """in_iter is the iterator that's iterating over the input chunks.
687 targetsize is how big a buffer to try to maintain."""
689 targetsize is how big a buffer to try to maintain."""
688 self.in_iter = iter(in_iter)
690 self.in_iter = iter(in_iter)
689 self.buf = ''
691 self.buf = ''
690 self.targetsize = int(targetsize)
692 self.targetsize = int(targetsize)
691 if self.targetsize <= 0:
693 if self.targetsize <= 0:
692 raise ValueError(_("targetsize must be greater than 0, was %d") %
694 raise ValueError(_("targetsize must be greater than 0, was %d") %
693 targetsize)
695 targetsize)
694 self.iterempty = False
696 self.iterempty = False
695
697
696 def fillbuf(self):
698 def fillbuf(self):
697 """Ignore target size; read every chunk from iterator until empty."""
699 """Ignore target size; read every chunk from iterator until empty."""
698 if not self.iterempty:
700 if not self.iterempty:
699 collector = cStringIO.StringIO()
701 collector = cStringIO.StringIO()
700 collector.write(self.buf)
702 collector.write(self.buf)
701 for ch in self.in_iter:
703 for ch in self.in_iter:
702 collector.write(ch)
704 collector.write(ch)
703 self.buf = collector.getvalue()
705 self.buf = collector.getvalue()
704 self.iterempty = True
706 self.iterempty = True
705
707
706 def read(self, l):
708 def read(self, l):
707 """Read L bytes of data from the iterator of chunks of data.
709 """Read L bytes of data from the iterator of chunks of data.
708 Returns less than L bytes if the iterator runs dry."""
710 Returns less than L bytes if the iterator runs dry."""
709 if l > len(self.buf) and not self.iterempty:
711 if l > len(self.buf) and not self.iterempty:
710 # Clamp to a multiple of self.targetsize
712 # Clamp to a multiple of self.targetsize
711 targetsize = self.targetsize * ((l // self.targetsize) + 1)
713 targetsize = self.targetsize * ((l // self.targetsize) + 1)
712 collector = cStringIO.StringIO()
714 collector = cStringIO.StringIO()
713 collector.write(self.buf)
715 collector.write(self.buf)
714 collected = len(self.buf)
716 collected = len(self.buf)
715 for chunk in self.in_iter:
717 for chunk in self.in_iter:
716 collector.write(chunk)
718 collector.write(chunk)
717 collected += len(chunk)
719 collected += len(chunk)
718 if collected >= targetsize:
720 if collected >= targetsize:
719 break
721 break
720 if collected < targetsize:
722 if collected < targetsize:
721 self.iterempty = True
723 self.iterempty = True
722 self.buf = collector.getvalue()
724 self.buf = collector.getvalue()
723 s, self.buf = self.buf[:l], buffer(self.buf, l)
725 s, self.buf = self.buf[:l], buffer(self.buf, l)
724 return s
726 return s
725
727
726 def filechunkiter(f, size = 65536):
728 def filechunkiter(f, size = 65536):
727 """Create a generator that produces all the data in the file size
729 """Create a generator that produces all the data in the file size
728 (default 65536) bytes at a time. Chunks may be less than size
730 (default 65536) bytes at a time. Chunks may be less than size
729 bytes if the chunk is the last chunk in the file, or the file is a
731 bytes if the chunk is the last chunk in the file, or the file is a
730 socket or some other type of file that sometimes reads less data
732 socket or some other type of file that sometimes reads less data
731 than is requested."""
733 than is requested."""
732 s = f.read(size)
734 s = f.read(size)
733 while len(s) > 0:
735 while len(s) > 0:
734 yield s
736 yield s
735 s = f.read(size)
737 s = f.read(size)
736
738
737 def makedate():
739 def makedate():
738 lt = time.localtime()
740 lt = time.localtime()
739 if lt[8] == 1 and time.daylight:
741 if lt[8] == 1 and time.daylight:
740 tz = time.altzone
742 tz = time.altzone
741 else:
743 else:
742 tz = time.timezone
744 tz = time.timezone
743 return time.mktime(lt), tz
745 return time.mktime(lt), tz
744
746
745 def datestr(date=None, format='%a %b %d %H:%M:%S %Y'):
747 def datestr(date=None, format='%a %b %d %H:%M:%S %Y'):
746 """represent a (unixtime, offset) tuple as a localized time.
748 """represent a (unixtime, offset) tuple as a localized time.
747 unixtime is seconds since the epoch, and offset is the time zone's
749 unixtime is seconds since the epoch, and offset is the time zone's
748 number of seconds away from UTC."""
750 number of seconds away from UTC."""
749 t, tz = date or makedate()
751 t, tz = date or makedate()
750 return ("%s %+03d%02d" %
752 return ("%s %+03d%02d" %
751 (time.strftime(format, time.gmtime(float(t) - tz)),
753 (time.strftime(format, time.gmtime(float(t) - tz)),
752 -tz / 3600,
754 -tz / 3600,
753 ((-tz % 3600) / 60)))
755 ((-tz % 3600) / 60)))
754
756
755 def shortuser(user):
757 def shortuser(user):
756 """Return a short representation of a user name or email address."""
758 """Return a short representation of a user name or email address."""
757 f = user.find('@')
759 f = user.find('@')
758 if f >= 0:
760 if f >= 0:
759 user = user[:f]
761 user = user[:f]
760 f = user.find('<')
762 f = user.find('<')
761 if f >= 0:
763 if f >= 0:
762 user = user[f+1:]
764 user = user[f+1:]
763 return user
765 return user
764
766
765 def walkrepos(path):
767 def walkrepos(path):
766 '''yield every hg repository under path, recursively.'''
768 '''yield every hg repository under path, recursively.'''
767 def errhandler(err):
769 def errhandler(err):
768 if err.filename == path:
770 if err.filename == path:
769 raise err
771 raise err
770
772
771 for root, dirs, files in os.walk(path, onerror=errhandler):
773 for root, dirs, files in os.walk(path, onerror=errhandler):
772 for d in dirs:
774 for d in dirs:
773 if d == '.hg':
775 if d == '.hg':
774 yield root
776 yield root
775 dirs[:] = []
777 dirs[:] = []
776 break
778 break
777
779
778 _rcpath = None
780 _rcpath = None
779
781
780 def rcpath():
782 def rcpath():
781 '''return hgrc search path. if env var HGRCPATH is set, use it.
783 '''return hgrc search path. if env var HGRCPATH is set, use it.
782 for each item in path, if directory, use files ending in .rc,
784 for each item in path, if directory, use files ending in .rc,
783 else use item.
785 else use item.
784 make HGRCPATH empty to only look in .hg/hgrc of current repo.
786 make HGRCPATH empty to only look in .hg/hgrc of current repo.
785 if no HGRCPATH, use default os-specific path.'''
787 if no HGRCPATH, use default os-specific path.'''
786 global _rcpath
788 global _rcpath
787 if _rcpath is None:
789 if _rcpath is None:
788 if 'HGRCPATH' in os.environ:
790 if 'HGRCPATH' in os.environ:
789 _rcpath = []
791 _rcpath = []
790 for p in os.environ['HGRCPATH'].split(os.pathsep):
792 for p in os.environ['HGRCPATH'].split(os.pathsep):
791 if not p: continue
793 if not p: continue
792 if os.path.isdir(p):
794 if os.path.isdir(p):
793 for f in os.listdir(p):
795 for f in os.listdir(p):
794 if f.endswith('.rc'):
796 if f.endswith('.rc'):
795 _rcpath.append(os.path.join(p, f))
797 _rcpath.append(os.path.join(p, f))
796 else:
798 else:
797 _rcpath.append(p)
799 _rcpath.append(p)
798 else:
800 else:
799 _rcpath = os_rcpath()
801 _rcpath = os_rcpath()
800 return _rcpath
802 return _rcpath
@@ -1,181 +1,211 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init
3 hg init
4 mkdir d1 d1/d11 d2
4 mkdir d1 d1/d11 d2
5 echo d1/a > d1/a
5 echo d1/a > d1/a
6 echo d1/ba > d1/ba
6 echo d1/ba > d1/ba
7 echo d1/a1 > d1/d11/a1
7 echo d1/a1 > d1/d11/a1
8 echo d1/b > d1/b
8 echo d1/b > d1/b
9 echo d2/b > d2/b
9 echo d2/b > d2/b
10 hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
10 hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
11 hg commit -m "1" -d "1000000 0"
11 hg commit -m "1" -d "1000000 0"
12
12
13 echo "# rename a single file"
13 echo "# rename a single file"
14 hg rename d1/d11/a1 d2/c
14 hg rename d1/d11/a1 d2/c
15 hg status
15 hg status
16 hg update -C
16 hg update -C
17
17
18 echo "# rename --after a single file"
18 echo "# rename --after a single file"
19 mv d1/d11/a1 d2/c
19 mv d1/d11/a1 d2/c
20 hg rename --after d1/d11/a1 d2/c
20 hg rename --after d1/d11/a1 d2/c
21 hg status
21 hg status
22 hg update -C
22 hg update -C
23
23
24 echo "# move a single file to an existing directory"
24 echo "# move a single file to an existing directory"
25 hg rename d1/d11/a1 d2
25 hg rename d1/d11/a1 d2
26 hg status
26 hg status
27 hg update -C
27 hg update -C
28
28
29 echo "# move --after a single file to an existing directory"
29 echo "# move --after a single file to an existing directory"
30 mv d1/d11/a1 d2
30 mv d1/d11/a1 d2
31 hg rename --after d1/d11/a1 d2
31 hg rename --after d1/d11/a1 d2
32 hg status
32 hg status
33 hg update -C
33 hg update -C
34
34
35 echo "# rename a file using a relative path"
35 echo "# rename a file using a relative path"
36 (cd d1/d11; hg rename ../../d2/b e)
36 (cd d1/d11; hg rename ../../d2/b e)
37 hg status
37 hg status
38 hg update -C
38 hg update -C
39
39
40 echo "# rename --after a file using a relative path"
40 echo "# rename --after a file using a relative path"
41 (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
41 (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
42 hg status
42 hg status
43 hg update -C
43 hg update -C
44
44
45 echo "# rename directory d1 as d3"
45 echo "# rename directory d1 as d3"
46 hg rename d1/ d3
46 hg rename d1/ d3
47 hg status
47 hg status
48 hg update -C
48 hg update -C
49
49
50 echo "# rename --after directory d1 as d3"
50 echo "# rename --after directory d1 as d3"
51 mv d1 d3
51 mv d1 d3
52 hg rename --after d1 d3
52 hg rename --after d1 d3
53 hg status
53 hg status
54 hg update -C
54 hg update -C
55
55
56 echo "# move a directory using a relative path"
56 echo "# move a directory using a relative path"
57 (cd d2; mkdir d3; hg rename ../d1/d11 d3)
57 (cd d2; mkdir d3; hg rename ../d1/d11 d3)
58 hg status
58 hg status
59 hg update -C
59 hg update -C
60
60
61 echo "# move --after a directory using a relative path"
61 echo "# move --after a directory using a relative path"
62 (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
62 (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
63 hg status
63 hg status
64 hg update -C
64 hg update -C
65
65
66 echo "# move directory d1/d11 to an existing directory d2 (removes empty d1)"
66 echo "# move directory d1/d11 to an existing directory d2 (removes empty d1)"
67 hg rename d1/d11/ d2
67 hg rename d1/d11/ d2
68 hg status
68 hg status
69 hg update -C
69 hg update -C
70
70
71 echo "# move directories d1 and d2 to a new directory d3"
71 echo "# move directories d1 and d2 to a new directory d3"
72 mkdir d3
72 mkdir d3
73 hg rename d1 d2 d3
73 hg rename d1 d2 d3
74 hg status
74 hg status
75 hg update -C
75 hg update -C
76
76
77 echo "# move --after directories d1 and d2 to a new directory d3"
77 echo "# move --after directories d1 and d2 to a new directory d3"
78 mkdir d3
78 mkdir d3
79 mv d1 d2 d3
79 mv d1 d2 d3
80 hg rename --after d1 d2 d3
80 hg rename --after d1 d2 d3
81 hg status
81 hg status
82 hg update -C
82 hg update -C
83
83
84 echo "# move everything under directory d1 to existing directory d2, do not"
84 echo "# move everything under directory d1 to existing directory d2, do not"
85 echo "# overwrite existing files (d2/b)"
85 echo "# overwrite existing files (d2/b)"
86 hg rename d1/* d2
86 hg rename d1/* d2
87 hg status
87 hg status
88 diff d1/b d2/b
88 diff d1/b d2/b
89 hg update -C
89 hg update -C
90
90
91 echo "# attempt to move potentially more than one file into a non-existent"
91 echo "# attempt to move potentially more than one file into a non-existent"
92 echo "# directory"
92 echo "# directory"
93 hg rename 'glob:d1/**' dx
93 hg rename 'glob:d1/**' dx
94
94
95 echo "# move every file under d1 to d2/d21 (glob)"
95 echo "# move every file under d1 to d2/d21 (glob)"
96 mkdir d2/d21
96 mkdir d2/d21
97 hg rename 'glob:d1/**' d2/d21
97 hg rename 'glob:d1/**' d2/d21
98 hg status
98 hg status
99 hg update -C
99 hg update -C
100
100
101 echo "# move --after some files under d1 to d2/d21 (glob)"
101 echo "# move --after some files under d1 to d2/d21 (glob)"
102 mkdir d2/d21
102 mkdir d2/d21
103 mv d1/a d1/d11/a1 d2/d21
103 mv d1/a d1/d11/a1 d2/d21
104 hg rename --after 'glob:d1/**' d2/d21
104 hg rename --after 'glob:d1/**' d2/d21
105 hg status
105 hg status
106 hg update -C
106 hg update -C
107
107
108 echo "# move every file under d1 starting with an 'a' to d2/d21 (regexp)"
108 echo "# move every file under d1 starting with an 'a' to d2/d21 (regexp)"
109 mkdir d2/d21
109 mkdir d2/d21
110 hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
110 hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
111 hg status
111 hg status
112 hg update -C
112 hg update -C
113
113
114 echo "# attempt to overwrite an existing file"
114 echo "# attempt to overwrite an existing file"
115 echo "ca" > d1/ca
115 echo "ca" > d1/ca
116 hg rename d1/ba d1/ca
116 hg rename d1/ba d1/ca
117 hg status
117 hg status
118 hg update -C
118 hg update -C
119
119
120 echo "# forced overwrite of an existing file"
120 echo "# forced overwrite of an existing file"
121 echo "ca" > d1/ca
121 echo "ca" > d1/ca
122 hg rename --force d1/ba d1/ca
122 hg rename --force d1/ba d1/ca
123 hg status
123 hg status
124 hg update -C
124 hg update -C
125
125
126 echo "# replace a symlink with a file"
126 echo "# replace a symlink with a file"
127 ln -s ba d1/ca
127 ln -s ba d1/ca
128 hg rename --force d1/ba d1/ca
128 hg rename --force d1/ba d1/ca
129 hg status
129 hg status
130 hg update -C
130 hg update -C
131
131
132 echo "# do not copy more than one source file to the same destination file"
132 echo "# do not copy more than one source file to the same destination file"
133 mkdir d3
133 mkdir d3
134 hg rename d1/* d2/* d3
134 hg rename d1/* d2/* d3
135 hg status
135 hg status
136 hg update -C
136 hg update -C
137
137
138 echo "# move a whole subtree with \"hg rename .\""
138 echo "# move a whole subtree with \"hg rename .\""
139 mkdir d3
139 mkdir d3
140 (cd d1; hg rename . ../d3)
140 (cd d1; hg rename . ../d3)
141 hg status
141 hg status
142 hg update -C
142 hg update -C
143
143
144 echo "# move a whole subtree with \"hg rename --after .\""
144 echo "# move a whole subtree with \"hg rename --after .\""
145 mkdir d3
145 mkdir d3
146 mv d1/* d3
146 mv d1/* d3
147 (cd d1; hg rename --after . ../d3)
147 (cd d1; hg rename --after . ../d3)
148 hg status
148 hg status
149 hg update -C
149 hg update -C
150
150
151 echo "# move the parent tree with \"hg rename ..\""
151 echo "# move the parent tree with \"hg rename ..\""
152 (cd d1/d11; hg rename .. ../../d3)
152 (cd d1/d11; hg rename .. ../../d3)
153 hg status
153 hg status
154 hg update -C
154 hg update -C
155
155
156 echo "# skip removed files"
156 echo "# skip removed files"
157 hg remove d1/b
157 hg remove d1/b
158 hg rename d1 d3
158 hg rename d1 d3
159 hg status
159 hg status
160 hg update -C
160 hg update -C
161
161
162 echo "# transitive rename"
162 echo "# transitive rename"
163 hg rename d1/b d1/bb
163 hg rename d1/b d1/bb
164 hg rename d1/bb d1/bc
164 hg rename d1/bb d1/bc
165 hg status
165 hg status
166 hg update -C
166 hg update -C
167
167
168 echo "# transitive rename --after"
168 echo "# transitive rename --after"
169 hg rename d1/b d1/bb
169 hg rename d1/b d1/bb
170 mv d1/bb d1/bc
170 mv d1/bb d1/bc
171 hg rename --after d1/bb d1/bc
171 hg rename --after d1/bb d1/bc
172 hg status
172 hg status
173 hg update -C
173 hg update -C
174
174
175 echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
175 echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
176 hg rename d1/b d1/bb
176 hg rename d1/b d1/bb
177 echo "some stuff added to d1/bb" >> d1/bb
177 echo "some stuff added to d1/bb" >> d1/bb
178 hg rename d1/bb d1/b
178 hg rename d1/bb d1/b
179 hg status
179 hg status
180 hg debugstate | grep copy
180 hg debugstate | grep copy
181 hg update -C
181 hg update -C
182
183 echo "# check illegal path components"
184
185 hg rename d1/d11/a1 .hg/foo
186 hg status
187 hg rename d1/d11/a1 ../foo
188 hg status
189
190 mv d1/d11/a1 .hg/foo
191 hg rename --after d1/d11/a1 .hg/foo
192 hg status
193 hg update -C
194 rm .hg/foo
195
196 hg rename d1/d11/a1 .hg
197 hg status
198 hg rename d1/d11/a1 ..
199 hg status
200
201 mv d1/d11/a1 .hg
202 hg rename --after d1/d11/a1 .hg
203 hg status
204 hg update -C
205 rm .hg/a1
206
207 (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
208 hg status
209 (cd d1/d11; hg rename ../../d2/b ../../../foo)
210 hg status
211
@@ -1,256 +1,272 b''
1 # rename a single file
1 # rename a single file
2 A d2/c
2 A d2/c
3 R d1/d11/a1
3 R d1/d11/a1
4 # rename --after a single file
4 # rename --after a single file
5 A d2/c
5 A d2/c
6 R d1/d11/a1
6 R d1/d11/a1
7 # move a single file to an existing directory
7 # move a single file to an existing directory
8 A d2/a1
8 A d2/a1
9 R d1/d11/a1
9 R d1/d11/a1
10 # move --after a single file to an existing directory
10 # move --after a single file to an existing directory
11 A d2/a1
11 A d2/a1
12 R d1/d11/a1
12 R d1/d11/a1
13 # rename a file using a relative path
13 # rename a file using a relative path
14 A d1/d11/e
14 A d1/d11/e
15 R d2/b
15 R d2/b
16 # rename --after a file using a relative path
16 # rename --after a file using a relative path
17 A d1/d11/e
17 A d1/d11/e
18 R d2/b
18 R d2/b
19 # rename directory d1 as d3
19 # rename directory d1 as d3
20 copying d1/a to d3/a
20 copying d1/a to d3/a
21 copying d1/b to d3/b
21 copying d1/b to d3/b
22 copying d1/ba to d3/ba
22 copying d1/ba to d3/ba
23 copying d1/d11/a1 to d3/d11/a1
23 copying d1/d11/a1 to d3/d11/a1
24 removing d1/a
24 removing d1/a
25 removing d1/b
25 removing d1/b
26 removing d1/ba
26 removing d1/ba
27 removing d1/d11/a1
27 removing d1/d11/a1
28 A d3/a
28 A d3/a
29 A d3/b
29 A d3/b
30 A d3/ba
30 A d3/ba
31 A d3/d11/a1
31 A d3/d11/a1
32 R d1/a
32 R d1/a
33 R d1/b
33 R d1/b
34 R d1/ba
34 R d1/ba
35 R d1/d11/a1
35 R d1/d11/a1
36 # rename --after directory d1 as d3
36 # rename --after directory d1 as d3
37 copying d1/a to d3/a
37 copying d1/a to d3/a
38 copying d1/b to d3/b
38 copying d1/b to d3/b
39 copying d1/ba to d3/ba
39 copying d1/ba to d3/ba
40 copying d1/d11/a1 to d3/d11/a1
40 copying d1/d11/a1 to d3/d11/a1
41 removing d1/a
41 removing d1/a
42 removing d1/b
42 removing d1/b
43 removing d1/ba
43 removing d1/ba
44 removing d1/d11/a1
44 removing d1/d11/a1
45 A d3/a
45 A d3/a
46 A d3/b
46 A d3/b
47 A d3/ba
47 A d3/ba
48 A d3/d11/a1
48 A d3/d11/a1
49 R d1/a
49 R d1/a
50 R d1/b
50 R d1/b
51 R d1/ba
51 R d1/ba
52 R d1/d11/a1
52 R d1/d11/a1
53 # move a directory using a relative path
53 # move a directory using a relative path
54 copying ../d1/d11/a1 to d3/d11/a1
54 copying ../d1/d11/a1 to d3/d11/a1
55 removing ../d1/d11/a1
55 removing ../d1/d11/a1
56 A d2/d3/d11/a1
56 A d2/d3/d11/a1
57 R d1/d11/a1
57 R d1/d11/a1
58 # move --after a directory using a relative path
58 # move --after a directory using a relative path
59 copying ../d1/d11/a1 to d3/d11/a1
59 copying ../d1/d11/a1 to d3/d11/a1
60 removing ../d1/d11/a1
60 removing ../d1/d11/a1
61 A d2/d3/d11/a1
61 A d2/d3/d11/a1
62 R d1/d11/a1
62 R d1/d11/a1
63 # move directory d1/d11 to an existing directory d2 (removes empty d1)
63 # move directory d1/d11 to an existing directory d2 (removes empty d1)
64 copying d1/d11/a1 to d2/d11/a1
64 copying d1/d11/a1 to d2/d11/a1
65 removing d1/d11/a1
65 removing d1/d11/a1
66 A d2/d11/a1
66 A d2/d11/a1
67 R d1/d11/a1
67 R d1/d11/a1
68 # move directories d1 and d2 to a new directory d3
68 # move directories d1 and d2 to a new directory d3
69 copying d1/a to d3/d1/a
69 copying d1/a to d3/d1/a
70 copying d1/b to d3/d1/b
70 copying d1/b to d3/d1/b
71 copying d1/ba to d3/d1/ba
71 copying d1/ba to d3/d1/ba
72 copying d1/d11/a1 to d3/d1/d11/a1
72 copying d1/d11/a1 to d3/d1/d11/a1
73 copying d2/b to d3/d2/b
73 copying d2/b to d3/d2/b
74 removing d1/a
74 removing d1/a
75 removing d1/b
75 removing d1/b
76 removing d1/ba
76 removing d1/ba
77 removing d1/d11/a1
77 removing d1/d11/a1
78 removing d2/b
78 removing d2/b
79 A d3/d1/a
79 A d3/d1/a
80 A d3/d1/b
80 A d3/d1/b
81 A d3/d1/ba
81 A d3/d1/ba
82 A d3/d1/d11/a1
82 A d3/d1/d11/a1
83 A d3/d2/b
83 A d3/d2/b
84 R d1/a
84 R d1/a
85 R d1/b
85 R d1/b
86 R d1/ba
86 R d1/ba
87 R d1/d11/a1
87 R d1/d11/a1
88 R d2/b
88 R d2/b
89 # move --after directories d1 and d2 to a new directory d3
89 # move --after directories d1 and d2 to a new directory d3
90 copying d1/a to d3/d1/a
90 copying d1/a to d3/d1/a
91 copying d1/b to d3/d1/b
91 copying d1/b to d3/d1/b
92 copying d1/ba to d3/d1/ba
92 copying d1/ba to d3/d1/ba
93 copying d1/d11/a1 to d3/d1/d11/a1
93 copying d1/d11/a1 to d3/d1/d11/a1
94 copying d2/b to d3/d2/b
94 copying d2/b to d3/d2/b
95 removing d1/a
95 removing d1/a
96 removing d1/b
96 removing d1/b
97 removing d1/ba
97 removing d1/ba
98 removing d1/d11/a1
98 removing d1/d11/a1
99 removing d2/b
99 removing d2/b
100 A d3/d1/a
100 A d3/d1/a
101 A d3/d1/b
101 A d3/d1/b
102 A d3/d1/ba
102 A d3/d1/ba
103 A d3/d1/d11/a1
103 A d3/d1/d11/a1
104 A d3/d2/b
104 A d3/d2/b
105 R d1/a
105 R d1/a
106 R d1/b
106 R d1/b
107 R d1/ba
107 R d1/ba
108 R d1/d11/a1
108 R d1/d11/a1
109 R d2/b
109 R d2/b
110 # move everything under directory d1 to existing directory d2, do not
110 # move everything under directory d1 to existing directory d2, do not
111 # overwrite existing files (d2/b)
111 # overwrite existing files (d2/b)
112 d2/b: not overwriting - file exists
112 d2/b: not overwriting - file exists
113 copying d1/d11/a1 to d2/d11/a1
113 copying d1/d11/a1 to d2/d11/a1
114 removing d1/d11/a1
114 removing d1/d11/a1
115 A d2/a
115 A d2/a
116 A d2/ba
116 A d2/ba
117 A d2/d11/a1
117 A d2/d11/a1
118 R d1/a
118 R d1/a
119 R d1/ba
119 R d1/ba
120 R d1/d11/a1
120 R d1/d11/a1
121 1c1
121 1c1
122 < d1/b
122 < d1/b
123 ---
123 ---
124 > d2/b
124 > d2/b
125 # attempt to move potentially more than one file into a non-existent
125 # attempt to move potentially more than one file into a non-existent
126 # directory
126 # directory
127 abort: with multiple sources, destination must be an existing directory
127 abort: with multiple sources, destination must be an existing directory
128 # move every file under d1 to d2/d21 (glob)
128 # move every file under d1 to d2/d21 (glob)
129 copying d1/a to d2/d21/a
129 copying d1/a to d2/d21/a
130 copying d1/b to d2/d21/b
130 copying d1/b to d2/d21/b
131 copying d1/ba to d2/d21/ba
131 copying d1/ba to d2/d21/ba
132 copying d1/d11/a1 to d2/d21/a1
132 copying d1/d11/a1 to d2/d21/a1
133 removing d1/a
133 removing d1/a
134 removing d1/b
134 removing d1/b
135 removing d1/ba
135 removing d1/ba
136 removing d1/d11/a1
136 removing d1/d11/a1
137 A d2/d21/a
137 A d2/d21/a
138 A d2/d21/a1
138 A d2/d21/a1
139 A d2/d21/b
139 A d2/d21/b
140 A d2/d21/ba
140 A d2/d21/ba
141 R d1/a
141 R d1/a
142 R d1/b
142 R d1/b
143 R d1/ba
143 R d1/ba
144 R d1/d11/a1
144 R d1/d11/a1
145 # move --after some files under d1 to d2/d21 (glob)
145 # move --after some files under d1 to d2/d21 (glob)
146 copying d1/a to d2/d21/a
146 copying d1/a to d2/d21/a
147 copying d1/d11/a1 to d2/d21/a1
147 copying d1/d11/a1 to d2/d21/a1
148 removing d1/a
148 removing d1/a
149 removing d1/d11/a1
149 removing d1/d11/a1
150 A d2/d21/a
150 A d2/d21/a
151 A d2/d21/a1
151 A d2/d21/a1
152 R d1/a
152 R d1/a
153 R d1/d11/a1
153 R d1/d11/a1
154 # move every file under d1 starting with an 'a' to d2/d21 (regexp)
154 # move every file under d1 starting with an 'a' to d2/d21 (regexp)
155 copying d1/a to d2/d21/a
155 copying d1/a to d2/d21/a
156 copying d1/d11/a1 to d2/d21/a1
156 copying d1/d11/a1 to d2/d21/a1
157 removing d1/a
157 removing d1/a
158 removing d1/d11/a1
158 removing d1/d11/a1
159 A d2/d21/a
159 A d2/d21/a
160 A d2/d21/a1
160 A d2/d21/a1
161 R d1/a
161 R d1/a
162 R d1/d11/a1
162 R d1/d11/a1
163 # attempt to overwrite an existing file
163 # attempt to overwrite an existing file
164 d1/ca: not overwriting - file exists
164 d1/ca: not overwriting - file exists
165 ? d1/ca
165 ? d1/ca
166 # forced overwrite of an existing file
166 # forced overwrite of an existing file
167 A d1/ca
167 A d1/ca
168 R d1/ba
168 R d1/ba
169 # replace a symlink with a file
169 # replace a symlink with a file
170 A d1/ca
170 A d1/ca
171 R d1/ba
171 R d1/ba
172 # do not copy more than one source file to the same destination file
172 # do not copy more than one source file to the same destination file
173 copying d1/d11/a1 to d3/d11/a1
173 copying d1/d11/a1 to d3/d11/a1
174 d3/b: not overwriting - d2/b collides with d1/b
174 d3/b: not overwriting - d2/b collides with d1/b
175 removing d1/d11/a1
175 removing d1/d11/a1
176 A d3/a
176 A d3/a
177 A d3/b
177 A d3/b
178 A d3/ba
178 A d3/ba
179 A d3/d11/a1
179 A d3/d11/a1
180 R d1/a
180 R d1/a
181 R d1/b
181 R d1/b
182 R d1/ba
182 R d1/ba
183 R d1/d11/a1
183 R d1/d11/a1
184 # move a whole subtree with "hg rename ."
184 # move a whole subtree with "hg rename ."
185 copying a to ../d3/d1/a
185 copying a to ../d3/d1/a
186 copying b to ../d3/d1/b
186 copying b to ../d3/d1/b
187 copying ba to ../d3/d1/ba
187 copying ba to ../d3/d1/ba
188 copying d11/a1 to ../d3/d1/d11/a1
188 copying d11/a1 to ../d3/d1/d11/a1
189 removing a
189 removing a
190 removing b
190 removing b
191 removing ba
191 removing ba
192 removing d11/a1
192 removing d11/a1
193 A d3/d1/a
193 A d3/d1/a
194 A d3/d1/b
194 A d3/d1/b
195 A d3/d1/ba
195 A d3/d1/ba
196 A d3/d1/d11/a1
196 A d3/d1/d11/a1
197 R d1/a
197 R d1/a
198 R d1/b
198 R d1/b
199 R d1/ba
199 R d1/ba
200 R d1/d11/a1
200 R d1/d11/a1
201 # move a whole subtree with "hg rename --after ."
201 # move a whole subtree with "hg rename --after ."
202 copying a to ../d3/a
202 copying a to ../d3/a
203 copying b to ../d3/b
203 copying b to ../d3/b
204 copying ba to ../d3/ba
204 copying ba to ../d3/ba
205 copying d11/a1 to ../d3/d11/a1
205 copying d11/a1 to ../d3/d11/a1
206 removing a
206 removing a
207 removing b
207 removing b
208 removing ba
208 removing ba
209 removing d11/a1
209 removing d11/a1
210 A d3/a
210 A d3/a
211 A d3/b
211 A d3/b
212 A d3/ba
212 A d3/ba
213 A d3/d11/a1
213 A d3/d11/a1
214 R d1/a
214 R d1/a
215 R d1/b
215 R d1/b
216 R d1/ba
216 R d1/ba
217 R d1/d11/a1
217 R d1/d11/a1
218 # move the parent tree with "hg rename .."
218 # move the parent tree with "hg rename .."
219 copying ../a to ../../d3/a
219 copying ../a to ../../d3/a
220 copying ../b to ../../d3/b
220 copying ../b to ../../d3/b
221 copying ../ba to ../../d3/ba
221 copying ../ba to ../../d3/ba
222 copying a1 to ../../d3/d11/a1
222 copying a1 to ../../d3/d11/a1
223 removing ../a
223 removing ../a
224 removing ../b
224 removing ../b
225 removing ../ba
225 removing ../ba
226 removing a1
226 removing a1
227 A d3/a
227 A d3/a
228 A d3/b
228 A d3/b
229 A d3/ba
229 A d3/ba
230 A d3/d11/a1
230 A d3/d11/a1
231 R d1/a
231 R d1/a
232 R d1/b
232 R d1/b
233 R d1/ba
233 R d1/ba
234 R d1/d11/a1
234 R d1/d11/a1
235 # skip removed files
235 # skip removed files
236 copying d1/a to d3/a
236 copying d1/a to d3/a
237 copying d1/ba to d3/ba
237 copying d1/ba to d3/ba
238 copying d1/d11/a1 to d3/d11/a1
238 copying d1/d11/a1 to d3/d11/a1
239 removing d1/a
239 removing d1/a
240 removing d1/ba
240 removing d1/ba
241 removing d1/d11/a1
241 removing d1/d11/a1
242 A d3/a
242 A d3/a
243 A d3/ba
243 A d3/ba
244 A d3/d11/a1
244 A d3/d11/a1
245 R d1/a
245 R d1/a
246 R d1/b
246 R d1/b
247 R d1/ba
247 R d1/ba
248 R d1/d11/a1
248 R d1/d11/a1
249 # transitive rename
249 # transitive rename
250 A d1/bc
250 A d1/bc
251 R d1/b
251 R d1/b
252 # transitive rename --after
252 # transitive rename --after
253 A d1/bc
253 A d1/bc
254 R d1/b
254 R d1/b
255 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
255 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
256 M d1/b
256 M d1/b
257 # check illegal path components
258 abort: path contains illegal component: .hg/foo
259
260 abort: ../foo not under root
261 abort: path contains illegal component: .hg/foo
262
263 ! d1/d11/a1
264 abort: path contains illegal component: .hg/a1
265
266 abort: ../a1 not under root
267 abort: path contains illegal component: .hg/a1
268
269 ! d1/d11/a1
270 abort: path contains illegal component: .hg/foo
271
272 abort: ../../../foo not under root
@@ -1,55 +1,61 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 mkdir t
3 mkdir t
4 cd t
4 cd t
5 hg init
5 hg init
6 mkdir -p beans
6 mkdir -p beans
7 for b in kidney navy turtle borlotti black pinto; do
7 for b in kidney navy turtle borlotti black pinto; do
8 echo $b > beans/$b
8 echo $b > beans/$b
9 done
9 done
10 mkdir -p mammals/Procyonidae
10 mkdir -p mammals/Procyonidae
11 for m in cacomistle coatimundi raccoon; do
11 for m in cacomistle coatimundi raccoon; do
12 echo $m > mammals/Procyonidae/$m
12 echo $m > mammals/Procyonidae/$m
13 done
13 done
14 echo skunk > mammals/skunk
14 echo skunk > mammals/skunk
15 echo fennel > fennel
15 echo fennel > fennel
16 echo fenugreek > fenugreek
16 echo fenugreek > fenugreek
17 echo fiddlehead > fiddlehead
17 echo fiddlehead > fiddlehead
18 echo glob:glob > glob:glob
18 echo glob:glob > glob:glob
19 hg addremove
19 hg addremove
20 hg commit -m "commit #0" -d "1000000 0"
20 hg commit -m "commit #0" -d "1000000 0"
21 hg debugwalk
21 hg debugwalk
22 cd mammals
22 cd mammals
23 hg debugwalk .
23 hg debugwalk .
24 hg debugwalk Procyonidae
24 hg debugwalk Procyonidae
25 cd Procyonidae
25 cd Procyonidae
26 hg debugwalk .
26 hg debugwalk .
27 hg debugwalk ..
27 hg debugwalk ..
28 cd ..
28 cd ..
29 hg debugwalk ../beans
29 hg debugwalk ../beans
30 hg debugwalk .
30 hg debugwalk .
31 hg debugwalk .hg
32 hg debugwalk ../.hg
31 cd ..
33 cd ..
32 hg debugwalk -Ibeans
34 hg debugwalk -Ibeans
33 hg debugwalk 'glob:mammals/../beans/b*'
35 hg debugwalk 'glob:mammals/../beans/b*'
34 hg debugwalk '-X*/Procyonidae' mammals
36 hg debugwalk '-X*/Procyonidae' mammals
35 hg debugwalk path:mammals
37 hg debugwalk path:mammals
36 hg debugwalk ..
38 hg debugwalk ..
37 hg debugwalk beans/../..
39 hg debugwalk beans/../..
40 hg debugwalk .hg
41 hg debugwalk beans/../.hg
42 hg debugwalk beans/../.hg/data
43 hg debugwalk beans/.hg
38 # Don't know how to test absolute paths without always getting a false
44 # Don't know how to test absolute paths without always getting a false
39 # error.
45 # error.
40 #hg debugwalk `pwd`/beans
46 #hg debugwalk `pwd`/beans
41 #hg debugwalk `pwd`/..
47 #hg debugwalk `pwd`/..
42 hg debugwalk glob:\*
48 hg debugwalk glob:\*
43 hg debugwalk 're:.*[kb]$'
49 hg debugwalk 're:.*[kb]$'
44 hg debugwalk path:beans/black
50 hg debugwalk path:beans/black
45 hg debugwalk beans 'glob:beans/*'
51 hg debugwalk beans 'glob:beans/*'
46 hg debugwalk 'glob:j*'
52 hg debugwalk 'glob:j*'
47 hg debugwalk NOEXIST
53 hg debugwalk NOEXIST
48 mkfifo fifo
54 mkfifo fifo
49 hg debugwalk fifo
55 hg debugwalk fifo
50 rm fenugreek
56 rm fenugreek
51 hg debugwalk fenugreek
57 hg debugwalk fenugreek
52 hg rm fenugreek
58 hg rm fenugreek
53 hg debugwalk fenugreek
59 hg debugwalk fenugreek
54 touch new
60 touch new
55 hg debugwalk new
61 hg debugwalk new
@@ -1,87 +1,97 b''
1 adding beans/black
1 adding beans/black
2 adding beans/borlotti
2 adding beans/borlotti
3 adding beans/kidney
3 adding beans/kidney
4 adding beans/navy
4 adding beans/navy
5 adding beans/pinto
5 adding beans/pinto
6 adding beans/turtle
6 adding beans/turtle
7 adding fennel
7 adding fennel
8 adding fenugreek
8 adding fenugreek
9 adding fiddlehead
9 adding fiddlehead
10 adding glob:glob
10 adding glob:glob
11 adding mammals/Procyonidae/cacomistle
11 adding mammals/Procyonidae/cacomistle
12 adding mammals/Procyonidae/coatimundi
12 adding mammals/Procyonidae/coatimundi
13 adding mammals/Procyonidae/raccoon
13 adding mammals/Procyonidae/raccoon
14 adding mammals/skunk
14 adding mammals/skunk
15 f beans/black beans/black
15 f beans/black beans/black
16 f beans/borlotti beans/borlotti
16 f beans/borlotti beans/borlotti
17 f beans/kidney beans/kidney
17 f beans/kidney beans/kidney
18 f beans/navy beans/navy
18 f beans/navy beans/navy
19 f beans/pinto beans/pinto
19 f beans/pinto beans/pinto
20 f beans/turtle beans/turtle
20 f beans/turtle beans/turtle
21 f fennel fennel
21 f fennel fennel
22 f fenugreek fenugreek
22 f fenugreek fenugreek
23 f fiddlehead fiddlehead
23 f fiddlehead fiddlehead
24 f glob:glob glob:glob
24 f glob:glob glob:glob
25 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
25 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
26 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
26 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
27 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
27 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
28 f mammals/skunk mammals/skunk
28 f mammals/skunk mammals/skunk
29 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
29 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
30 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
30 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
31 f mammals/Procyonidae/raccoon Procyonidae/raccoon
31 f mammals/Procyonidae/raccoon Procyonidae/raccoon
32 f mammals/skunk skunk
32 f mammals/skunk skunk
33 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
33 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
34 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
34 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
35 f mammals/Procyonidae/raccoon Procyonidae/raccoon
35 f mammals/Procyonidae/raccoon Procyonidae/raccoon
36 f mammals/Procyonidae/cacomistle cacomistle
36 f mammals/Procyonidae/cacomistle cacomistle
37 f mammals/Procyonidae/coatimundi coatimundi
37 f mammals/Procyonidae/coatimundi coatimundi
38 f mammals/Procyonidae/raccoon raccoon
38 f mammals/Procyonidae/raccoon raccoon
39 f mammals/Procyonidae/cacomistle cacomistle
39 f mammals/Procyonidae/cacomistle cacomistle
40 f mammals/Procyonidae/coatimundi coatimundi
40 f mammals/Procyonidae/coatimundi coatimundi
41 f mammals/Procyonidae/raccoon raccoon
41 f mammals/Procyonidae/raccoon raccoon
42 f mammals/skunk ../skunk
42 f mammals/skunk ../skunk
43 f beans/black ../beans/black
43 f beans/black ../beans/black
44 f beans/borlotti ../beans/borlotti
44 f beans/borlotti ../beans/borlotti
45 f beans/kidney ../beans/kidney
45 f beans/kidney ../beans/kidney
46 f beans/navy ../beans/navy
46 f beans/navy ../beans/navy
47 f beans/pinto ../beans/pinto
47 f beans/pinto ../beans/pinto
48 f beans/turtle ../beans/turtle
48 f beans/turtle ../beans/turtle
49 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
49 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
50 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
50 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
51 f mammals/Procyonidae/raccoon Procyonidae/raccoon
51 f mammals/Procyonidae/raccoon Procyonidae/raccoon
52 f mammals/skunk skunk
52 f mammals/skunk skunk
53 .hg: No such file or directory
54 abort: path contains illegal component: .hg
55
53 f beans/black beans/black
56 f beans/black beans/black
54 f beans/borlotti beans/borlotti
57 f beans/borlotti beans/borlotti
55 f beans/kidney beans/kidney
58 f beans/kidney beans/kidney
56 f beans/navy beans/navy
59 f beans/navy beans/navy
57 f beans/pinto beans/pinto
60 f beans/pinto beans/pinto
58 f beans/turtle beans/turtle
61 f beans/turtle beans/turtle
59 f beans/black beans/black
62 f beans/black beans/black
60 f beans/borlotti beans/borlotti
63 f beans/borlotti beans/borlotti
61 f mammals/skunk mammals/skunk
64 f mammals/skunk mammals/skunk
62 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
65 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
63 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
66 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
64 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
67 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
65 f mammals/skunk mammals/skunk
68 f mammals/skunk mammals/skunk
66 abort: .. not under root
69 abort: .. not under root
67 abort: beans/../.. not under root
70 abort: beans/../.. not under root
71 abort: path contains illegal component: .hg
72
73 abort: path contains illegal component: .hg
74
75 abort: path contains illegal component: .hg/data
76
77 beans/.hg: No such file or directory
68 f fennel fennel
78 f fennel fennel
69 f fenugreek fenugreek
79 f fenugreek fenugreek
70 f fiddlehead fiddlehead
80 f fiddlehead fiddlehead
71 f glob:glob glob:glob
81 f glob:glob glob:glob
72 f beans/black beans/black
82 f beans/black beans/black
73 f fenugreek fenugreek
83 f fenugreek fenugreek
74 f glob:glob glob:glob
84 f glob:glob glob:glob
75 f mammals/skunk mammals/skunk
85 f mammals/skunk mammals/skunk
76 f beans/black beans/black
86 f beans/black beans/black
77 f beans/black beans/black
87 f beans/black beans/black
78 f beans/borlotti beans/borlotti
88 f beans/borlotti beans/borlotti
79 f beans/kidney beans/kidney
89 f beans/kidney beans/kidney
80 f beans/navy beans/navy
90 f beans/navy beans/navy
81 f beans/pinto beans/pinto
91 f beans/pinto beans/pinto
82 f beans/turtle beans/turtle
92 f beans/turtle beans/turtle
83 NOEXIST: No such file or directory
93 NOEXIST: No such file or directory
84 fifo: unsupported file type (type is fifo)
94 fifo: unsupported file type (type is fifo)
85 m fenugreek fenugreek exact
95 m fenugreek fenugreek exact
86 m fenugreek fenugreek exact
96 m fenugreek fenugreek exact
87 f new new exact
97 f new new exact
General Comments 0
You need to be logged in to leave comments. Login now