##// END OF EJS Templates
merge with crew
Benoit Boissinot -
r1958:f92cf4a8 merge default
parent child Browse files
Show More
@@ -1,800 +1,800 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 return pconvert(name[len(rootsep):])
189 elif name == root:
189 elif name == root:
190 return ''
190 return ''
191 else:
191 else:
192 raise Abort('%s not under root' % myname)
192 raise Abort('%s not under root' % myname)
193
193
194 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
194 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
195 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
195 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
196
196
197 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
197 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
198 if os.name == 'nt':
198 if os.name == 'nt':
199 dflt_pat = 'glob'
199 dflt_pat = 'glob'
200 else:
200 else:
201 dflt_pat = 'relpath'
201 dflt_pat = 'relpath'
202 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
202 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
203
203
204 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
204 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
205 """build a function to match a set of file patterns
205 """build a function to match a set of file patterns
206
206
207 arguments:
207 arguments:
208 canonroot - the canonical root of the tree you're matching against
208 canonroot - the canonical root of the tree you're matching against
209 cwd - the current working directory, if relevant
209 cwd - the current working directory, if relevant
210 names - patterns to find
210 names - patterns to find
211 inc - patterns to include
211 inc - patterns to include
212 exc - patterns to exclude
212 exc - patterns to exclude
213 head - a regex to prepend to patterns to control whether a match is rooted
213 head - a regex to prepend to patterns to control whether a match is rooted
214
214
215 a pattern is one of:
215 a pattern is one of:
216 'glob:<rooted glob>'
216 'glob:<rooted glob>'
217 're:<rooted regexp>'
217 're:<rooted regexp>'
218 'path:<rooted path>'
218 'path:<rooted path>'
219 'relglob:<relative glob>'
219 'relglob:<relative glob>'
220 'relpath:<relative path>'
220 'relpath:<relative path>'
221 'relre:<relative regexp>'
221 'relre:<relative regexp>'
222 '<rooted path or regexp>'
222 '<rooted path or regexp>'
223
223
224 returns:
224 returns:
225 a 3-tuple containing
225 a 3-tuple containing
226 - list of explicit non-pattern names passed in
226 - list of explicit non-pattern names passed in
227 - a bool match(filename) function
227 - a bool match(filename) function
228 - a bool indicating if any patterns were passed in
228 - a bool indicating if any patterns were passed in
229
229
230 todo:
230 todo:
231 make head regex a rooted bool
231 make head regex a rooted bool
232 """
232 """
233
233
234 def contains_glob(name):
234 def contains_glob(name):
235 for c in name:
235 for c in name:
236 if c in _globchars: return True
236 if c in _globchars: return True
237 return False
237 return False
238
238
239 def regex(kind, name, tail):
239 def regex(kind, name, tail):
240 '''convert a pattern into a regular expression'''
240 '''convert a pattern into a regular expression'''
241 if kind == 're':
241 if kind == 're':
242 return name
242 return name
243 elif kind == 'path':
243 elif kind == 'path':
244 return '^' + re.escape(name) + '(?:/|$)'
244 return '^' + re.escape(name) + '(?:/|$)'
245 elif kind == 'relglob':
245 elif kind == 'relglob':
246 return head + globre(name, '(?:|.*/)', tail)
246 return head + globre(name, '(?:|.*/)', tail)
247 elif kind == 'relpath':
247 elif kind == 'relpath':
248 return head + re.escape(name) + tail
248 return head + re.escape(name) + tail
249 elif kind == 'relre':
249 elif kind == 'relre':
250 if name.startswith('^'):
250 if name.startswith('^'):
251 return name
251 return name
252 return '.*' + name
252 return '.*' + name
253 return head + globre(name, '', tail)
253 return head + globre(name, '', tail)
254
254
255 def matchfn(pats, tail):
255 def matchfn(pats, tail):
256 """build a matching function from a set of patterns"""
256 """build a matching function from a set of patterns"""
257 if not pats:
257 if not pats:
258 return
258 return
259 matches = []
259 matches = []
260 for k, p in pats:
260 for k, p in pats:
261 try:
261 try:
262 pat = '(?:%s)' % regex(k, p, tail)
262 pat = '(?:%s)' % regex(k, p, tail)
263 matches.append(re.compile(pat).match)
263 matches.append(re.compile(pat).match)
264 except re.error:
264 except re.error:
265 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
265 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
266 else: raise Abort("invalid pattern (%s): %s" % (k, p))
266 else: raise Abort("invalid pattern (%s): %s" % (k, p))
267
267
268 def buildfn(text):
268 def buildfn(text):
269 for m in matches:
269 for m in matches:
270 r = m(text)
270 r = m(text)
271 if r:
271 if r:
272 return r
272 return r
273
273
274 return buildfn
274 return buildfn
275
275
276 def globprefix(pat):
276 def globprefix(pat):
277 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
277 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
278 root = []
278 root = []
279 for p in pat.split(os.sep):
279 for p in pat.split(os.sep):
280 if contains_glob(p): break
280 if contains_glob(p): break
281 root.append(p)
281 root.append(p)
282 return '/'.join(root)
282 return '/'.join(root)
283
283
284 pats = []
284 pats = []
285 files = []
285 files = []
286 roots = []
286 roots = []
287 for kind, name in [patkind(p, dflt_pat) for p in names]:
287 for kind, name in [patkind(p, dflt_pat) for p in names]:
288 if kind in ('glob', 'relpath'):
288 if kind in ('glob', 'relpath'):
289 name = canonpath(canonroot, cwd, name)
289 name = canonpath(canonroot, cwd, name)
290 if name == '':
290 if name == '':
291 kind, name = 'glob', '**'
291 kind, name = 'glob', '**'
292 if kind in ('glob', 'path', 're'):
292 if kind in ('glob', 'path', 're'):
293 pats.append((kind, name))
293 pats.append((kind, name))
294 if kind == 'glob':
294 if kind == 'glob':
295 root = globprefix(name)
295 root = globprefix(name)
296 if root: roots.append(root)
296 if root: roots.append(root)
297 elif kind == 'relpath':
297 elif kind == 'relpath':
298 files.append((kind, name))
298 files.append((kind, name))
299 roots.append(name)
299 roots.append(name)
300
300
301 patmatch = matchfn(pats, '$') or always
301 patmatch = matchfn(pats, '$') or always
302 filematch = matchfn(files, '(?:/|$)') or always
302 filematch = matchfn(files, '(?:/|$)') or always
303 incmatch = always
303 incmatch = always
304 if inc:
304 if inc:
305 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
305 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
306 excmatch = lambda fn: False
306 excmatch = lambda fn: False
307 if exc:
307 if exc:
308 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
308 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
309
309
310 return (roots,
310 return (roots,
311 lambda fn: (incmatch(fn) and not excmatch(fn) and
311 lambda fn: (incmatch(fn) and not excmatch(fn) and
312 (fn.endswith('/') or
312 (fn.endswith('/') or
313 (not pats and not files) or
313 (not pats and not files) or
314 (pats and patmatch(fn)) or
314 (pats and patmatch(fn)) or
315 (files and filematch(fn)))),
315 (files and filematch(fn)))),
316 (inc or exc or (pats and pats != [('glob', '**')])) and True)
316 (inc or exc or (pats and pats != [('glob', '**')])) and True)
317
317
318 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
318 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
319 '''enhanced shell command execution.
319 '''enhanced shell command execution.
320 run with environment maybe modified, maybe in different dir.
320 run with environment maybe modified, maybe in different dir.
321
321
322 if command fails and onerr is None, return status. if ui object,
322 if command fails and onerr is None, return status. if ui object,
323 print error message and return status, else raise onerr object as
323 print error message and return status, else raise onerr object as
324 exception.'''
324 exception.'''
325 oldenv = {}
325 oldenv = {}
326 for k in environ:
326 for k in environ:
327 oldenv[k] = os.environ.get(k)
327 oldenv[k] = os.environ.get(k)
328 if cwd is not None:
328 if cwd is not None:
329 oldcwd = os.getcwd()
329 oldcwd = os.getcwd()
330 try:
330 try:
331 for k, v in environ.iteritems():
331 for k, v in environ.iteritems():
332 os.environ[k] = str(v)
332 os.environ[k] = str(v)
333 if cwd is not None and oldcwd != cwd:
333 if cwd is not None and oldcwd != cwd:
334 os.chdir(cwd)
334 os.chdir(cwd)
335 rc = os.system(cmd)
335 rc = os.system(cmd)
336 if rc and onerr:
336 if rc and onerr:
337 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
337 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
338 explain_exit(rc)[0])
338 explain_exit(rc)[0])
339 if errprefix:
339 if errprefix:
340 errmsg = '%s: %s' % (errprefix, errmsg)
340 errmsg = '%s: %s' % (errprefix, errmsg)
341 try:
341 try:
342 onerr.warn(errmsg + '\n')
342 onerr.warn(errmsg + '\n')
343 except AttributeError:
343 except AttributeError:
344 raise onerr(errmsg)
344 raise onerr(errmsg)
345 return rc
345 return rc
346 finally:
346 finally:
347 for k, v in oldenv.iteritems():
347 for k, v in oldenv.iteritems():
348 if v is None:
348 if v is None:
349 del os.environ[k]
349 del os.environ[k]
350 else:
350 else:
351 os.environ[k] = v
351 os.environ[k] = v
352 if cwd is not None and oldcwd != cwd:
352 if cwd is not None and oldcwd != cwd:
353 os.chdir(oldcwd)
353 os.chdir(oldcwd)
354
354
355 def rename(src, dst):
355 def rename(src, dst):
356 """forcibly rename a file"""
356 """forcibly rename a file"""
357 try:
357 try:
358 os.rename(src, dst)
358 os.rename(src, dst)
359 except:
359 except:
360 os.unlink(dst)
360 os.unlink(dst)
361 os.rename(src, dst)
361 os.rename(src, dst)
362
362
363 def unlink(f):
363 def unlink(f):
364 """unlink and remove the directory if it is empty"""
364 """unlink and remove the directory if it is empty"""
365 os.unlink(f)
365 os.unlink(f)
366 # try removing directories that might now be empty
366 # try removing directories that might now be empty
367 try: os.removedirs(os.path.dirname(f))
367 try: os.removedirs(os.path.dirname(f))
368 except: pass
368 except: pass
369
369
370 def copyfiles(src, dst, hardlink=None):
370 def copyfiles(src, dst, hardlink=None):
371 """Copy a directory tree using hardlinks if possible"""
371 """Copy a directory tree using hardlinks if possible"""
372
372
373 if hardlink is None:
373 if hardlink is None:
374 hardlink = (os.stat(src).st_dev ==
374 hardlink = (os.stat(src).st_dev ==
375 os.stat(os.path.dirname(dst)).st_dev)
375 os.stat(os.path.dirname(dst)).st_dev)
376
376
377 if os.path.isdir(src):
377 if os.path.isdir(src):
378 os.mkdir(dst)
378 os.mkdir(dst)
379 for name in os.listdir(src):
379 for name in os.listdir(src):
380 srcname = os.path.join(src, name)
380 srcname = os.path.join(src, name)
381 dstname = os.path.join(dst, name)
381 dstname = os.path.join(dst, name)
382 copyfiles(srcname, dstname, hardlink)
382 copyfiles(srcname, dstname, hardlink)
383 else:
383 else:
384 if hardlink:
384 if hardlink:
385 try:
385 try:
386 os_link(src, dst)
386 os_link(src, dst)
387 except:
387 except:
388 hardlink = False
388 hardlink = False
389 shutil.copy(src, dst)
389 shutil.copy(src, dst)
390 else:
390 else:
391 shutil.copy(src, dst)
391 shutil.copy(src, dst)
392
392
393 def audit_path(path):
393 def audit_path(path):
394 """Abort if path contains dangerous components"""
394 """Abort if path contains dangerous components"""
395 parts = os.path.normcase(path).split(os.sep)
395 parts = os.path.normcase(path).split(os.sep)
396 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
396 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
397 or os.pardir in parts):
397 or os.pardir in parts):
398 raise Abort(_("path contains illegal component: %s\n") % path)
398 raise Abort(_("path contains illegal component: %s\n") % path)
399
399
400 def opener(base, audit=True):
400 def opener(base, audit=True):
401 """
401 """
402 return a function that opens files relative to base
402 return a function that opens files relative to base
403
403
404 this function is used to hide the details of COW semantics and
404 this function is used to hide the details of COW semantics and
405 remote file access from higher level code.
405 remote file access from higher level code.
406 """
406 """
407 p = base
407 p = base
408 audit_p = audit
408 audit_p = audit
409
409
410 def mktempcopy(name):
410 def mktempcopy(name):
411 d, fn = os.path.split(name)
411 d, fn = os.path.split(name)
412 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
412 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
413 fp = os.fdopen(fd, "wb")
413 fp = os.fdopen(fd, "wb")
414 try:
414 try:
415 fp.write(file(name, "rb").read())
415 fp.write(file(name, "rb").read())
416 except:
416 except:
417 try: os.unlink(temp)
417 try: os.unlink(temp)
418 except: pass
418 except: pass
419 raise
419 raise
420 fp.close()
420 fp.close()
421 st = os.lstat(name)
421 st = os.lstat(name)
422 os.chmod(temp, st.st_mode)
422 os.chmod(temp, st.st_mode)
423 return temp
423 return temp
424
424
425 class atomicfile(file):
425 class atomicfile(file):
426 """the file will only be copied on close"""
426 """the file will only be copied on close"""
427 def __init__(self, name, mode, atomic=False):
427 def __init__(self, name, mode, atomic=False):
428 self.__name = name
428 self.__name = name
429 self.temp = mktempcopy(name)
429 self.temp = mktempcopy(name)
430 file.__init__(self, self.temp, mode)
430 file.__init__(self, self.temp, mode)
431 def close(self):
431 def close(self):
432 if not self.closed:
432 if not self.closed:
433 file.close(self)
433 file.close(self)
434 rename(self.temp, self.__name)
434 rename(self.temp, self.__name)
435 def __del__(self):
435 def __del__(self):
436 self.close()
436 self.close()
437
437
438 def o(path, mode="r", text=False, atomic=False):
438 def o(path, mode="r", text=False, atomic=False):
439 if audit_p:
439 if audit_p:
440 audit_path(path)
440 audit_path(path)
441 f = os.path.join(p, path)
441 f = os.path.join(p, path)
442
442
443 if not text:
443 if not text:
444 mode += "b" # for that other OS
444 mode += "b" # for that other OS
445
445
446 if mode[0] != "r":
446 if mode[0] != "r":
447 try:
447 try:
448 nlink = nlinks(f)
448 nlink = nlinks(f)
449 except OSError:
449 except OSError:
450 d = os.path.dirname(f)
450 d = os.path.dirname(f)
451 if not os.path.isdir(d):
451 if not os.path.isdir(d):
452 os.makedirs(d)
452 os.makedirs(d)
453 else:
453 else:
454 if atomic:
454 if atomic:
455 return atomicfile(f, mode)
455 return atomicfile(f, mode)
456 if nlink > 1:
456 if nlink > 1:
457 rename(mktempcopy(f), f)
457 rename(mktempcopy(f), f)
458 return file(f, mode)
458 return file(f, mode)
459
459
460 return o
460 return o
461
461
462 def _makelock_file(info, pathname):
462 def _makelock_file(info, pathname):
463 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
463 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
464 os.write(ld, info)
464 os.write(ld, info)
465 os.close(ld)
465 os.close(ld)
466
466
467 def _readlock_file(pathname):
467 def _readlock_file(pathname):
468 return file(pathname).read()
468 return file(pathname).read()
469
469
470 def nlinks(pathname):
470 def nlinks(pathname):
471 """Return number of hardlinks for the given file."""
471 """Return number of hardlinks for the given file."""
472 return os.stat(pathname).st_nlink
472 return os.stat(pathname).st_nlink
473
473
474 if hasattr(os, 'link'):
474 if hasattr(os, 'link'):
475 os_link = os.link
475 os_link = os.link
476 else:
476 else:
477 def os_link(src, dst):
477 def os_link(src, dst):
478 raise OSError(0, _("Hardlinks not supported"))
478 raise OSError(0, _("Hardlinks not supported"))
479
479
480 # Platform specific variants
480 # Platform specific variants
481 if os.name == 'nt':
481 if os.name == 'nt':
482 demandload(globals(), "msvcrt")
482 demandload(globals(), "msvcrt")
483 nulldev = 'NUL:'
483 nulldev = 'NUL:'
484
484
485 class winstdout:
485 class winstdout:
486 '''stdout on windows misbehaves if sent through a pipe'''
486 '''stdout on windows misbehaves if sent through a pipe'''
487
487
488 def __init__(self, fp):
488 def __init__(self, fp):
489 self.fp = fp
489 self.fp = fp
490
490
491 def __getattr__(self, key):
491 def __getattr__(self, key):
492 return getattr(self.fp, key)
492 return getattr(self.fp, key)
493
493
494 def close(self):
494 def close(self):
495 try:
495 try:
496 self.fp.close()
496 self.fp.close()
497 except: pass
497 except: pass
498
498
499 def write(self, s):
499 def write(self, s):
500 try:
500 try:
501 return self.fp.write(s)
501 return self.fp.write(s)
502 except IOError, inst:
502 except IOError, inst:
503 if inst.errno != 0: raise
503 if inst.errno != 0: raise
504 self.close()
504 self.close()
505 raise IOError(errno.EPIPE, 'Broken pipe')
505 raise IOError(errno.EPIPE, 'Broken pipe')
506
506
507 sys.stdout = winstdout(sys.stdout)
507 sys.stdout = winstdout(sys.stdout)
508
508
509 def os_rcpath():
509 def os_rcpath():
510 '''return default os-specific hgrc search path'''
510 '''return default os-specific hgrc search path'''
511 try:
511 try:
512 import win32api, win32process
512 import win32api, win32process
513 proc = win32api.GetCurrentProcess()
513 proc = win32api.GetCurrentProcess()
514 filename = win32process.GetModuleFileNameEx(proc, 0)
514 filename = win32process.GetModuleFileNameEx(proc, 0)
515 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
515 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
516 except ImportError:
516 except ImportError:
517 systemrc = r'c:\mercurial\mercurial.ini'
517 systemrc = r'c:\mercurial\mercurial.ini'
518
518
519 return [systemrc,
519 return [systemrc,
520 os.path.join(os.path.expanduser('~'), 'mercurial.ini')]
520 os.path.join(os.path.expanduser('~'), 'mercurial.ini')]
521
521
522 def parse_patch_output(output_line):
522 def parse_patch_output(output_line):
523 """parses the output produced by patch and returns the file name"""
523 """parses the output produced by patch and returns the file name"""
524 pf = output_line[14:]
524 pf = output_line[14:]
525 if pf[0] == '`':
525 if pf[0] == '`':
526 pf = pf[1:-1] # Remove the quotes
526 pf = pf[1:-1] # Remove the quotes
527 return pf
527 return pf
528
528
529 try: # ActivePython can create hard links using win32file module
529 try: # ActivePython can create hard links using win32file module
530 import win32api, win32con, win32file
530 import win32api, win32con, win32file
531
531
532 def os_link(src, dst): # NB will only succeed on NTFS
532 def os_link(src, dst): # NB will only succeed on NTFS
533 win32file.CreateHardLink(dst, src)
533 win32file.CreateHardLink(dst, src)
534
534
535 def nlinks(pathname):
535 def nlinks(pathname):
536 """Return number of hardlinks for the given file."""
536 """Return number of hardlinks for the given file."""
537 try:
537 try:
538 fh = win32file.CreateFile(pathname,
538 fh = win32file.CreateFile(pathname,
539 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
539 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
540 None, win32file.OPEN_EXISTING, 0, None)
540 None, win32file.OPEN_EXISTING, 0, None)
541 res = win32file.GetFileInformationByHandle(fh)
541 res = win32file.GetFileInformationByHandle(fh)
542 fh.Close()
542 fh.Close()
543 return res[7]
543 return res[7]
544 except:
544 except:
545 return os.stat(pathname).st_nlink
545 return os.stat(pathname).st_nlink
546
546
547 def testpid(pid):
547 def testpid(pid):
548 '''return False if pid is dead, True if running or not known'''
548 '''return False if pid is dead, True if running or not known'''
549 try:
549 try:
550 win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
550 win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
551 False, pid)
551 False, pid)
552 except:
552 except:
553 return True
553 return True
554
554
555 except ImportError:
555 except ImportError:
556 def testpid(pid):
556 def testpid(pid):
557 '''return False if pid dead, True if running or not known'''
557 '''return False if pid dead, True if running or not known'''
558 return True
558 return True
559
559
560 def is_exec(f, last):
560 def is_exec(f, last):
561 return last
561 return last
562
562
563 def set_exec(f, mode):
563 def set_exec(f, mode):
564 pass
564 pass
565
565
566 def set_binary(fd):
566 def set_binary(fd):
567 msvcrt.setmode(fd.fileno(), os.O_BINARY)
567 msvcrt.setmode(fd.fileno(), os.O_BINARY)
568
568
569 def pconvert(path):
569 def pconvert(path):
570 return path.replace("\\", "/")
570 return path.replace("\\", "/")
571
571
572 def localpath(path):
572 def localpath(path):
573 return path.replace('/', '\\')
573 return path.replace('/', '\\')
574
574
575 def normpath(path):
575 def normpath(path):
576 return pconvert(os.path.normpath(path))
576 return pconvert(os.path.normpath(path))
577
577
578 makelock = _makelock_file
578 makelock = _makelock_file
579 readlock = _readlock_file
579 readlock = _readlock_file
580
580
581 def explain_exit(code):
581 def explain_exit(code):
582 return _("exited with status %d") % code, code
582 return _("exited with status %d") % code, code
583
583
584 else:
584 else:
585 nulldev = '/dev/null'
585 nulldev = '/dev/null'
586
586
587 def rcfiles(path):
587 def rcfiles(path):
588 rcs = [os.path.join(path, 'hgrc')]
588 rcs = [os.path.join(path, 'hgrc')]
589 rcdir = os.path.join(path, 'hgrc.d')
589 rcdir = os.path.join(path, 'hgrc.d')
590 try:
590 try:
591 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
591 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
592 if f.endswith(".rc")])
592 if f.endswith(".rc")])
593 except OSError, inst: pass
593 except OSError, inst: pass
594 return rcs
594 return rcs
595
595
596 def os_rcpath():
596 def os_rcpath():
597 '''return default os-specific hgrc search path'''
597 '''return default os-specific hgrc search path'''
598 path = []
598 path = []
599 if len(sys.argv) > 0:
599 if len(sys.argv) > 0:
600 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
600 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
601 '/../etc/mercurial'))
601 '/../etc/mercurial'))
602 path.extend(rcfiles('/etc/mercurial'))
602 path.extend(rcfiles('/etc/mercurial'))
603 path.append(os.path.expanduser('~/.hgrc'))
603 path.append(os.path.expanduser('~/.hgrc'))
604 path = [os.path.normpath(f) for f in path]
604 path = [os.path.normpath(f) for f in path]
605 return path
605 return path
606
606
607 def parse_patch_output(output_line):
607 def parse_patch_output(output_line):
608 """parses the output produced by patch and returns the file name"""
608 """parses the output produced by patch and returns the file name"""
609 pf = output_line[14:]
609 pf = output_line[14:]
610 if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0:
610 if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0:
611 pf = pf[1:-1] # Remove the quotes
611 pf = pf[1:-1] # Remove the quotes
612 return pf
612 return pf
613
613
614 def is_exec(f, last):
614 def is_exec(f, last):
615 """check whether a file is executable"""
615 """check whether a file is executable"""
616 return (os.stat(f).st_mode & 0100 != 0)
616 return (os.stat(f).st_mode & 0100 != 0)
617
617
618 def set_exec(f, mode):
618 def set_exec(f, mode):
619 s = os.stat(f).st_mode
619 s = os.stat(f).st_mode
620 if (s & 0100 != 0) == mode:
620 if (s & 0100 != 0) == mode:
621 return
621 return
622 if mode:
622 if mode:
623 # Turn on +x for every +r bit when making a file executable
623 # Turn on +x for every +r bit when making a file executable
624 # and obey umask.
624 # and obey umask.
625 umask = os.umask(0)
625 umask = os.umask(0)
626 os.umask(umask)
626 os.umask(umask)
627 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
627 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
628 else:
628 else:
629 os.chmod(f, s & 0666)
629 os.chmod(f, s & 0666)
630
630
631 def set_binary(fd):
631 def set_binary(fd):
632 pass
632 pass
633
633
634 def pconvert(path):
634 def pconvert(path):
635 return path
635 return path
636
636
637 def localpath(path):
637 def localpath(path):
638 return path
638 return path
639
639
640 normpath = os.path.normpath
640 normpath = os.path.normpath
641
641
642 def makelock(info, pathname):
642 def makelock(info, pathname):
643 try:
643 try:
644 os.symlink(info, pathname)
644 os.symlink(info, pathname)
645 except OSError, why:
645 except OSError, why:
646 if why.errno == errno.EEXIST:
646 if why.errno == errno.EEXIST:
647 raise
647 raise
648 else:
648 else:
649 _makelock_file(info, pathname)
649 _makelock_file(info, pathname)
650
650
651 def readlock(pathname):
651 def readlock(pathname):
652 try:
652 try:
653 return os.readlink(pathname)
653 return os.readlink(pathname)
654 except OSError, why:
654 except OSError, why:
655 if why.errno == errno.EINVAL:
655 if why.errno == errno.EINVAL:
656 return _readlock_file(pathname)
656 return _readlock_file(pathname)
657 else:
657 else:
658 raise
658 raise
659
659
660 def testpid(pid):
660 def testpid(pid):
661 '''return False if pid dead, True if running or not sure'''
661 '''return False if pid dead, True if running or not sure'''
662 try:
662 try:
663 os.kill(pid, 0)
663 os.kill(pid, 0)
664 return True
664 return True
665 except OSError, inst:
665 except OSError, inst:
666 return inst.errno != errno.ESRCH
666 return inst.errno != errno.ESRCH
667
667
668 def explain_exit(code):
668 def explain_exit(code):
669 """return a 2-tuple (desc, code) describing a process's status"""
669 """return a 2-tuple (desc, code) describing a process's status"""
670 if os.WIFEXITED(code):
670 if os.WIFEXITED(code):
671 val = os.WEXITSTATUS(code)
671 val = os.WEXITSTATUS(code)
672 return _("exited with status %d") % val, val
672 return _("exited with status %d") % val, val
673 elif os.WIFSIGNALED(code):
673 elif os.WIFSIGNALED(code):
674 val = os.WTERMSIG(code)
674 val = os.WTERMSIG(code)
675 return _("killed by signal %d") % val, val
675 return _("killed by signal %d") % val, val
676 elif os.WIFSTOPPED(code):
676 elif os.WIFSTOPPED(code):
677 val = os.WSTOPSIG(code)
677 val = os.WSTOPSIG(code)
678 return _("stopped by signal %d") % val, val
678 return _("stopped by signal %d") % val, val
679 raise ValueError(_("invalid exit code"))
679 raise ValueError(_("invalid exit code"))
680
680
681 class chunkbuffer(object):
681 class chunkbuffer(object):
682 """Allow arbitrary sized chunks of data to be efficiently read from an
682 """Allow arbitrary sized chunks of data to be efficiently read from an
683 iterator over chunks of arbitrary size."""
683 iterator over chunks of arbitrary size."""
684
684
685 def __init__(self, in_iter, targetsize = 2**16):
685 def __init__(self, in_iter, targetsize = 2**16):
686 """in_iter is the iterator that's iterating over the input chunks.
686 """in_iter is the iterator that's iterating over the input chunks.
687 targetsize is how big a buffer to try to maintain."""
687 targetsize is how big a buffer to try to maintain."""
688 self.in_iter = iter(in_iter)
688 self.in_iter = iter(in_iter)
689 self.buf = ''
689 self.buf = ''
690 self.targetsize = int(targetsize)
690 self.targetsize = int(targetsize)
691 if self.targetsize <= 0:
691 if self.targetsize <= 0:
692 raise ValueError(_("targetsize must be greater than 0, was %d") %
692 raise ValueError(_("targetsize must be greater than 0, was %d") %
693 targetsize)
693 targetsize)
694 self.iterempty = False
694 self.iterempty = False
695
695
696 def fillbuf(self):
696 def fillbuf(self):
697 """Ignore target size; read every chunk from iterator until empty."""
697 """Ignore target size; read every chunk from iterator until empty."""
698 if not self.iterempty:
698 if not self.iterempty:
699 collector = cStringIO.StringIO()
699 collector = cStringIO.StringIO()
700 collector.write(self.buf)
700 collector.write(self.buf)
701 for ch in self.in_iter:
701 for ch in self.in_iter:
702 collector.write(ch)
702 collector.write(ch)
703 self.buf = collector.getvalue()
703 self.buf = collector.getvalue()
704 self.iterempty = True
704 self.iterempty = True
705
705
706 def read(self, l):
706 def read(self, l):
707 """Read L bytes of data from the iterator of chunks of data.
707 """Read L bytes of data from the iterator of chunks of data.
708 Returns less than L bytes if the iterator runs dry."""
708 Returns less than L bytes if the iterator runs dry."""
709 if l > len(self.buf) and not self.iterempty:
709 if l > len(self.buf) and not self.iterempty:
710 # Clamp to a multiple of self.targetsize
710 # Clamp to a multiple of self.targetsize
711 targetsize = self.targetsize * ((l // self.targetsize) + 1)
711 targetsize = self.targetsize * ((l // self.targetsize) + 1)
712 collector = cStringIO.StringIO()
712 collector = cStringIO.StringIO()
713 collector.write(self.buf)
713 collector.write(self.buf)
714 collected = len(self.buf)
714 collected = len(self.buf)
715 for chunk in self.in_iter:
715 for chunk in self.in_iter:
716 collector.write(chunk)
716 collector.write(chunk)
717 collected += len(chunk)
717 collected += len(chunk)
718 if collected >= targetsize:
718 if collected >= targetsize:
719 break
719 break
720 if collected < targetsize:
720 if collected < targetsize:
721 self.iterempty = True
721 self.iterempty = True
722 self.buf = collector.getvalue()
722 self.buf = collector.getvalue()
723 s, self.buf = self.buf[:l], buffer(self.buf, l)
723 s, self.buf = self.buf[:l], buffer(self.buf, l)
724 return s
724 return s
725
725
726 def filechunkiter(f, size = 65536):
726 def filechunkiter(f, size = 65536):
727 """Create a generator that produces all the data in the file size
727 """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
728 (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
729 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
730 socket or some other type of file that sometimes reads less data
731 than is requested."""
731 than is requested."""
732 s = f.read(size)
732 s = f.read(size)
733 while len(s) > 0:
733 while len(s) > 0:
734 yield s
734 yield s
735 s = f.read(size)
735 s = f.read(size)
736
736
737 def makedate():
737 def makedate():
738 lt = time.localtime()
738 lt = time.localtime()
739 if lt[8] == 1 and time.daylight:
739 if lt[8] == 1 and time.daylight:
740 tz = time.altzone
740 tz = time.altzone
741 else:
741 else:
742 tz = time.timezone
742 tz = time.timezone
743 return time.mktime(lt), tz
743 return time.mktime(lt), tz
744
744
745 def datestr(date=None, format='%c'):
745 def datestr(date=None, format='%a %b %d %H:%M:%S %Y'):
746 """represent a (unixtime, offset) tuple as a localized time.
746 """represent a (unixtime, offset) tuple as a localized time.
747 unixtime is seconds since the epoch, and offset is the time zone's
747 unixtime is seconds since the epoch, and offset is the time zone's
748 number of seconds away from UTC."""
748 number of seconds away from UTC."""
749 t, tz = date or makedate()
749 t, tz = date or makedate()
750 return ("%s %+03d%02d" %
750 return ("%s %+03d%02d" %
751 (time.strftime(format, time.gmtime(float(t) - tz)),
751 (time.strftime(format, time.gmtime(float(t) - tz)),
752 -tz / 3600,
752 -tz / 3600,
753 ((-tz % 3600) / 60)))
753 ((-tz % 3600) / 60)))
754
754
755 def shortuser(user):
755 def shortuser(user):
756 """Return a short representation of a user name or email address."""
756 """Return a short representation of a user name or email address."""
757 f = user.find('@')
757 f = user.find('@')
758 if f >= 0:
758 if f >= 0:
759 user = user[:f]
759 user = user[:f]
760 f = user.find('<')
760 f = user.find('<')
761 if f >= 0:
761 if f >= 0:
762 user = user[f+1:]
762 user = user[f+1:]
763 return user
763 return user
764
764
765 def walkrepos(path):
765 def walkrepos(path):
766 '''yield every hg repository under path, recursively.'''
766 '''yield every hg repository under path, recursively.'''
767 def errhandler(err):
767 def errhandler(err):
768 if err.filename == path:
768 if err.filename == path:
769 raise err
769 raise err
770
770
771 for root, dirs, files in os.walk(path, onerror=errhandler):
771 for root, dirs, files in os.walk(path, onerror=errhandler):
772 for d in dirs:
772 for d in dirs:
773 if d == '.hg':
773 if d == '.hg':
774 yield root
774 yield root
775 dirs[:] = []
775 dirs[:] = []
776 break
776 break
777
777
778 _rcpath = None
778 _rcpath = None
779
779
780 def rcpath():
780 def rcpath():
781 '''return hgrc search path. if env var HGRCPATH is set, use it.
781 '''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,
782 for each item in path, if directory, use files ending in .rc,
783 else use item.
783 else use item.
784 make HGRCPATH empty to only look in .hg/hgrc of current repo.
784 make HGRCPATH empty to only look in .hg/hgrc of current repo.
785 if no HGRCPATH, use default os-specific path.'''
785 if no HGRCPATH, use default os-specific path.'''
786 global _rcpath
786 global _rcpath
787 if _rcpath is None:
787 if _rcpath is None:
788 if 'HGRCPATH' in os.environ:
788 if 'HGRCPATH' in os.environ:
789 _rcpath = []
789 _rcpath = []
790 for p in os.environ['HGRCPATH'].split(os.pathsep):
790 for p in os.environ['HGRCPATH'].split(os.pathsep):
791 if not p: continue
791 if not p: continue
792 if os.path.isdir(p):
792 if os.path.isdir(p):
793 for f in os.listdir(p):
793 for f in os.listdir(p):
794 if f.endswith('.rc'):
794 if f.endswith('.rc'):
795 _rcpath.append(os.path.join(p, f))
795 _rcpath.append(os.path.join(p, f))
796 else:
796 else:
797 _rcpath.append(p)
797 _rcpath.append(p)
798 else:
798 else:
799 _rcpath = os_rcpath()
799 _rcpath = os_rcpath()
800 return _rcpath
800 return _rcpath
@@ -1,17 +1,17 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init a
3 hg init a
4 cd a
4 cd a
5 echo a > foo
5 echo a > foo
6 hg add foo
6 hg add foo
7 hg commit -m 1 -d "1000000 0"
7 hg commit -m 1 -d "1000000 0"
8 rm foo
8 rm foo
9 hg remove foo
9 hg remove foo
10 hg commit -m 2 -d "1000000 0"
10 hg commit -m 2 -d "1000000 0"
11 hg export 0 | sed -e "s/Thu Jan 01/Thu Jan 1/"
11 hg export 0
12 hg export 1 | sed -e "s/Thu Jan 01/Thu Jan 1/"
12 hg export 1
13 hg log -p -r 0 | sed -e "s/Thu Jan 01/Thu Jan 1/"
13 hg log -p -r 0
14 hg log -p -r 1 | sed -e "s/Thu Jan 01/Thu Jan 1/"
14 hg log -p -r 1
15
15
16 cd ..
16 cd ..
17 hg clone a b
17 hg clone a b
@@ -1,47 +1,47 b''
1 # HG changeset patch
1 # HG changeset patch
2 # User test
2 # User test
3 # Node ID 8ba83d44753d6259db5ce6524974dd1174e90f47
3 # Node ID 8ba83d44753d6259db5ce6524974dd1174e90f47
4 # Parent 0000000000000000000000000000000000000000
4 # Parent 0000000000000000000000000000000000000000
5 1
5 1
6
6
7 diff -r 000000000000 -r 8ba83d44753d foo
7 diff -r 000000000000 -r 8ba83d44753d foo
8 --- /dev/null Thu Jan 1 00:00:00 1970 +0000
8 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9 +++ b/foo Mon Jan 12 13:46:40 1970 +0000
9 +++ b/foo Mon Jan 12 13:46:40 1970 +0000
10 @@ -0,0 +1,1 @@
10 @@ -0,0 +1,1 @@
11 +a
11 +a
12 # HG changeset patch
12 # HG changeset patch
13 # User test
13 # User test
14 # Node ID a1fce69c50d97881c5c014ab23f580f720c78678
14 # Node ID a1fce69c50d97881c5c014ab23f580f720c78678
15 # Parent 8ba83d44753d6259db5ce6524974dd1174e90f47
15 # Parent 8ba83d44753d6259db5ce6524974dd1174e90f47
16 2
16 2
17
17
18 diff -r 8ba83d44753d -r a1fce69c50d9 foo
18 diff -r 8ba83d44753d -r a1fce69c50d9 foo
19 --- a/foo Mon Jan 12 13:46:40 1970 +0000
19 --- a/foo Mon Jan 12 13:46:40 1970 +0000
20 +++ /dev/null Thu Jan 1 00:00:00 1970 +0000
20 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
21 @@ -1,1 +0,0 @@
21 @@ -1,1 +0,0 @@
22 -a
22 -a
23 changeset: 0:8ba83d44753d
23 changeset: 0:8ba83d44753d
24 user: test
24 user: test
25 date: Mon Jan 12 13:46:40 1970 +0000
25 date: Mon Jan 12 13:46:40 1970 +0000
26 summary: 1
26 summary: 1
27
27
28 diff -r 000000000000 -r 8ba83d44753d foo
28 diff -r 000000000000 -r 8ba83d44753d foo
29 --- /dev/null Thu Jan 1 00:00:00 1970 +0000
29 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
30 +++ b/foo Mon Jan 12 13:46:40 1970 +0000
30 +++ b/foo Mon Jan 12 13:46:40 1970 +0000
31 @@ -0,0 +1,1 @@
31 @@ -0,0 +1,1 @@
32 +a
32 +a
33
33
34
34
35 changeset: 1:a1fce69c50d9
35 changeset: 1:a1fce69c50d9
36 tag: tip
36 tag: tip
37 user: test
37 user: test
38 date: Mon Jan 12 13:46:40 1970 +0000
38 date: Mon Jan 12 13:46:40 1970 +0000
39 summary: 2
39 summary: 2
40
40
41 diff -r 8ba83d44753d -r a1fce69c50d9 foo
41 diff -r 8ba83d44753d -r a1fce69c50d9 foo
42 --- a/foo Mon Jan 12 13:46:40 1970 +0000
42 --- a/foo Mon Jan 12 13:46:40 1970 +0000
43 +++ /dev/null Thu Jan 1 00:00:00 1970 +0000
43 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
44 @@ -1,1 +0,0 @@
44 @@ -1,1 +0,0 @@
45 -a
45 -a
46
46
47
47
General Comments 0
You need to be logged in to leave comments. Login now