##// END OF EJS Templates
util: do not recurse in makedirs if name is '' (issue2528)
Nicolas Dumazet -
r13053:2649be11 stable
parent child Browse files
Show More
@@ -1,1478 +1,1478 b''
1 # util.py - Mercurial utility functions and platform specfic implementations
1 # util.py - Mercurial utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specfic implementations.
10 """Mercurial utility functions and platform specfic implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from i18n import _
16 from i18n import _
17 import error, osutil, encoding
17 import error, osutil, encoding
18 import errno, re, shutil, sys, tempfile, traceback
18 import errno, re, shutil, sys, tempfile, traceback
19 import os, stat, time, calendar, textwrap, unicodedata, signal
19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 import imp, socket
20 import imp, socket
21
21
22 # Python compatibility
22 # Python compatibility
23
23
24 def sha1(s):
24 def sha1(s):
25 return _fastsha1(s)
25 return _fastsha1(s)
26
26
27 def _fastsha1(s):
27 def _fastsha1(s):
28 # This function will import sha1 from hashlib or sha (whichever is
28 # This function will import sha1 from hashlib or sha (whichever is
29 # available) and overwrite itself with it on the first call.
29 # available) and overwrite itself with it on the first call.
30 # Subsequent calls will go directly to the imported function.
30 # Subsequent calls will go directly to the imported function.
31 if sys.version_info >= (2, 5):
31 if sys.version_info >= (2, 5):
32 from hashlib import sha1 as _sha1
32 from hashlib import sha1 as _sha1
33 else:
33 else:
34 from sha import sha as _sha1
34 from sha import sha as _sha1
35 global _fastsha1, sha1
35 global _fastsha1, sha1
36 _fastsha1 = sha1 = _sha1
36 _fastsha1 = sha1 = _sha1
37 return _sha1(s)
37 return _sha1(s)
38
38
39 import __builtin__
39 import __builtin__
40
40
41 if sys.version_info[0] < 3:
41 if sys.version_info[0] < 3:
42 def fakebuffer(sliceable, offset=0):
42 def fakebuffer(sliceable, offset=0):
43 return sliceable[offset:]
43 return sliceable[offset:]
44 else:
44 else:
45 def fakebuffer(sliceable, offset=0):
45 def fakebuffer(sliceable, offset=0):
46 return memoryview(sliceable)[offset:]
46 return memoryview(sliceable)[offset:]
47 try:
47 try:
48 buffer
48 buffer
49 except NameError:
49 except NameError:
50 __builtin__.buffer = fakebuffer
50 __builtin__.buffer = fakebuffer
51
51
52 import subprocess
52 import subprocess
53 closefds = os.name == 'posix'
53 closefds = os.name == 'posix'
54
54
55 def popen2(cmd, env=None, newlines=False):
55 def popen2(cmd, env=None, newlines=False):
56 # Setting bufsize to -1 lets the system decide the buffer size.
56 # Setting bufsize to -1 lets the system decide the buffer size.
57 # The default for bufsize is 0, meaning unbuffered. This leads to
57 # The default for bufsize is 0, meaning unbuffered. This leads to
58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
60 close_fds=closefds,
60 close_fds=closefds,
61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 universal_newlines=newlines,
62 universal_newlines=newlines,
63 env=env)
63 env=env)
64 return p.stdin, p.stdout
64 return p.stdin, p.stdout
65
65
66 def popen3(cmd, env=None, newlines=False):
66 def popen3(cmd, env=None, newlines=False):
67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
68 close_fds=closefds,
68 close_fds=closefds,
69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 stderr=subprocess.PIPE,
70 stderr=subprocess.PIPE,
71 universal_newlines=newlines,
71 universal_newlines=newlines,
72 env=env)
72 env=env)
73 return p.stdin, p.stdout, p.stderr
73 return p.stdin, p.stdout, p.stderr
74
74
75 def version():
75 def version():
76 """Return version information if available."""
76 """Return version information if available."""
77 try:
77 try:
78 import __version__
78 import __version__
79 return __version__.version
79 return __version__.version
80 except ImportError:
80 except ImportError:
81 return 'unknown'
81 return 'unknown'
82
82
83 # used by parsedate
83 # used by parsedate
84 defaultdateformats = (
84 defaultdateformats = (
85 '%Y-%m-%d %H:%M:%S',
85 '%Y-%m-%d %H:%M:%S',
86 '%Y-%m-%d %I:%M:%S%p',
86 '%Y-%m-%d %I:%M:%S%p',
87 '%Y-%m-%d %H:%M',
87 '%Y-%m-%d %H:%M',
88 '%Y-%m-%d %I:%M%p',
88 '%Y-%m-%d %I:%M%p',
89 '%Y-%m-%d',
89 '%Y-%m-%d',
90 '%m-%d',
90 '%m-%d',
91 '%m/%d',
91 '%m/%d',
92 '%m/%d/%y',
92 '%m/%d/%y',
93 '%m/%d/%Y',
93 '%m/%d/%Y',
94 '%a %b %d %H:%M:%S %Y',
94 '%a %b %d %H:%M:%S %Y',
95 '%a %b %d %I:%M:%S%p %Y',
95 '%a %b %d %I:%M:%S%p %Y',
96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
97 '%b %d %H:%M:%S %Y',
97 '%b %d %H:%M:%S %Y',
98 '%b %d %I:%M:%S%p %Y',
98 '%b %d %I:%M:%S%p %Y',
99 '%b %d %H:%M:%S',
99 '%b %d %H:%M:%S',
100 '%b %d %I:%M:%S%p',
100 '%b %d %I:%M:%S%p',
101 '%b %d %H:%M',
101 '%b %d %H:%M',
102 '%b %d %I:%M%p',
102 '%b %d %I:%M%p',
103 '%b %d %Y',
103 '%b %d %Y',
104 '%b %d',
104 '%b %d',
105 '%H:%M:%S',
105 '%H:%M:%S',
106 '%I:%M:%S%p',
106 '%I:%M:%S%p',
107 '%H:%M',
107 '%H:%M',
108 '%I:%M%p',
108 '%I:%M%p',
109 )
109 )
110
110
111 extendeddateformats = defaultdateformats + (
111 extendeddateformats = defaultdateformats + (
112 "%Y",
112 "%Y",
113 "%Y-%m",
113 "%Y-%m",
114 "%b",
114 "%b",
115 "%b %Y",
115 "%b %Y",
116 )
116 )
117
117
118 def cachefunc(func):
118 def cachefunc(func):
119 '''cache the result of function calls'''
119 '''cache the result of function calls'''
120 # XXX doesn't handle keywords args
120 # XXX doesn't handle keywords args
121 cache = {}
121 cache = {}
122 if func.func_code.co_argcount == 1:
122 if func.func_code.co_argcount == 1:
123 # we gain a small amount of time because
123 # we gain a small amount of time because
124 # we don't need to pack/unpack the list
124 # we don't need to pack/unpack the list
125 def f(arg):
125 def f(arg):
126 if arg not in cache:
126 if arg not in cache:
127 cache[arg] = func(arg)
127 cache[arg] = func(arg)
128 return cache[arg]
128 return cache[arg]
129 else:
129 else:
130 def f(*args):
130 def f(*args):
131 if args not in cache:
131 if args not in cache:
132 cache[args] = func(*args)
132 cache[args] = func(*args)
133 return cache[args]
133 return cache[args]
134
134
135 return f
135 return f
136
136
137 def lrucachefunc(func):
137 def lrucachefunc(func):
138 '''cache most recent results of function calls'''
138 '''cache most recent results of function calls'''
139 cache = {}
139 cache = {}
140 order = []
140 order = []
141 if func.func_code.co_argcount == 1:
141 if func.func_code.co_argcount == 1:
142 def f(arg):
142 def f(arg):
143 if arg not in cache:
143 if arg not in cache:
144 if len(cache) > 20:
144 if len(cache) > 20:
145 del cache[order.pop(0)]
145 del cache[order.pop(0)]
146 cache[arg] = func(arg)
146 cache[arg] = func(arg)
147 else:
147 else:
148 order.remove(arg)
148 order.remove(arg)
149 order.append(arg)
149 order.append(arg)
150 return cache[arg]
150 return cache[arg]
151 else:
151 else:
152 def f(*args):
152 def f(*args):
153 if args not in cache:
153 if args not in cache:
154 if len(cache) > 20:
154 if len(cache) > 20:
155 del cache[order.pop(0)]
155 del cache[order.pop(0)]
156 cache[args] = func(*args)
156 cache[args] = func(*args)
157 else:
157 else:
158 order.remove(args)
158 order.remove(args)
159 order.append(args)
159 order.append(args)
160 return cache[args]
160 return cache[args]
161
161
162 return f
162 return f
163
163
164 class propertycache(object):
164 class propertycache(object):
165 def __init__(self, func):
165 def __init__(self, func):
166 self.func = func
166 self.func = func
167 self.name = func.__name__
167 self.name = func.__name__
168 def __get__(self, obj, type=None):
168 def __get__(self, obj, type=None):
169 result = self.func(obj)
169 result = self.func(obj)
170 setattr(obj, self.name, result)
170 setattr(obj, self.name, result)
171 return result
171 return result
172
172
173 def pipefilter(s, cmd):
173 def pipefilter(s, cmd):
174 '''filter string S through command CMD, returning its output'''
174 '''filter string S through command CMD, returning its output'''
175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
177 pout, perr = p.communicate(s)
177 pout, perr = p.communicate(s)
178 return pout
178 return pout
179
179
180 def tempfilter(s, cmd):
180 def tempfilter(s, cmd):
181 '''filter string S through a pair of temporary files with CMD.
181 '''filter string S through a pair of temporary files with CMD.
182 CMD is used as a template to create the real command to be run,
182 CMD is used as a template to create the real command to be run,
183 with the strings INFILE and OUTFILE replaced by the real names of
183 with the strings INFILE and OUTFILE replaced by the real names of
184 the temporary files generated.'''
184 the temporary files generated.'''
185 inname, outname = None, None
185 inname, outname = None, None
186 try:
186 try:
187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
188 fp = os.fdopen(infd, 'wb')
188 fp = os.fdopen(infd, 'wb')
189 fp.write(s)
189 fp.write(s)
190 fp.close()
190 fp.close()
191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
192 os.close(outfd)
192 os.close(outfd)
193 cmd = cmd.replace('INFILE', inname)
193 cmd = cmd.replace('INFILE', inname)
194 cmd = cmd.replace('OUTFILE', outname)
194 cmd = cmd.replace('OUTFILE', outname)
195 code = os.system(cmd)
195 code = os.system(cmd)
196 if sys.platform == 'OpenVMS' and code & 1:
196 if sys.platform == 'OpenVMS' and code & 1:
197 code = 0
197 code = 0
198 if code:
198 if code:
199 raise Abort(_("command '%s' failed: %s") %
199 raise Abort(_("command '%s' failed: %s") %
200 (cmd, explain_exit(code)))
200 (cmd, explain_exit(code)))
201 return open(outname, 'rb').read()
201 return open(outname, 'rb').read()
202 finally:
202 finally:
203 try:
203 try:
204 if inname:
204 if inname:
205 os.unlink(inname)
205 os.unlink(inname)
206 except:
206 except:
207 pass
207 pass
208 try:
208 try:
209 if outname:
209 if outname:
210 os.unlink(outname)
210 os.unlink(outname)
211 except:
211 except:
212 pass
212 pass
213
213
214 filtertable = {
214 filtertable = {
215 'tempfile:': tempfilter,
215 'tempfile:': tempfilter,
216 'pipe:': pipefilter,
216 'pipe:': pipefilter,
217 }
217 }
218
218
219 def filter(s, cmd):
219 def filter(s, cmd):
220 "filter a string through a command that transforms its input to its output"
220 "filter a string through a command that transforms its input to its output"
221 for name, fn in filtertable.iteritems():
221 for name, fn in filtertable.iteritems():
222 if cmd.startswith(name):
222 if cmd.startswith(name):
223 return fn(s, cmd[len(name):].lstrip())
223 return fn(s, cmd[len(name):].lstrip())
224 return pipefilter(s, cmd)
224 return pipefilter(s, cmd)
225
225
226 def binary(s):
226 def binary(s):
227 """return true if a string is binary data"""
227 """return true if a string is binary data"""
228 return bool(s and '\0' in s)
228 return bool(s and '\0' in s)
229
229
230 def increasingchunks(source, min=1024, max=65536):
230 def increasingchunks(source, min=1024, max=65536):
231 '''return no less than min bytes per chunk while data remains,
231 '''return no less than min bytes per chunk while data remains,
232 doubling min after each chunk until it reaches max'''
232 doubling min after each chunk until it reaches max'''
233 def log2(x):
233 def log2(x):
234 if not x:
234 if not x:
235 return 0
235 return 0
236 i = 0
236 i = 0
237 while x:
237 while x:
238 x >>= 1
238 x >>= 1
239 i += 1
239 i += 1
240 return i - 1
240 return i - 1
241
241
242 buf = []
242 buf = []
243 blen = 0
243 blen = 0
244 for chunk in source:
244 for chunk in source:
245 buf.append(chunk)
245 buf.append(chunk)
246 blen += len(chunk)
246 blen += len(chunk)
247 if blen >= min:
247 if blen >= min:
248 if min < max:
248 if min < max:
249 min = min << 1
249 min = min << 1
250 nmin = 1 << log2(blen)
250 nmin = 1 << log2(blen)
251 if nmin > min:
251 if nmin > min:
252 min = nmin
252 min = nmin
253 if min > max:
253 if min > max:
254 min = max
254 min = max
255 yield ''.join(buf)
255 yield ''.join(buf)
256 blen = 0
256 blen = 0
257 buf = []
257 buf = []
258 if buf:
258 if buf:
259 yield ''.join(buf)
259 yield ''.join(buf)
260
260
261 Abort = error.Abort
261 Abort = error.Abort
262
262
263 def always(fn):
263 def always(fn):
264 return True
264 return True
265
265
266 def never(fn):
266 def never(fn):
267 return False
267 return False
268
268
269 def pathto(root, n1, n2):
269 def pathto(root, n1, n2):
270 '''return the relative path from one place to another.
270 '''return the relative path from one place to another.
271 root should use os.sep to separate directories
271 root should use os.sep to separate directories
272 n1 should use os.sep to separate directories
272 n1 should use os.sep to separate directories
273 n2 should use "/" to separate directories
273 n2 should use "/" to separate directories
274 returns an os.sep-separated path.
274 returns an os.sep-separated path.
275
275
276 If n1 is a relative path, it's assumed it's
276 If n1 is a relative path, it's assumed it's
277 relative to root.
277 relative to root.
278 n2 should always be relative to root.
278 n2 should always be relative to root.
279 '''
279 '''
280 if not n1:
280 if not n1:
281 return localpath(n2)
281 return localpath(n2)
282 if os.path.isabs(n1):
282 if os.path.isabs(n1):
283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
284 return os.path.join(root, localpath(n2))
284 return os.path.join(root, localpath(n2))
285 n2 = '/'.join((pconvert(root), n2))
285 n2 = '/'.join((pconvert(root), n2))
286 a, b = splitpath(n1), n2.split('/')
286 a, b = splitpath(n1), n2.split('/')
287 a.reverse()
287 a.reverse()
288 b.reverse()
288 b.reverse()
289 while a and b and a[-1] == b[-1]:
289 while a and b and a[-1] == b[-1]:
290 a.pop()
290 a.pop()
291 b.pop()
291 b.pop()
292 b.reverse()
292 b.reverse()
293 return os.sep.join((['..'] * len(a)) + b) or '.'
293 return os.sep.join((['..'] * len(a)) + b) or '.'
294
294
295 def canonpath(root, cwd, myname, auditor=None):
295 def canonpath(root, cwd, myname, auditor=None):
296 """return the canonical path of myname, given cwd and root"""
296 """return the canonical path of myname, given cwd and root"""
297 if endswithsep(root):
297 if endswithsep(root):
298 rootsep = root
298 rootsep = root
299 else:
299 else:
300 rootsep = root + os.sep
300 rootsep = root + os.sep
301 name = myname
301 name = myname
302 if not os.path.isabs(name):
302 if not os.path.isabs(name):
303 name = os.path.join(root, cwd, name)
303 name = os.path.join(root, cwd, name)
304 name = os.path.normpath(name)
304 name = os.path.normpath(name)
305 if auditor is None:
305 if auditor is None:
306 auditor = path_auditor(root)
306 auditor = path_auditor(root)
307 if name != rootsep and name.startswith(rootsep):
307 if name != rootsep and name.startswith(rootsep):
308 name = name[len(rootsep):]
308 name = name[len(rootsep):]
309 auditor(name)
309 auditor(name)
310 return pconvert(name)
310 return pconvert(name)
311 elif name == root:
311 elif name == root:
312 return ''
312 return ''
313 else:
313 else:
314 # Determine whether `name' is in the hierarchy at or beneath `root',
314 # Determine whether `name' is in the hierarchy at or beneath `root',
315 # by iterating name=dirname(name) until that causes no change (can't
315 # by iterating name=dirname(name) until that causes no change (can't
316 # check name == '/', because that doesn't work on windows). For each
316 # check name == '/', because that doesn't work on windows). For each
317 # `name', compare dev/inode numbers. If they match, the list `rel'
317 # `name', compare dev/inode numbers. If they match, the list `rel'
318 # holds the reversed list of components making up the relative file
318 # holds the reversed list of components making up the relative file
319 # name we want.
319 # name we want.
320 root_st = os.stat(root)
320 root_st = os.stat(root)
321 rel = []
321 rel = []
322 while True:
322 while True:
323 try:
323 try:
324 name_st = os.stat(name)
324 name_st = os.stat(name)
325 except OSError:
325 except OSError:
326 break
326 break
327 if samestat(name_st, root_st):
327 if samestat(name_st, root_st):
328 if not rel:
328 if not rel:
329 # name was actually the same as root (maybe a symlink)
329 # name was actually the same as root (maybe a symlink)
330 return ''
330 return ''
331 rel.reverse()
331 rel.reverse()
332 name = os.path.join(*rel)
332 name = os.path.join(*rel)
333 auditor(name)
333 auditor(name)
334 return pconvert(name)
334 return pconvert(name)
335 dirname, basename = os.path.split(name)
335 dirname, basename = os.path.split(name)
336 rel.append(basename)
336 rel.append(basename)
337 if dirname == name:
337 if dirname == name:
338 break
338 break
339 name = dirname
339 name = dirname
340
340
341 raise Abort('%s not under root' % myname)
341 raise Abort('%s not under root' % myname)
342
342
343 _hgexecutable = None
343 _hgexecutable = None
344
344
345 def main_is_frozen():
345 def main_is_frozen():
346 """return True if we are a frozen executable.
346 """return True if we are a frozen executable.
347
347
348 The code supports py2exe (most common, Windows only) and tools/freeze
348 The code supports py2exe (most common, Windows only) and tools/freeze
349 (portable, not much used).
349 (portable, not much used).
350 """
350 """
351 return (hasattr(sys, "frozen") or # new py2exe
351 return (hasattr(sys, "frozen") or # new py2exe
352 hasattr(sys, "importers") or # old py2exe
352 hasattr(sys, "importers") or # old py2exe
353 imp.is_frozen("__main__")) # tools/freeze
353 imp.is_frozen("__main__")) # tools/freeze
354
354
355 def hgexecutable():
355 def hgexecutable():
356 """return location of the 'hg' executable.
356 """return location of the 'hg' executable.
357
357
358 Defaults to $HG or 'hg' in the search path.
358 Defaults to $HG or 'hg' in the search path.
359 """
359 """
360 if _hgexecutable is None:
360 if _hgexecutable is None:
361 hg = os.environ.get('HG')
361 hg = os.environ.get('HG')
362 if hg:
362 if hg:
363 set_hgexecutable(hg)
363 set_hgexecutable(hg)
364 elif main_is_frozen():
364 elif main_is_frozen():
365 set_hgexecutable(sys.executable)
365 set_hgexecutable(sys.executable)
366 else:
366 else:
367 exe = find_exe('hg') or os.path.basename(sys.argv[0])
367 exe = find_exe('hg') or os.path.basename(sys.argv[0])
368 set_hgexecutable(exe)
368 set_hgexecutable(exe)
369 return _hgexecutable
369 return _hgexecutable
370
370
371 def set_hgexecutable(path):
371 def set_hgexecutable(path):
372 """set location of the 'hg' executable"""
372 """set location of the 'hg' executable"""
373 global _hgexecutable
373 global _hgexecutable
374 _hgexecutable = path
374 _hgexecutable = path
375
375
376 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
376 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
377 '''enhanced shell command execution.
377 '''enhanced shell command execution.
378 run with environment maybe modified, maybe in different dir.
378 run with environment maybe modified, maybe in different dir.
379
379
380 if command fails and onerr is None, return status. if ui object,
380 if command fails and onerr is None, return status. if ui object,
381 print error message and return status, else raise onerr object as
381 print error message and return status, else raise onerr object as
382 exception.
382 exception.
383
383
384 if out is specified, it is assumed to be a file-like object that has a
384 if out is specified, it is assumed to be a file-like object that has a
385 write() method. stdout and stderr will be redirected to out.'''
385 write() method. stdout and stderr will be redirected to out.'''
386 def py2shell(val):
386 def py2shell(val):
387 'convert python object into string that is useful to shell'
387 'convert python object into string that is useful to shell'
388 if val is None or val is False:
388 if val is None or val is False:
389 return '0'
389 return '0'
390 if val is True:
390 if val is True:
391 return '1'
391 return '1'
392 return str(val)
392 return str(val)
393 origcmd = cmd
393 origcmd = cmd
394 if os.name == 'nt':
394 if os.name == 'nt':
395 cmd = '"%s"' % cmd
395 cmd = '"%s"' % cmd
396 env = dict(os.environ)
396 env = dict(os.environ)
397 env.update((k, py2shell(v)) for k, v in environ.iteritems())
397 env.update((k, py2shell(v)) for k, v in environ.iteritems())
398 env['HG'] = hgexecutable()
398 env['HG'] = hgexecutable()
399 if out is None:
399 if out is None:
400 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
400 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
401 env=env, cwd=cwd)
401 env=env, cwd=cwd)
402 else:
402 else:
403 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
403 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
404 env=env, cwd=cwd, stdout=subprocess.PIPE,
404 env=env, cwd=cwd, stdout=subprocess.PIPE,
405 stderr=subprocess.STDOUT)
405 stderr=subprocess.STDOUT)
406 for line in proc.stdout:
406 for line in proc.stdout:
407 out.write(line)
407 out.write(line)
408 proc.wait()
408 proc.wait()
409 rc = proc.returncode
409 rc = proc.returncode
410 if sys.platform == 'OpenVMS' and rc & 1:
410 if sys.platform == 'OpenVMS' and rc & 1:
411 rc = 0
411 rc = 0
412 if rc and onerr:
412 if rc and onerr:
413 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
413 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
414 explain_exit(rc)[0])
414 explain_exit(rc)[0])
415 if errprefix:
415 if errprefix:
416 errmsg = '%s: %s' % (errprefix, errmsg)
416 errmsg = '%s: %s' % (errprefix, errmsg)
417 try:
417 try:
418 onerr.warn(errmsg + '\n')
418 onerr.warn(errmsg + '\n')
419 except AttributeError:
419 except AttributeError:
420 raise onerr(errmsg)
420 raise onerr(errmsg)
421 return rc
421 return rc
422
422
423 def checksignature(func):
423 def checksignature(func):
424 '''wrap a function with code to check for calling errors'''
424 '''wrap a function with code to check for calling errors'''
425 def check(*args, **kwargs):
425 def check(*args, **kwargs):
426 try:
426 try:
427 return func(*args, **kwargs)
427 return func(*args, **kwargs)
428 except TypeError:
428 except TypeError:
429 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
429 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
430 raise error.SignatureError
430 raise error.SignatureError
431 raise
431 raise
432
432
433 return check
433 return check
434
434
435 def unlink(f):
435 def unlink(f):
436 """unlink and remove the directory if it is empty"""
436 """unlink and remove the directory if it is empty"""
437 os.unlink(f)
437 os.unlink(f)
438 # try removing directories that might now be empty
438 # try removing directories that might now be empty
439 try:
439 try:
440 os.removedirs(os.path.dirname(f))
440 os.removedirs(os.path.dirname(f))
441 except OSError:
441 except OSError:
442 pass
442 pass
443
443
444 def copyfile(src, dest):
444 def copyfile(src, dest):
445 "copy a file, preserving mode and atime/mtime"
445 "copy a file, preserving mode and atime/mtime"
446 if os.path.islink(src):
446 if os.path.islink(src):
447 try:
447 try:
448 os.unlink(dest)
448 os.unlink(dest)
449 except:
449 except:
450 pass
450 pass
451 os.symlink(os.readlink(src), dest)
451 os.symlink(os.readlink(src), dest)
452 else:
452 else:
453 try:
453 try:
454 shutil.copyfile(src, dest)
454 shutil.copyfile(src, dest)
455 shutil.copystat(src, dest)
455 shutil.copystat(src, dest)
456 except shutil.Error, inst:
456 except shutil.Error, inst:
457 raise Abort(str(inst))
457 raise Abort(str(inst))
458
458
459 def copyfiles(src, dst, hardlink=None):
459 def copyfiles(src, dst, hardlink=None):
460 """Copy a directory tree using hardlinks if possible"""
460 """Copy a directory tree using hardlinks if possible"""
461
461
462 if hardlink is None:
462 if hardlink is None:
463 hardlink = (os.stat(src).st_dev ==
463 hardlink = (os.stat(src).st_dev ==
464 os.stat(os.path.dirname(dst)).st_dev)
464 os.stat(os.path.dirname(dst)).st_dev)
465
465
466 num = 0
466 num = 0
467 if os.path.isdir(src):
467 if os.path.isdir(src):
468 os.mkdir(dst)
468 os.mkdir(dst)
469 for name, kind in osutil.listdir(src):
469 for name, kind in osutil.listdir(src):
470 srcname = os.path.join(src, name)
470 srcname = os.path.join(src, name)
471 dstname = os.path.join(dst, name)
471 dstname = os.path.join(dst, name)
472 hardlink, n = copyfiles(srcname, dstname, hardlink)
472 hardlink, n = copyfiles(srcname, dstname, hardlink)
473 num += n
473 num += n
474 else:
474 else:
475 if hardlink:
475 if hardlink:
476 try:
476 try:
477 os_link(src, dst)
477 os_link(src, dst)
478 except (IOError, OSError):
478 except (IOError, OSError):
479 hardlink = False
479 hardlink = False
480 shutil.copy(src, dst)
480 shutil.copy(src, dst)
481 else:
481 else:
482 shutil.copy(src, dst)
482 shutil.copy(src, dst)
483 num += 1
483 num += 1
484
484
485 return hardlink, num
485 return hardlink, num
486
486
487 class path_auditor(object):
487 class path_auditor(object):
488 '''ensure that a filesystem path contains no banned components.
488 '''ensure that a filesystem path contains no banned components.
489 the following properties of a path are checked:
489 the following properties of a path are checked:
490
490
491 - under top-level .hg
491 - under top-level .hg
492 - starts at the root of a windows drive
492 - starts at the root of a windows drive
493 - contains ".."
493 - contains ".."
494 - traverses a symlink (e.g. a/symlink_here/b)
494 - traverses a symlink (e.g. a/symlink_here/b)
495 - inside a nested repository (a callback can be used to approve
495 - inside a nested repository (a callback can be used to approve
496 some nested repositories, e.g., subrepositories)
496 some nested repositories, e.g., subrepositories)
497 '''
497 '''
498
498
499 def __init__(self, root, callback=None):
499 def __init__(self, root, callback=None):
500 self.audited = set()
500 self.audited = set()
501 self.auditeddir = set()
501 self.auditeddir = set()
502 self.root = root
502 self.root = root
503 self.callback = callback
503 self.callback = callback
504
504
505 def __call__(self, path):
505 def __call__(self, path):
506 if path in self.audited:
506 if path in self.audited:
507 return
507 return
508 normpath = os.path.normcase(path)
508 normpath = os.path.normcase(path)
509 parts = splitpath(normpath)
509 parts = splitpath(normpath)
510 if (os.path.splitdrive(path)[0]
510 if (os.path.splitdrive(path)[0]
511 or parts[0].lower() in ('.hg', '.hg.', '')
511 or parts[0].lower() in ('.hg', '.hg.', '')
512 or os.pardir in parts):
512 or os.pardir in parts):
513 raise Abort(_("path contains illegal component: %s") % path)
513 raise Abort(_("path contains illegal component: %s") % path)
514 if '.hg' in path.lower():
514 if '.hg' in path.lower():
515 lparts = [p.lower() for p in parts]
515 lparts = [p.lower() for p in parts]
516 for p in '.hg', '.hg.':
516 for p in '.hg', '.hg.':
517 if p in lparts[1:]:
517 if p in lparts[1:]:
518 pos = lparts.index(p)
518 pos = lparts.index(p)
519 base = os.path.join(*parts[:pos])
519 base = os.path.join(*parts[:pos])
520 raise Abort(_('path %r is inside repo %r') % (path, base))
520 raise Abort(_('path %r is inside repo %r') % (path, base))
521 def check(prefix):
521 def check(prefix):
522 curpath = os.path.join(self.root, prefix)
522 curpath = os.path.join(self.root, prefix)
523 try:
523 try:
524 st = os.lstat(curpath)
524 st = os.lstat(curpath)
525 except OSError, err:
525 except OSError, err:
526 # EINVAL can be raised as invalid path syntax under win32.
526 # EINVAL can be raised as invalid path syntax under win32.
527 # They must be ignored for patterns can be checked too.
527 # They must be ignored for patterns can be checked too.
528 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
528 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
529 raise
529 raise
530 else:
530 else:
531 if stat.S_ISLNK(st.st_mode):
531 if stat.S_ISLNK(st.st_mode):
532 raise Abort(_('path %r traverses symbolic link %r') %
532 raise Abort(_('path %r traverses symbolic link %r') %
533 (path, prefix))
533 (path, prefix))
534 elif (stat.S_ISDIR(st.st_mode) and
534 elif (stat.S_ISDIR(st.st_mode) and
535 os.path.isdir(os.path.join(curpath, '.hg'))):
535 os.path.isdir(os.path.join(curpath, '.hg'))):
536 if not self.callback or not self.callback(curpath):
536 if not self.callback or not self.callback(curpath):
537 raise Abort(_('path %r is inside repo %r') %
537 raise Abort(_('path %r is inside repo %r') %
538 (path, prefix))
538 (path, prefix))
539 parts.pop()
539 parts.pop()
540 prefixes = []
540 prefixes = []
541 while parts:
541 while parts:
542 prefix = os.sep.join(parts)
542 prefix = os.sep.join(parts)
543 if prefix in self.auditeddir:
543 if prefix in self.auditeddir:
544 break
544 break
545 check(prefix)
545 check(prefix)
546 prefixes.append(prefix)
546 prefixes.append(prefix)
547 parts.pop()
547 parts.pop()
548
548
549 self.audited.add(path)
549 self.audited.add(path)
550 # only add prefixes to the cache after checking everything: we don't
550 # only add prefixes to the cache after checking everything: we don't
551 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
551 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
552 self.auditeddir.update(prefixes)
552 self.auditeddir.update(prefixes)
553
553
554 def nlinks(pathname):
554 def nlinks(pathname):
555 """Return number of hardlinks for the given file."""
555 """Return number of hardlinks for the given file."""
556 return os.lstat(pathname).st_nlink
556 return os.lstat(pathname).st_nlink
557
557
558 if hasattr(os, 'link'):
558 if hasattr(os, 'link'):
559 os_link = os.link
559 os_link = os.link
560 else:
560 else:
561 def os_link(src, dst):
561 def os_link(src, dst):
562 raise OSError(0, _("Hardlinks not supported"))
562 raise OSError(0, _("Hardlinks not supported"))
563
563
564 def lookup_reg(key, name=None, scope=None):
564 def lookup_reg(key, name=None, scope=None):
565 return None
565 return None
566
566
567 def hidewindow():
567 def hidewindow():
568 """Hide current shell window.
568 """Hide current shell window.
569
569
570 Used to hide the window opened when starting asynchronous
570 Used to hide the window opened when starting asynchronous
571 child process under Windows, unneeded on other systems.
571 child process under Windows, unneeded on other systems.
572 """
572 """
573 pass
573 pass
574
574
575 if os.name == 'nt':
575 if os.name == 'nt':
576 from windows import *
576 from windows import *
577 else:
577 else:
578 from posix import *
578 from posix import *
579
579
580 def makelock(info, pathname):
580 def makelock(info, pathname):
581 try:
581 try:
582 return os.symlink(info, pathname)
582 return os.symlink(info, pathname)
583 except OSError, why:
583 except OSError, why:
584 if why.errno == errno.EEXIST:
584 if why.errno == errno.EEXIST:
585 raise
585 raise
586 except AttributeError: # no symlink in os
586 except AttributeError: # no symlink in os
587 pass
587 pass
588
588
589 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
589 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
590 os.write(ld, info)
590 os.write(ld, info)
591 os.close(ld)
591 os.close(ld)
592
592
593 def readlock(pathname):
593 def readlock(pathname):
594 try:
594 try:
595 return os.readlink(pathname)
595 return os.readlink(pathname)
596 except OSError, why:
596 except OSError, why:
597 if why.errno not in (errno.EINVAL, errno.ENOSYS):
597 if why.errno not in (errno.EINVAL, errno.ENOSYS):
598 raise
598 raise
599 except AttributeError: # no symlink in os
599 except AttributeError: # no symlink in os
600 pass
600 pass
601 return posixfile(pathname).read()
601 return posixfile(pathname).read()
602
602
603 def fstat(fp):
603 def fstat(fp):
604 '''stat file object that may not have fileno method.'''
604 '''stat file object that may not have fileno method.'''
605 try:
605 try:
606 return os.fstat(fp.fileno())
606 return os.fstat(fp.fileno())
607 except AttributeError:
607 except AttributeError:
608 return os.stat(fp.name)
608 return os.stat(fp.name)
609
609
610 # File system features
610 # File system features
611
611
612 def checkcase(path):
612 def checkcase(path):
613 """
613 """
614 Check whether the given path is on a case-sensitive filesystem
614 Check whether the given path is on a case-sensitive filesystem
615
615
616 Requires a path (like /foo/.hg) ending with a foldable final
616 Requires a path (like /foo/.hg) ending with a foldable final
617 directory component.
617 directory component.
618 """
618 """
619 s1 = os.stat(path)
619 s1 = os.stat(path)
620 d, b = os.path.split(path)
620 d, b = os.path.split(path)
621 p2 = os.path.join(d, b.upper())
621 p2 = os.path.join(d, b.upper())
622 if path == p2:
622 if path == p2:
623 p2 = os.path.join(d, b.lower())
623 p2 = os.path.join(d, b.lower())
624 try:
624 try:
625 s2 = os.stat(p2)
625 s2 = os.stat(p2)
626 if s2 == s1:
626 if s2 == s1:
627 return False
627 return False
628 return True
628 return True
629 except:
629 except:
630 return True
630 return True
631
631
632 _fspathcache = {}
632 _fspathcache = {}
633 def fspath(name, root):
633 def fspath(name, root):
634 '''Get name in the case stored in the filesystem
634 '''Get name in the case stored in the filesystem
635
635
636 The name is either relative to root, or it is an absolute path starting
636 The name is either relative to root, or it is an absolute path starting
637 with root. Note that this function is unnecessary, and should not be
637 with root. Note that this function is unnecessary, and should not be
638 called, for case-sensitive filesystems (simply because it's expensive).
638 called, for case-sensitive filesystems (simply because it's expensive).
639 '''
639 '''
640 # If name is absolute, make it relative
640 # If name is absolute, make it relative
641 if name.lower().startswith(root.lower()):
641 if name.lower().startswith(root.lower()):
642 l = len(root)
642 l = len(root)
643 if name[l] == os.sep or name[l] == os.altsep:
643 if name[l] == os.sep or name[l] == os.altsep:
644 l = l + 1
644 l = l + 1
645 name = name[l:]
645 name = name[l:]
646
646
647 if not os.path.lexists(os.path.join(root, name)):
647 if not os.path.lexists(os.path.join(root, name)):
648 return None
648 return None
649
649
650 seps = os.sep
650 seps = os.sep
651 if os.altsep:
651 if os.altsep:
652 seps = seps + os.altsep
652 seps = seps + os.altsep
653 # Protect backslashes. This gets silly very quickly.
653 # Protect backslashes. This gets silly very quickly.
654 seps.replace('\\','\\\\')
654 seps.replace('\\','\\\\')
655 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
655 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
656 dir = os.path.normcase(os.path.normpath(root))
656 dir = os.path.normcase(os.path.normpath(root))
657 result = []
657 result = []
658 for part, sep in pattern.findall(name):
658 for part, sep in pattern.findall(name):
659 if sep:
659 if sep:
660 result.append(sep)
660 result.append(sep)
661 continue
661 continue
662
662
663 if dir not in _fspathcache:
663 if dir not in _fspathcache:
664 _fspathcache[dir] = os.listdir(dir)
664 _fspathcache[dir] = os.listdir(dir)
665 contents = _fspathcache[dir]
665 contents = _fspathcache[dir]
666
666
667 lpart = part.lower()
667 lpart = part.lower()
668 lenp = len(part)
668 lenp = len(part)
669 for n in contents:
669 for n in contents:
670 if lenp == len(n) and n.lower() == lpart:
670 if lenp == len(n) and n.lower() == lpart:
671 result.append(n)
671 result.append(n)
672 break
672 break
673 else:
673 else:
674 # Cannot happen, as the file exists!
674 # Cannot happen, as the file exists!
675 result.append(part)
675 result.append(part)
676 dir = os.path.join(dir, lpart)
676 dir = os.path.join(dir, lpart)
677
677
678 return ''.join(result)
678 return ''.join(result)
679
679
680 def checkexec(path):
680 def checkexec(path):
681 """
681 """
682 Check whether the given path is on a filesystem with UNIX-like exec flags
682 Check whether the given path is on a filesystem with UNIX-like exec flags
683
683
684 Requires a directory (like /foo/.hg)
684 Requires a directory (like /foo/.hg)
685 """
685 """
686
686
687 # VFAT on some Linux versions can flip mode but it doesn't persist
687 # VFAT on some Linux versions can flip mode but it doesn't persist
688 # a FS remount. Frequently we can detect it if files are created
688 # a FS remount. Frequently we can detect it if files are created
689 # with exec bit on.
689 # with exec bit on.
690
690
691 try:
691 try:
692 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
692 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
693 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
693 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
694 try:
694 try:
695 os.close(fh)
695 os.close(fh)
696 m = os.stat(fn).st_mode & 0777
696 m = os.stat(fn).st_mode & 0777
697 new_file_has_exec = m & EXECFLAGS
697 new_file_has_exec = m & EXECFLAGS
698 os.chmod(fn, m ^ EXECFLAGS)
698 os.chmod(fn, m ^ EXECFLAGS)
699 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
699 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
700 finally:
700 finally:
701 os.unlink(fn)
701 os.unlink(fn)
702 except (IOError, OSError):
702 except (IOError, OSError):
703 # we don't care, the user probably won't be able to commit anyway
703 # we don't care, the user probably won't be able to commit anyway
704 return False
704 return False
705 return not (new_file_has_exec or exec_flags_cannot_flip)
705 return not (new_file_has_exec or exec_flags_cannot_flip)
706
706
707 def checklink(path):
707 def checklink(path):
708 """check whether the given path is on a symlink-capable filesystem"""
708 """check whether the given path is on a symlink-capable filesystem"""
709 # mktemp is not racy because symlink creation will fail if the
709 # mktemp is not racy because symlink creation will fail if the
710 # file already exists
710 # file already exists
711 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
711 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
712 try:
712 try:
713 os.symlink(".", name)
713 os.symlink(".", name)
714 os.unlink(name)
714 os.unlink(name)
715 return True
715 return True
716 except (OSError, AttributeError):
716 except (OSError, AttributeError):
717 return False
717 return False
718
718
719 def checknlink(testfile):
719 def checknlink(testfile):
720 '''check whether hardlink count reporting works properly'''
720 '''check whether hardlink count reporting works properly'''
721 f = testfile + ".hgtmp"
721 f = testfile + ".hgtmp"
722
722
723 try:
723 try:
724 os_link(testfile, f)
724 os_link(testfile, f)
725 except OSError:
725 except OSError:
726 return False
726 return False
727
727
728 try:
728 try:
729 # nlinks() may behave differently for files on Windows shares if
729 # nlinks() may behave differently for files on Windows shares if
730 # the file is open.
730 # the file is open.
731 fd = open(f)
731 fd = open(f)
732 return nlinks(f) > 1
732 return nlinks(f) > 1
733 finally:
733 finally:
734 fd.close()
734 fd.close()
735 os.unlink(f)
735 os.unlink(f)
736
736
737 return False
737 return False
738
738
739 def endswithsep(path):
739 def endswithsep(path):
740 '''Check path ends with os.sep or os.altsep.'''
740 '''Check path ends with os.sep or os.altsep.'''
741 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
741 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
742
742
743 def splitpath(path):
743 def splitpath(path):
744 '''Split path by os.sep.
744 '''Split path by os.sep.
745 Note that this function does not use os.altsep because this is
745 Note that this function does not use os.altsep because this is
746 an alternative of simple "xxx.split(os.sep)".
746 an alternative of simple "xxx.split(os.sep)".
747 It is recommended to use os.path.normpath() before using this
747 It is recommended to use os.path.normpath() before using this
748 function if need.'''
748 function if need.'''
749 return path.split(os.sep)
749 return path.split(os.sep)
750
750
751 def gui():
751 def gui():
752 '''Are we running in a GUI?'''
752 '''Are we running in a GUI?'''
753 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
753 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
754
754
755 def mktempcopy(name, emptyok=False, createmode=None):
755 def mktempcopy(name, emptyok=False, createmode=None):
756 """Create a temporary file with the same contents from name
756 """Create a temporary file with the same contents from name
757
757
758 The permission bits are copied from the original file.
758 The permission bits are copied from the original file.
759
759
760 If the temporary file is going to be truncated immediately, you
760 If the temporary file is going to be truncated immediately, you
761 can use emptyok=True as an optimization.
761 can use emptyok=True as an optimization.
762
762
763 Returns the name of the temporary file.
763 Returns the name of the temporary file.
764 """
764 """
765 d, fn = os.path.split(name)
765 d, fn = os.path.split(name)
766 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
766 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
767 os.close(fd)
767 os.close(fd)
768 # Temporary files are created with mode 0600, which is usually not
768 # Temporary files are created with mode 0600, which is usually not
769 # what we want. If the original file already exists, just copy
769 # what we want. If the original file already exists, just copy
770 # its mode. Otherwise, manually obey umask.
770 # its mode. Otherwise, manually obey umask.
771 try:
771 try:
772 st_mode = os.lstat(name).st_mode & 0777
772 st_mode = os.lstat(name).st_mode & 0777
773 except OSError, inst:
773 except OSError, inst:
774 if inst.errno != errno.ENOENT:
774 if inst.errno != errno.ENOENT:
775 raise
775 raise
776 st_mode = createmode
776 st_mode = createmode
777 if st_mode is None:
777 if st_mode is None:
778 st_mode = ~umask
778 st_mode = ~umask
779 st_mode &= 0666
779 st_mode &= 0666
780 os.chmod(temp, st_mode)
780 os.chmod(temp, st_mode)
781 if emptyok:
781 if emptyok:
782 return temp
782 return temp
783 try:
783 try:
784 try:
784 try:
785 ifp = posixfile(name, "rb")
785 ifp = posixfile(name, "rb")
786 except IOError, inst:
786 except IOError, inst:
787 if inst.errno == errno.ENOENT:
787 if inst.errno == errno.ENOENT:
788 return temp
788 return temp
789 if not getattr(inst, 'filename', None):
789 if not getattr(inst, 'filename', None):
790 inst.filename = name
790 inst.filename = name
791 raise
791 raise
792 ofp = posixfile(temp, "wb")
792 ofp = posixfile(temp, "wb")
793 for chunk in filechunkiter(ifp):
793 for chunk in filechunkiter(ifp):
794 ofp.write(chunk)
794 ofp.write(chunk)
795 ifp.close()
795 ifp.close()
796 ofp.close()
796 ofp.close()
797 except:
797 except:
798 try: os.unlink(temp)
798 try: os.unlink(temp)
799 except: pass
799 except: pass
800 raise
800 raise
801 return temp
801 return temp
802
802
803 class atomictempfile(object):
803 class atomictempfile(object):
804 """file-like object that atomically updates a file
804 """file-like object that atomically updates a file
805
805
806 All writes will be redirected to a temporary copy of the original
806 All writes will be redirected to a temporary copy of the original
807 file. When rename is called, the copy is renamed to the original
807 file. When rename is called, the copy is renamed to the original
808 name, making the changes visible.
808 name, making the changes visible.
809 """
809 """
810 def __init__(self, name, mode='w+b', createmode=None):
810 def __init__(self, name, mode='w+b', createmode=None):
811 self.__name = name
811 self.__name = name
812 self._fp = None
812 self._fp = None
813 self.temp = mktempcopy(name, emptyok=('w' in mode),
813 self.temp = mktempcopy(name, emptyok=('w' in mode),
814 createmode=createmode)
814 createmode=createmode)
815 self._fp = posixfile(self.temp, mode)
815 self._fp = posixfile(self.temp, mode)
816
816
817 def __getattr__(self, name):
817 def __getattr__(self, name):
818 return getattr(self._fp, name)
818 return getattr(self._fp, name)
819
819
820 def rename(self):
820 def rename(self):
821 if not self._fp.closed:
821 if not self._fp.closed:
822 self._fp.close()
822 self._fp.close()
823 rename(self.temp, localpath(self.__name))
823 rename(self.temp, localpath(self.__name))
824
824
825 def __del__(self):
825 def __del__(self):
826 if not self._fp:
826 if not self._fp:
827 return
827 return
828 if not self._fp.closed:
828 if not self._fp.closed:
829 try:
829 try:
830 os.unlink(self.temp)
830 os.unlink(self.temp)
831 except: pass
831 except: pass
832 self._fp.close()
832 self._fp.close()
833
833
834 def makedirs(name, mode=None):
834 def makedirs(name, mode=None):
835 """recursive directory creation with parent mode inheritance"""
835 """recursive directory creation with parent mode inheritance"""
836 try:
836 try:
837 os.mkdir(name)
837 os.mkdir(name)
838 if mode is not None:
838 if mode is not None:
839 os.chmod(name, mode)
839 os.chmod(name, mode)
840 return
840 return
841 except OSError, err:
841 except OSError, err:
842 if err.errno == errno.EEXIST:
842 if err.errno == errno.EEXIST:
843 return
843 return
844 if err.errno != errno.ENOENT:
844 if not name or err.errno != errno.ENOENT:
845 raise
845 raise
846 parent = os.path.abspath(os.path.dirname(name))
846 parent = os.path.abspath(os.path.dirname(name))
847 makedirs(parent, mode)
847 makedirs(parent, mode)
848 makedirs(name, mode)
848 makedirs(name, mode)
849
849
850 class opener(object):
850 class opener(object):
851 """Open files relative to a base directory
851 """Open files relative to a base directory
852
852
853 This class is used to hide the details of COW semantics and
853 This class is used to hide the details of COW semantics and
854 remote file access from higher level code.
854 remote file access from higher level code.
855 """
855 """
856 def __init__(self, base, audit=True):
856 def __init__(self, base, audit=True):
857 self.base = base
857 self.base = base
858 if audit:
858 if audit:
859 self.auditor = path_auditor(base)
859 self.auditor = path_auditor(base)
860 else:
860 else:
861 self.auditor = always
861 self.auditor = always
862 self.createmode = None
862 self.createmode = None
863 self._trustnlink = None
863 self._trustnlink = None
864
864
865 @propertycache
865 @propertycache
866 def _can_symlink(self):
866 def _can_symlink(self):
867 return checklink(self.base)
867 return checklink(self.base)
868
868
869 def _fixfilemode(self, name):
869 def _fixfilemode(self, name):
870 if self.createmode is None:
870 if self.createmode is None:
871 return
871 return
872 os.chmod(name, self.createmode & 0666)
872 os.chmod(name, self.createmode & 0666)
873
873
874 def __call__(self, path, mode="r", text=False, atomictemp=False):
874 def __call__(self, path, mode="r", text=False, atomictemp=False):
875 self.auditor(path)
875 self.auditor(path)
876 f = os.path.join(self.base, path)
876 f = os.path.join(self.base, path)
877
877
878 if not text and "b" not in mode:
878 if not text and "b" not in mode:
879 mode += "b" # for that other OS
879 mode += "b" # for that other OS
880
880
881 nlink = -1
881 nlink = -1
882 st_mode = None
882 st_mode = None
883 dirname, basename = os.path.split(f)
883 dirname, basename = os.path.split(f)
884 # If basename is empty, then the path is malformed because it points
884 # If basename is empty, then the path is malformed because it points
885 # to a directory. Let the posixfile() call below raise IOError.
885 # to a directory. Let the posixfile() call below raise IOError.
886 if basename and mode not in ('r', 'rb'):
886 if basename and mode not in ('r', 'rb'):
887 if atomictemp:
887 if atomictemp:
888 if not os.path.isdir(dirname):
888 if not os.path.isdir(dirname):
889 makedirs(dirname, self.createmode)
889 makedirs(dirname, self.createmode)
890 return atomictempfile(f, mode, self.createmode)
890 return atomictempfile(f, mode, self.createmode)
891 try:
891 try:
892 if 'w' in mode:
892 if 'w' in mode:
893 st_mode = os.lstat(f).st_mode & 0777
893 st_mode = os.lstat(f).st_mode & 0777
894 os.unlink(f)
894 os.unlink(f)
895 nlink = 0
895 nlink = 0
896 else:
896 else:
897 # nlinks() may behave differently for files on Windows
897 # nlinks() may behave differently for files on Windows
898 # shares if the file is open.
898 # shares if the file is open.
899 fd = open(f)
899 fd = open(f)
900 nlink = nlinks(f)
900 nlink = nlinks(f)
901 fd.close()
901 fd.close()
902 except (OSError, IOError):
902 except (OSError, IOError):
903 nlink = 0
903 nlink = 0
904 if not os.path.isdir(dirname):
904 if not os.path.isdir(dirname):
905 makedirs(dirname, self.createmode)
905 makedirs(dirname, self.createmode)
906 if nlink > 0:
906 if nlink > 0:
907 if self._trustnlink is None:
907 if self._trustnlink is None:
908 self._trustnlink = nlink > 1 or checknlink(f)
908 self._trustnlink = nlink > 1 or checknlink(f)
909 if nlink > 1 or not self._trustnlink:
909 if nlink > 1 or not self._trustnlink:
910 rename(mktempcopy(f), f)
910 rename(mktempcopy(f), f)
911 fp = posixfile(f, mode)
911 fp = posixfile(f, mode)
912 if nlink == 0:
912 if nlink == 0:
913 if st_mode is None:
913 if st_mode is None:
914 self._fixfilemode(f)
914 self._fixfilemode(f)
915 else:
915 else:
916 os.chmod(f, st_mode)
916 os.chmod(f, st_mode)
917 return fp
917 return fp
918
918
919 def symlink(self, src, dst):
919 def symlink(self, src, dst):
920 self.auditor(dst)
920 self.auditor(dst)
921 linkname = os.path.join(self.base, dst)
921 linkname = os.path.join(self.base, dst)
922 try:
922 try:
923 os.unlink(linkname)
923 os.unlink(linkname)
924 except OSError:
924 except OSError:
925 pass
925 pass
926
926
927 dirname = os.path.dirname(linkname)
927 dirname = os.path.dirname(linkname)
928 if not os.path.exists(dirname):
928 if not os.path.exists(dirname):
929 makedirs(dirname, self.createmode)
929 makedirs(dirname, self.createmode)
930
930
931 if self._can_symlink:
931 if self._can_symlink:
932 try:
932 try:
933 os.symlink(src, linkname)
933 os.symlink(src, linkname)
934 except OSError, err:
934 except OSError, err:
935 raise OSError(err.errno, _('could not symlink to %r: %s') %
935 raise OSError(err.errno, _('could not symlink to %r: %s') %
936 (src, err.strerror), linkname)
936 (src, err.strerror), linkname)
937 else:
937 else:
938 f = self(dst, "w")
938 f = self(dst, "w")
939 f.write(src)
939 f.write(src)
940 f.close()
940 f.close()
941 self._fixfilemode(dst)
941 self._fixfilemode(dst)
942
942
943 class chunkbuffer(object):
943 class chunkbuffer(object):
944 """Allow arbitrary sized chunks of data to be efficiently read from an
944 """Allow arbitrary sized chunks of data to be efficiently read from an
945 iterator over chunks of arbitrary size."""
945 iterator over chunks of arbitrary size."""
946
946
947 def __init__(self, in_iter):
947 def __init__(self, in_iter):
948 """in_iter is the iterator that's iterating over the input chunks.
948 """in_iter is the iterator that's iterating over the input chunks.
949 targetsize is how big a buffer to try to maintain."""
949 targetsize is how big a buffer to try to maintain."""
950 def splitbig(chunks):
950 def splitbig(chunks):
951 for chunk in chunks:
951 for chunk in chunks:
952 if len(chunk) > 2**20:
952 if len(chunk) > 2**20:
953 pos = 0
953 pos = 0
954 while pos < len(chunk):
954 while pos < len(chunk):
955 end = pos + 2 ** 18
955 end = pos + 2 ** 18
956 yield chunk[pos:end]
956 yield chunk[pos:end]
957 pos = end
957 pos = end
958 else:
958 else:
959 yield chunk
959 yield chunk
960 self.iter = splitbig(in_iter)
960 self.iter = splitbig(in_iter)
961 self._queue = []
961 self._queue = []
962
962
963 def read(self, l):
963 def read(self, l):
964 """Read L bytes of data from the iterator of chunks of data.
964 """Read L bytes of data from the iterator of chunks of data.
965 Returns less than L bytes if the iterator runs dry."""
965 Returns less than L bytes if the iterator runs dry."""
966 left = l
966 left = l
967 buf = ''
967 buf = ''
968 queue = self._queue
968 queue = self._queue
969 while left > 0:
969 while left > 0:
970 # refill the queue
970 # refill the queue
971 if not queue:
971 if not queue:
972 target = 2**18
972 target = 2**18
973 for chunk in self.iter:
973 for chunk in self.iter:
974 queue.append(chunk)
974 queue.append(chunk)
975 target -= len(chunk)
975 target -= len(chunk)
976 if target <= 0:
976 if target <= 0:
977 break
977 break
978 if not queue:
978 if not queue:
979 break
979 break
980
980
981 chunk = queue.pop(0)
981 chunk = queue.pop(0)
982 left -= len(chunk)
982 left -= len(chunk)
983 if left < 0:
983 if left < 0:
984 queue.insert(0, chunk[left:])
984 queue.insert(0, chunk[left:])
985 buf += chunk[:left]
985 buf += chunk[:left]
986 else:
986 else:
987 buf += chunk
987 buf += chunk
988
988
989 return buf
989 return buf
990
990
991 def filechunkiter(f, size=65536, limit=None):
991 def filechunkiter(f, size=65536, limit=None):
992 """Create a generator that produces the data in the file size
992 """Create a generator that produces the data in the file size
993 (default 65536) bytes at a time, up to optional limit (default is
993 (default 65536) bytes at a time, up to optional limit (default is
994 to read all data). Chunks may be less than size bytes if the
994 to read all data). Chunks may be less than size bytes if the
995 chunk is the last chunk in the file, or the file is a socket or
995 chunk is the last chunk in the file, or the file is a socket or
996 some other type of file that sometimes reads less data than is
996 some other type of file that sometimes reads less data than is
997 requested."""
997 requested."""
998 assert size >= 0
998 assert size >= 0
999 assert limit is None or limit >= 0
999 assert limit is None or limit >= 0
1000 while True:
1000 while True:
1001 if limit is None:
1001 if limit is None:
1002 nbytes = size
1002 nbytes = size
1003 else:
1003 else:
1004 nbytes = min(limit, size)
1004 nbytes = min(limit, size)
1005 s = nbytes and f.read(nbytes)
1005 s = nbytes and f.read(nbytes)
1006 if not s:
1006 if not s:
1007 break
1007 break
1008 if limit:
1008 if limit:
1009 limit -= len(s)
1009 limit -= len(s)
1010 yield s
1010 yield s
1011
1011
1012 def makedate():
1012 def makedate():
1013 lt = time.localtime()
1013 lt = time.localtime()
1014 if lt[8] == 1 and time.daylight:
1014 if lt[8] == 1 and time.daylight:
1015 tz = time.altzone
1015 tz = time.altzone
1016 else:
1016 else:
1017 tz = time.timezone
1017 tz = time.timezone
1018 return time.mktime(lt), tz
1018 return time.mktime(lt), tz
1019
1019
1020 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1020 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1021 """represent a (unixtime, offset) tuple as a localized time.
1021 """represent a (unixtime, offset) tuple as a localized time.
1022 unixtime is seconds since the epoch, and offset is the time zone's
1022 unixtime is seconds since the epoch, and offset is the time zone's
1023 number of seconds away from UTC. if timezone is false, do not
1023 number of seconds away from UTC. if timezone is false, do not
1024 append time zone to string."""
1024 append time zone to string."""
1025 t, tz = date or makedate()
1025 t, tz = date or makedate()
1026 if t < 0:
1026 if t < 0:
1027 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1027 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1028 tz = 0
1028 tz = 0
1029 if "%1" in format or "%2" in format:
1029 if "%1" in format or "%2" in format:
1030 sign = (tz > 0) and "-" or "+"
1030 sign = (tz > 0) and "-" or "+"
1031 minutes = abs(tz) // 60
1031 minutes = abs(tz) // 60
1032 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1032 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1033 format = format.replace("%2", "%02d" % (minutes % 60))
1033 format = format.replace("%2", "%02d" % (minutes % 60))
1034 s = time.strftime(format, time.gmtime(float(t) - tz))
1034 s = time.strftime(format, time.gmtime(float(t) - tz))
1035 return s
1035 return s
1036
1036
1037 def shortdate(date=None):
1037 def shortdate(date=None):
1038 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1038 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1039 return datestr(date, format='%Y-%m-%d')
1039 return datestr(date, format='%Y-%m-%d')
1040
1040
1041 def strdate(string, format, defaults=[]):
1041 def strdate(string, format, defaults=[]):
1042 """parse a localized time string and return a (unixtime, offset) tuple.
1042 """parse a localized time string and return a (unixtime, offset) tuple.
1043 if the string cannot be parsed, ValueError is raised."""
1043 if the string cannot be parsed, ValueError is raised."""
1044 def timezone(string):
1044 def timezone(string):
1045 tz = string.split()[-1]
1045 tz = string.split()[-1]
1046 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1046 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1047 sign = (tz[0] == "+") and 1 or -1
1047 sign = (tz[0] == "+") and 1 or -1
1048 hours = int(tz[1:3])
1048 hours = int(tz[1:3])
1049 minutes = int(tz[3:5])
1049 minutes = int(tz[3:5])
1050 return -sign * (hours * 60 + minutes) * 60
1050 return -sign * (hours * 60 + minutes) * 60
1051 if tz == "GMT" or tz == "UTC":
1051 if tz == "GMT" or tz == "UTC":
1052 return 0
1052 return 0
1053 return None
1053 return None
1054
1054
1055 # NOTE: unixtime = localunixtime + offset
1055 # NOTE: unixtime = localunixtime + offset
1056 offset, date = timezone(string), string
1056 offset, date = timezone(string), string
1057 if offset != None:
1057 if offset != None:
1058 date = " ".join(string.split()[:-1])
1058 date = " ".join(string.split()[:-1])
1059
1059
1060 # add missing elements from defaults
1060 # add missing elements from defaults
1061 for part in defaults:
1061 for part in defaults:
1062 found = [True for p in part if ("%"+p) in format]
1062 found = [True for p in part if ("%"+p) in format]
1063 if not found:
1063 if not found:
1064 date += "@" + defaults[part]
1064 date += "@" + defaults[part]
1065 format += "@%" + part[0]
1065 format += "@%" + part[0]
1066
1066
1067 timetuple = time.strptime(date, format)
1067 timetuple = time.strptime(date, format)
1068 localunixtime = int(calendar.timegm(timetuple))
1068 localunixtime = int(calendar.timegm(timetuple))
1069 if offset is None:
1069 if offset is None:
1070 # local timezone
1070 # local timezone
1071 unixtime = int(time.mktime(timetuple))
1071 unixtime = int(time.mktime(timetuple))
1072 offset = unixtime - localunixtime
1072 offset = unixtime - localunixtime
1073 else:
1073 else:
1074 unixtime = localunixtime + offset
1074 unixtime = localunixtime + offset
1075 return unixtime, offset
1075 return unixtime, offset
1076
1076
1077 def parsedate(date, formats=None, defaults=None):
1077 def parsedate(date, formats=None, defaults=None):
1078 """parse a localized date/time string and return a (unixtime, offset) tuple.
1078 """parse a localized date/time string and return a (unixtime, offset) tuple.
1079
1079
1080 The date may be a "unixtime offset" string or in one of the specified
1080 The date may be a "unixtime offset" string or in one of the specified
1081 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1081 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1082 """
1082 """
1083 if not date:
1083 if not date:
1084 return 0, 0
1084 return 0, 0
1085 if isinstance(date, tuple) and len(date) == 2:
1085 if isinstance(date, tuple) and len(date) == 2:
1086 return date
1086 return date
1087 if not formats:
1087 if not formats:
1088 formats = defaultdateformats
1088 formats = defaultdateformats
1089 date = date.strip()
1089 date = date.strip()
1090 try:
1090 try:
1091 when, offset = map(int, date.split(' '))
1091 when, offset = map(int, date.split(' '))
1092 except ValueError:
1092 except ValueError:
1093 # fill out defaults
1093 # fill out defaults
1094 if not defaults:
1094 if not defaults:
1095 defaults = {}
1095 defaults = {}
1096 now = makedate()
1096 now = makedate()
1097 for part in "d mb yY HI M S".split():
1097 for part in "d mb yY HI M S".split():
1098 if part not in defaults:
1098 if part not in defaults:
1099 if part[0] in "HMS":
1099 if part[0] in "HMS":
1100 defaults[part] = "00"
1100 defaults[part] = "00"
1101 else:
1101 else:
1102 defaults[part] = datestr(now, "%" + part[0])
1102 defaults[part] = datestr(now, "%" + part[0])
1103
1103
1104 for format in formats:
1104 for format in formats:
1105 try:
1105 try:
1106 when, offset = strdate(date, format, defaults)
1106 when, offset = strdate(date, format, defaults)
1107 except (ValueError, OverflowError):
1107 except (ValueError, OverflowError):
1108 pass
1108 pass
1109 else:
1109 else:
1110 break
1110 break
1111 else:
1111 else:
1112 raise Abort(_('invalid date: %r') % date)
1112 raise Abort(_('invalid date: %r') % date)
1113 # validate explicit (probably user-specified) date and
1113 # validate explicit (probably user-specified) date and
1114 # time zone offset. values must fit in signed 32 bits for
1114 # time zone offset. values must fit in signed 32 bits for
1115 # current 32-bit linux runtimes. timezones go from UTC-12
1115 # current 32-bit linux runtimes. timezones go from UTC-12
1116 # to UTC+14
1116 # to UTC+14
1117 if abs(when) > 0x7fffffff:
1117 if abs(when) > 0x7fffffff:
1118 raise Abort(_('date exceeds 32 bits: %d') % when)
1118 raise Abort(_('date exceeds 32 bits: %d') % when)
1119 if offset < -50400 or offset > 43200:
1119 if offset < -50400 or offset > 43200:
1120 raise Abort(_('impossible time zone offset: %d') % offset)
1120 raise Abort(_('impossible time zone offset: %d') % offset)
1121 return when, offset
1121 return when, offset
1122
1122
1123 def matchdate(date):
1123 def matchdate(date):
1124 """Return a function that matches a given date match specifier
1124 """Return a function that matches a given date match specifier
1125
1125
1126 Formats include:
1126 Formats include:
1127
1127
1128 '{date}' match a given date to the accuracy provided
1128 '{date}' match a given date to the accuracy provided
1129
1129
1130 '<{date}' on or before a given date
1130 '<{date}' on or before a given date
1131
1131
1132 '>{date}' on or after a given date
1132 '>{date}' on or after a given date
1133
1133
1134 """
1134 """
1135
1135
1136 def lower(date):
1136 def lower(date):
1137 d = dict(mb="1", d="1")
1137 d = dict(mb="1", d="1")
1138 return parsedate(date, extendeddateformats, d)[0]
1138 return parsedate(date, extendeddateformats, d)[0]
1139
1139
1140 def upper(date):
1140 def upper(date):
1141 d = dict(mb="12", HI="23", M="59", S="59")
1141 d = dict(mb="12", HI="23", M="59", S="59")
1142 for days in "31 30 29".split():
1142 for days in "31 30 29".split():
1143 try:
1143 try:
1144 d["d"] = days
1144 d["d"] = days
1145 return parsedate(date, extendeddateformats, d)[0]
1145 return parsedate(date, extendeddateformats, d)[0]
1146 except:
1146 except:
1147 pass
1147 pass
1148 d["d"] = "28"
1148 d["d"] = "28"
1149 return parsedate(date, extendeddateformats, d)[0]
1149 return parsedate(date, extendeddateformats, d)[0]
1150
1150
1151 date = date.strip()
1151 date = date.strip()
1152 if date[0] == "<":
1152 if date[0] == "<":
1153 when = upper(date[1:])
1153 when = upper(date[1:])
1154 return lambda x: x <= when
1154 return lambda x: x <= when
1155 elif date[0] == ">":
1155 elif date[0] == ">":
1156 when = lower(date[1:])
1156 when = lower(date[1:])
1157 return lambda x: x >= when
1157 return lambda x: x >= when
1158 elif date[0] == "-":
1158 elif date[0] == "-":
1159 try:
1159 try:
1160 days = int(date[1:])
1160 days = int(date[1:])
1161 except ValueError:
1161 except ValueError:
1162 raise Abort(_("invalid day spec: %s") % date[1:])
1162 raise Abort(_("invalid day spec: %s") % date[1:])
1163 when = makedate()[0] - days * 3600 * 24
1163 when = makedate()[0] - days * 3600 * 24
1164 return lambda x: x >= when
1164 return lambda x: x >= when
1165 elif " to " in date:
1165 elif " to " in date:
1166 a, b = date.split(" to ")
1166 a, b = date.split(" to ")
1167 start, stop = lower(a), upper(b)
1167 start, stop = lower(a), upper(b)
1168 return lambda x: x >= start and x <= stop
1168 return lambda x: x >= start and x <= stop
1169 else:
1169 else:
1170 start, stop = lower(date), upper(date)
1170 start, stop = lower(date), upper(date)
1171 return lambda x: x >= start and x <= stop
1171 return lambda x: x >= start and x <= stop
1172
1172
1173 def shortuser(user):
1173 def shortuser(user):
1174 """Return a short representation of a user name or email address."""
1174 """Return a short representation of a user name or email address."""
1175 f = user.find('@')
1175 f = user.find('@')
1176 if f >= 0:
1176 if f >= 0:
1177 user = user[:f]
1177 user = user[:f]
1178 f = user.find('<')
1178 f = user.find('<')
1179 if f >= 0:
1179 if f >= 0:
1180 user = user[f + 1:]
1180 user = user[f + 1:]
1181 f = user.find(' ')
1181 f = user.find(' ')
1182 if f >= 0:
1182 if f >= 0:
1183 user = user[:f]
1183 user = user[:f]
1184 f = user.find('.')
1184 f = user.find('.')
1185 if f >= 0:
1185 if f >= 0:
1186 user = user[:f]
1186 user = user[:f]
1187 return user
1187 return user
1188
1188
1189 def email(author):
1189 def email(author):
1190 '''get email of author.'''
1190 '''get email of author.'''
1191 r = author.find('>')
1191 r = author.find('>')
1192 if r == -1:
1192 if r == -1:
1193 r = None
1193 r = None
1194 return author[author.find('<') + 1:r]
1194 return author[author.find('<') + 1:r]
1195
1195
1196 def ellipsis(text, maxlength=400):
1196 def ellipsis(text, maxlength=400):
1197 """Trim string to at most maxlength (default: 400) characters."""
1197 """Trim string to at most maxlength (default: 400) characters."""
1198 if len(text) <= maxlength:
1198 if len(text) <= maxlength:
1199 return text
1199 return text
1200 else:
1200 else:
1201 return "%s..." % (text[:maxlength - 3])
1201 return "%s..." % (text[:maxlength - 3])
1202
1202
1203 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1203 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1204 '''yield every hg repository under path, recursively.'''
1204 '''yield every hg repository under path, recursively.'''
1205 def errhandler(err):
1205 def errhandler(err):
1206 if err.filename == path:
1206 if err.filename == path:
1207 raise err
1207 raise err
1208 if followsym and hasattr(os.path, 'samestat'):
1208 if followsym and hasattr(os.path, 'samestat'):
1209 def _add_dir_if_not_there(dirlst, dirname):
1209 def _add_dir_if_not_there(dirlst, dirname):
1210 match = False
1210 match = False
1211 samestat = os.path.samestat
1211 samestat = os.path.samestat
1212 dirstat = os.stat(dirname)
1212 dirstat = os.stat(dirname)
1213 for lstdirstat in dirlst:
1213 for lstdirstat in dirlst:
1214 if samestat(dirstat, lstdirstat):
1214 if samestat(dirstat, lstdirstat):
1215 match = True
1215 match = True
1216 break
1216 break
1217 if not match:
1217 if not match:
1218 dirlst.append(dirstat)
1218 dirlst.append(dirstat)
1219 return not match
1219 return not match
1220 else:
1220 else:
1221 followsym = False
1221 followsym = False
1222
1222
1223 if (seen_dirs is None) and followsym:
1223 if (seen_dirs is None) and followsym:
1224 seen_dirs = []
1224 seen_dirs = []
1225 _add_dir_if_not_there(seen_dirs, path)
1225 _add_dir_if_not_there(seen_dirs, path)
1226 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1226 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1227 dirs.sort()
1227 dirs.sort()
1228 if '.hg' in dirs:
1228 if '.hg' in dirs:
1229 yield root # found a repository
1229 yield root # found a repository
1230 qroot = os.path.join(root, '.hg', 'patches')
1230 qroot = os.path.join(root, '.hg', 'patches')
1231 if os.path.isdir(os.path.join(qroot, '.hg')):
1231 if os.path.isdir(os.path.join(qroot, '.hg')):
1232 yield qroot # we have a patch queue repo here
1232 yield qroot # we have a patch queue repo here
1233 if recurse:
1233 if recurse:
1234 # avoid recursing inside the .hg directory
1234 # avoid recursing inside the .hg directory
1235 dirs.remove('.hg')
1235 dirs.remove('.hg')
1236 else:
1236 else:
1237 dirs[:] = [] # don't descend further
1237 dirs[:] = [] # don't descend further
1238 elif followsym:
1238 elif followsym:
1239 newdirs = []
1239 newdirs = []
1240 for d in dirs:
1240 for d in dirs:
1241 fname = os.path.join(root, d)
1241 fname = os.path.join(root, d)
1242 if _add_dir_if_not_there(seen_dirs, fname):
1242 if _add_dir_if_not_there(seen_dirs, fname):
1243 if os.path.islink(fname):
1243 if os.path.islink(fname):
1244 for hgname in walkrepos(fname, True, seen_dirs):
1244 for hgname in walkrepos(fname, True, seen_dirs):
1245 yield hgname
1245 yield hgname
1246 else:
1246 else:
1247 newdirs.append(d)
1247 newdirs.append(d)
1248 dirs[:] = newdirs
1248 dirs[:] = newdirs
1249
1249
1250 _rcpath = None
1250 _rcpath = None
1251
1251
1252 def os_rcpath():
1252 def os_rcpath():
1253 '''return default os-specific hgrc search path'''
1253 '''return default os-specific hgrc search path'''
1254 path = system_rcpath()
1254 path = system_rcpath()
1255 path.extend(user_rcpath())
1255 path.extend(user_rcpath())
1256 path = [os.path.normpath(f) for f in path]
1256 path = [os.path.normpath(f) for f in path]
1257 return path
1257 return path
1258
1258
1259 def rcpath():
1259 def rcpath():
1260 '''return hgrc search path. if env var HGRCPATH is set, use it.
1260 '''return hgrc search path. if env var HGRCPATH is set, use it.
1261 for each item in path, if directory, use files ending in .rc,
1261 for each item in path, if directory, use files ending in .rc,
1262 else use item.
1262 else use item.
1263 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1263 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1264 if no HGRCPATH, use default os-specific path.'''
1264 if no HGRCPATH, use default os-specific path.'''
1265 global _rcpath
1265 global _rcpath
1266 if _rcpath is None:
1266 if _rcpath is None:
1267 if 'HGRCPATH' in os.environ:
1267 if 'HGRCPATH' in os.environ:
1268 _rcpath = []
1268 _rcpath = []
1269 for p in os.environ['HGRCPATH'].split(os.pathsep):
1269 for p in os.environ['HGRCPATH'].split(os.pathsep):
1270 if not p:
1270 if not p:
1271 continue
1271 continue
1272 p = expandpath(p)
1272 p = expandpath(p)
1273 if os.path.isdir(p):
1273 if os.path.isdir(p):
1274 for f, kind in osutil.listdir(p):
1274 for f, kind in osutil.listdir(p):
1275 if f.endswith('.rc'):
1275 if f.endswith('.rc'):
1276 _rcpath.append(os.path.join(p, f))
1276 _rcpath.append(os.path.join(p, f))
1277 else:
1277 else:
1278 _rcpath.append(p)
1278 _rcpath.append(p)
1279 else:
1279 else:
1280 _rcpath = os_rcpath()
1280 _rcpath = os_rcpath()
1281 return _rcpath
1281 return _rcpath
1282
1282
1283 def bytecount(nbytes):
1283 def bytecount(nbytes):
1284 '''return byte count formatted as readable string, with units'''
1284 '''return byte count formatted as readable string, with units'''
1285
1285
1286 units = (
1286 units = (
1287 (100, 1 << 30, _('%.0f GB')),
1287 (100, 1 << 30, _('%.0f GB')),
1288 (10, 1 << 30, _('%.1f GB')),
1288 (10, 1 << 30, _('%.1f GB')),
1289 (1, 1 << 30, _('%.2f GB')),
1289 (1, 1 << 30, _('%.2f GB')),
1290 (100, 1 << 20, _('%.0f MB')),
1290 (100, 1 << 20, _('%.0f MB')),
1291 (10, 1 << 20, _('%.1f MB')),
1291 (10, 1 << 20, _('%.1f MB')),
1292 (1, 1 << 20, _('%.2f MB')),
1292 (1, 1 << 20, _('%.2f MB')),
1293 (100, 1 << 10, _('%.0f KB')),
1293 (100, 1 << 10, _('%.0f KB')),
1294 (10, 1 << 10, _('%.1f KB')),
1294 (10, 1 << 10, _('%.1f KB')),
1295 (1, 1 << 10, _('%.2f KB')),
1295 (1, 1 << 10, _('%.2f KB')),
1296 (1, 1, _('%.0f bytes')),
1296 (1, 1, _('%.0f bytes')),
1297 )
1297 )
1298
1298
1299 for multiplier, divisor, format in units:
1299 for multiplier, divisor, format in units:
1300 if nbytes >= divisor * multiplier:
1300 if nbytes >= divisor * multiplier:
1301 return format % (nbytes / float(divisor))
1301 return format % (nbytes / float(divisor))
1302 return units[-1][2] % nbytes
1302 return units[-1][2] % nbytes
1303
1303
1304 def drop_scheme(scheme, path):
1304 def drop_scheme(scheme, path):
1305 sc = scheme + ':'
1305 sc = scheme + ':'
1306 if path.startswith(sc):
1306 if path.startswith(sc):
1307 path = path[len(sc):]
1307 path = path[len(sc):]
1308 if path.startswith('//'):
1308 if path.startswith('//'):
1309 if scheme == 'file':
1309 if scheme == 'file':
1310 i = path.find('/', 2)
1310 i = path.find('/', 2)
1311 if i == -1:
1311 if i == -1:
1312 return ''
1312 return ''
1313 # On Windows, absolute paths are rooted at the current drive
1313 # On Windows, absolute paths are rooted at the current drive
1314 # root. On POSIX they are rooted at the file system root.
1314 # root. On POSIX they are rooted at the file system root.
1315 if os.name == 'nt':
1315 if os.name == 'nt':
1316 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1316 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1317 path = os.path.join(droot, path[i + 1:])
1317 path = os.path.join(droot, path[i + 1:])
1318 else:
1318 else:
1319 path = path[i:]
1319 path = path[i:]
1320 else:
1320 else:
1321 path = path[2:]
1321 path = path[2:]
1322 return path
1322 return path
1323
1323
1324 def uirepr(s):
1324 def uirepr(s):
1325 # Avoid double backslash in Windows path repr()
1325 # Avoid double backslash in Windows path repr()
1326 return repr(s).replace('\\\\', '\\')
1326 return repr(s).replace('\\\\', '\\')
1327
1327
1328 #### naming convention of below implementation follows 'textwrap' module
1328 #### naming convention of below implementation follows 'textwrap' module
1329
1329
1330 class MBTextWrapper(textwrap.TextWrapper):
1330 class MBTextWrapper(textwrap.TextWrapper):
1331 def __init__(self, **kwargs):
1331 def __init__(self, **kwargs):
1332 textwrap.TextWrapper.__init__(self, **kwargs)
1332 textwrap.TextWrapper.__init__(self, **kwargs)
1333
1333
1334 def _cutdown(self, str, space_left):
1334 def _cutdown(self, str, space_left):
1335 l = 0
1335 l = 0
1336 ucstr = unicode(str, encoding.encoding)
1336 ucstr = unicode(str, encoding.encoding)
1337 w = unicodedata.east_asian_width
1337 w = unicodedata.east_asian_width
1338 for i in xrange(len(ucstr)):
1338 for i in xrange(len(ucstr)):
1339 l += w(ucstr[i]) in 'WFA' and 2 or 1
1339 l += w(ucstr[i]) in 'WFA' and 2 or 1
1340 if space_left < l:
1340 if space_left < l:
1341 return (ucstr[:i].encode(encoding.encoding),
1341 return (ucstr[:i].encode(encoding.encoding),
1342 ucstr[i:].encode(encoding.encoding))
1342 ucstr[i:].encode(encoding.encoding))
1343 return str, ''
1343 return str, ''
1344
1344
1345 # ----------------------------------------
1345 # ----------------------------------------
1346 # overriding of base class
1346 # overriding of base class
1347
1347
1348 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1348 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1349 space_left = max(width - cur_len, 1)
1349 space_left = max(width - cur_len, 1)
1350
1350
1351 if self.break_long_words:
1351 if self.break_long_words:
1352 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1352 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1353 cur_line.append(cut)
1353 cur_line.append(cut)
1354 reversed_chunks[-1] = res
1354 reversed_chunks[-1] = res
1355 elif not cur_line:
1355 elif not cur_line:
1356 cur_line.append(reversed_chunks.pop())
1356 cur_line.append(reversed_chunks.pop())
1357
1357
1358 #### naming convention of above implementation follows 'textwrap' module
1358 #### naming convention of above implementation follows 'textwrap' module
1359
1359
1360 def wrap(line, width, initindent='', hangindent=''):
1360 def wrap(line, width, initindent='', hangindent=''):
1361 maxindent = max(len(hangindent), len(initindent))
1361 maxindent = max(len(hangindent), len(initindent))
1362 if width <= maxindent:
1362 if width <= maxindent:
1363 # adjust for weird terminal size
1363 # adjust for weird terminal size
1364 width = max(78, maxindent + 1)
1364 width = max(78, maxindent + 1)
1365 wrapper = MBTextWrapper(width=width,
1365 wrapper = MBTextWrapper(width=width,
1366 initial_indent=initindent,
1366 initial_indent=initindent,
1367 subsequent_indent=hangindent)
1367 subsequent_indent=hangindent)
1368 return wrapper.fill(line)
1368 return wrapper.fill(line)
1369
1369
1370 def iterlines(iterator):
1370 def iterlines(iterator):
1371 for chunk in iterator:
1371 for chunk in iterator:
1372 for line in chunk.splitlines():
1372 for line in chunk.splitlines():
1373 yield line
1373 yield line
1374
1374
1375 def expandpath(path):
1375 def expandpath(path):
1376 return os.path.expanduser(os.path.expandvars(path))
1376 return os.path.expanduser(os.path.expandvars(path))
1377
1377
1378 def hgcmd():
1378 def hgcmd():
1379 """Return the command used to execute current hg
1379 """Return the command used to execute current hg
1380
1380
1381 This is different from hgexecutable() because on Windows we want
1381 This is different from hgexecutable() because on Windows we want
1382 to avoid things opening new shell windows like batch files, so we
1382 to avoid things opening new shell windows like batch files, so we
1383 get either the python call or current executable.
1383 get either the python call or current executable.
1384 """
1384 """
1385 if main_is_frozen():
1385 if main_is_frozen():
1386 return [sys.executable]
1386 return [sys.executable]
1387 return gethgcmd()
1387 return gethgcmd()
1388
1388
1389 def rundetached(args, condfn):
1389 def rundetached(args, condfn):
1390 """Execute the argument list in a detached process.
1390 """Execute the argument list in a detached process.
1391
1391
1392 condfn is a callable which is called repeatedly and should return
1392 condfn is a callable which is called repeatedly and should return
1393 True once the child process is known to have started successfully.
1393 True once the child process is known to have started successfully.
1394 At this point, the child process PID is returned. If the child
1394 At this point, the child process PID is returned. If the child
1395 process fails to start or finishes before condfn() evaluates to
1395 process fails to start or finishes before condfn() evaluates to
1396 True, return -1.
1396 True, return -1.
1397 """
1397 """
1398 # Windows case is easier because the child process is either
1398 # Windows case is easier because the child process is either
1399 # successfully starting and validating the condition or exiting
1399 # successfully starting and validating the condition or exiting
1400 # on failure. We just poll on its PID. On Unix, if the child
1400 # on failure. We just poll on its PID. On Unix, if the child
1401 # process fails to start, it will be left in a zombie state until
1401 # process fails to start, it will be left in a zombie state until
1402 # the parent wait on it, which we cannot do since we expect a long
1402 # the parent wait on it, which we cannot do since we expect a long
1403 # running process on success. Instead we listen for SIGCHLD telling
1403 # running process on success. Instead we listen for SIGCHLD telling
1404 # us our child process terminated.
1404 # us our child process terminated.
1405 terminated = set()
1405 terminated = set()
1406 def handler(signum, frame):
1406 def handler(signum, frame):
1407 terminated.add(os.wait())
1407 terminated.add(os.wait())
1408 prevhandler = None
1408 prevhandler = None
1409 if hasattr(signal, 'SIGCHLD'):
1409 if hasattr(signal, 'SIGCHLD'):
1410 prevhandler = signal.signal(signal.SIGCHLD, handler)
1410 prevhandler = signal.signal(signal.SIGCHLD, handler)
1411 try:
1411 try:
1412 pid = spawndetached(args)
1412 pid = spawndetached(args)
1413 while not condfn():
1413 while not condfn():
1414 if ((pid in terminated or not testpid(pid))
1414 if ((pid in terminated or not testpid(pid))
1415 and not condfn()):
1415 and not condfn()):
1416 return -1
1416 return -1
1417 time.sleep(0.1)
1417 time.sleep(0.1)
1418 return pid
1418 return pid
1419 finally:
1419 finally:
1420 if prevhandler is not None:
1420 if prevhandler is not None:
1421 signal.signal(signal.SIGCHLD, prevhandler)
1421 signal.signal(signal.SIGCHLD, prevhandler)
1422
1422
1423 try:
1423 try:
1424 any, all = any, all
1424 any, all = any, all
1425 except NameError:
1425 except NameError:
1426 def any(iterable):
1426 def any(iterable):
1427 for i in iterable:
1427 for i in iterable:
1428 if i:
1428 if i:
1429 return True
1429 return True
1430 return False
1430 return False
1431
1431
1432 def all(iterable):
1432 def all(iterable):
1433 for i in iterable:
1433 for i in iterable:
1434 if not i:
1434 if not i:
1435 return False
1435 return False
1436 return True
1436 return True
1437
1437
1438 def interpolate(prefix, mapping, s, fn=None):
1438 def interpolate(prefix, mapping, s, fn=None):
1439 """Return the result of interpolating items in the mapping into string s.
1439 """Return the result of interpolating items in the mapping into string s.
1440
1440
1441 prefix is a single character string, or a two character string with
1441 prefix is a single character string, or a two character string with
1442 a backslash as the first character if the prefix needs to be escaped in
1442 a backslash as the first character if the prefix needs to be escaped in
1443 a regular expression.
1443 a regular expression.
1444
1444
1445 fn is an optional function that will be applied to the replacement text
1445 fn is an optional function that will be applied to the replacement text
1446 just before replacement.
1446 just before replacement.
1447 """
1447 """
1448 fn = fn or (lambda s: s)
1448 fn = fn or (lambda s: s)
1449 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1449 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1450 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1450 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1451
1451
1452 def getport(port):
1452 def getport(port):
1453 """Return the port for a given network service.
1453 """Return the port for a given network service.
1454
1454
1455 If port is an integer, it's returned as is. If it's a string, it's
1455 If port is an integer, it's returned as is. If it's a string, it's
1456 looked up using socket.getservbyname(). If there's no matching
1456 looked up using socket.getservbyname(). If there's no matching
1457 service, util.Abort is raised.
1457 service, util.Abort is raised.
1458 """
1458 """
1459 try:
1459 try:
1460 return int(port)
1460 return int(port)
1461 except ValueError:
1461 except ValueError:
1462 pass
1462 pass
1463
1463
1464 try:
1464 try:
1465 return socket.getservbyname(port)
1465 return socket.getservbyname(port)
1466 except socket.error:
1466 except socket.error:
1467 raise Abort(_("no port number associated with service '%s'") % port)
1467 raise Abort(_("no port number associated with service '%s'") % port)
1468
1468
1469 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1469 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1470 '0': False, 'no': False, 'false': False, 'off': False,
1470 '0': False, 'no': False, 'false': False, 'off': False,
1471 'never': False}
1471 'never': False}
1472
1472
1473 def parsebool(s):
1473 def parsebool(s):
1474 """Parse s into a boolean.
1474 """Parse s into a boolean.
1475
1475
1476 If s is not a valid boolean, returns None.
1476 If s is not a valid boolean, returns None.
1477 """
1477 """
1478 return _booleans.get(s.lower(), None)
1478 return _booleans.get(s.lower(), None)
@@ -1,561 +1,568 b''
1 $ cp "$TESTDIR"/printenv.py .
1 $ cp "$TESTDIR"/printenv.py .
2
2
3 Setting up test
3 Setting up test
4
4
5 $ hg init test
5 $ hg init test
6 $ cd test
6 $ cd test
7 $ echo 0 > afile
7 $ echo 0 > afile
8 $ hg add afile
8 $ hg add afile
9 $ hg commit -m "0.0"
9 $ hg commit -m "0.0"
10 $ echo 1 >> afile
10 $ echo 1 >> afile
11 $ hg commit -m "0.1"
11 $ hg commit -m "0.1"
12 $ echo 2 >> afile
12 $ echo 2 >> afile
13 $ hg commit -m "0.2"
13 $ hg commit -m "0.2"
14 $ echo 3 >> afile
14 $ echo 3 >> afile
15 $ hg commit -m "0.3"
15 $ hg commit -m "0.3"
16 $ hg update -C 0
16 $ hg update -C 0
17 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
17 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
18 $ echo 1 >> afile
18 $ echo 1 >> afile
19 $ hg commit -m "1.1"
19 $ hg commit -m "1.1"
20 created new head
20 created new head
21 $ echo 2 >> afile
21 $ echo 2 >> afile
22 $ hg commit -m "1.2"
22 $ hg commit -m "1.2"
23 $ echo "a line" > fred
23 $ echo "a line" > fred
24 $ echo 3 >> afile
24 $ echo 3 >> afile
25 $ hg add fred
25 $ hg add fred
26 $ hg commit -m "1.3"
26 $ hg commit -m "1.3"
27 $ hg mv afile adifferentfile
27 $ hg mv afile adifferentfile
28 $ hg commit -m "1.3m"
28 $ hg commit -m "1.3m"
29 $ hg update -C 3
29 $ hg update -C 3
30 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
30 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
31 $ hg mv afile anotherfile
31 $ hg mv afile anotherfile
32 $ hg commit -m "0.3m"
32 $ hg commit -m "0.3m"
33 $ hg verify
33 $ hg verify
34 checking changesets
34 checking changesets
35 checking manifests
35 checking manifests
36 crosschecking files in changesets and manifests
36 crosschecking files in changesets and manifests
37 checking files
37 checking files
38 4 files, 9 changesets, 7 total revisions
38 4 files, 9 changesets, 7 total revisions
39 $ cd ..
39 $ cd ..
40 $ hg init empty
40 $ hg init empty
41
41
42 Bundle --all
42 Bundle --all
43
43
44 $ hg -R test bundle --all all.hg
44 $ hg -R test bundle --all all.hg
45 9 changesets found
45 9 changesets found
46
46
47 Bundle test to full.hg
47 Bundle test to full.hg
48
48
49 $ hg -R test bundle full.hg empty
49 $ hg -R test bundle full.hg empty
50 searching for changes
50 searching for changes
51 9 changesets found
51 9 changesets found
52
52
53 Unbundle full.hg in test
53 Unbundle full.hg in test
54
54
55 $ hg -R test unbundle full.hg
55 $ hg -R test unbundle full.hg
56 adding changesets
56 adding changesets
57 adding manifests
57 adding manifests
58 adding file changes
58 adding file changes
59 added 0 changesets with 0 changes to 4 files
59 added 0 changesets with 0 changes to 4 files
60 (run 'hg update' to get a working copy)
60 (run 'hg update' to get a working copy)
61
61
62 Verify empty
62 Verify empty
63
63
64 $ hg -R empty heads
64 $ hg -R empty heads
65 [1]
65 [1]
66 $ hg -R empty verify
66 $ hg -R empty verify
67 checking changesets
67 checking changesets
68 checking manifests
68 checking manifests
69 crosschecking files in changesets and manifests
69 crosschecking files in changesets and manifests
70 checking files
70 checking files
71 0 files, 0 changesets, 0 total revisions
71 0 files, 0 changesets, 0 total revisions
72
72
73 Pull full.hg into test (using --cwd)
73 Pull full.hg into test (using --cwd)
74
74
75 $ hg --cwd test pull ../full.hg
75 $ hg --cwd test pull ../full.hg
76 pulling from ../full.hg
76 pulling from ../full.hg
77 searching for changes
77 searching for changes
78 no changes found
78 no changes found
79
79
80 Pull full.hg into empty (using --cwd)
80 Pull full.hg into empty (using --cwd)
81
81
82 $ hg --cwd empty pull ../full.hg
82 $ hg --cwd empty pull ../full.hg
83 pulling from ../full.hg
83 pulling from ../full.hg
84 requesting all changes
84 requesting all changes
85 adding changesets
85 adding changesets
86 adding manifests
86 adding manifests
87 adding file changes
87 adding file changes
88 added 9 changesets with 7 changes to 4 files (+1 heads)
88 added 9 changesets with 7 changes to 4 files (+1 heads)
89 (run 'hg heads' to see heads, 'hg merge' to merge)
89 (run 'hg heads' to see heads, 'hg merge' to merge)
90
90
91 Rollback empty
91 Rollback empty
92
92
93 $ hg -R empty rollback
93 $ hg -R empty rollback
94 rolling back to revision -1 (undo pull)
94 rolling back to revision -1 (undo pull)
95
95
96 Pull full.hg into empty again (using --cwd)
96 Pull full.hg into empty again (using --cwd)
97
97
98 $ hg --cwd empty pull ../full.hg
98 $ hg --cwd empty pull ../full.hg
99 pulling from ../full.hg
99 pulling from ../full.hg
100 requesting all changes
100 requesting all changes
101 adding changesets
101 adding changesets
102 adding manifests
102 adding manifests
103 adding file changes
103 adding file changes
104 added 9 changesets with 7 changes to 4 files (+1 heads)
104 added 9 changesets with 7 changes to 4 files (+1 heads)
105 (run 'hg heads' to see heads, 'hg merge' to merge)
105 (run 'hg heads' to see heads, 'hg merge' to merge)
106
106
107 Pull full.hg into test (using -R)
107 Pull full.hg into test (using -R)
108
108
109 $ hg -R test pull full.hg
109 $ hg -R test pull full.hg
110 pulling from full.hg
110 pulling from full.hg
111 searching for changes
111 searching for changes
112 no changes found
112 no changes found
113
113
114 Pull full.hg into empty (using -R)
114 Pull full.hg into empty (using -R)
115
115
116 $ hg -R empty pull full.hg
116 $ hg -R empty pull full.hg
117 pulling from full.hg
117 pulling from full.hg
118 searching for changes
118 searching for changes
119 no changes found
119 no changes found
120
120
121 Rollback empty
121 Rollback empty
122
122
123 $ hg -R empty rollback
123 $ hg -R empty rollback
124 rolling back to revision -1 (undo pull)
124 rolling back to revision -1 (undo pull)
125
125
126 Pull full.hg into empty again (using -R)
126 Pull full.hg into empty again (using -R)
127
127
128 $ hg -R empty pull full.hg
128 $ hg -R empty pull full.hg
129 pulling from full.hg
129 pulling from full.hg
130 requesting all changes
130 requesting all changes
131 adding changesets
131 adding changesets
132 adding manifests
132 adding manifests
133 adding file changes
133 adding file changes
134 added 9 changesets with 7 changes to 4 files (+1 heads)
134 added 9 changesets with 7 changes to 4 files (+1 heads)
135 (run 'hg heads' to see heads, 'hg merge' to merge)
135 (run 'hg heads' to see heads, 'hg merge' to merge)
136
136
137 Log -R full.hg in fresh empty
137 Log -R full.hg in fresh empty
138
138
139 $ rm -r empty
139 $ rm -r empty
140 $ hg init empty
140 $ hg init empty
141 $ cd empty
141 $ cd empty
142 $ hg -R bundle://../full.hg log
142 $ hg -R bundle://../full.hg log
143 changeset: 8:aa35859c02ea
143 changeset: 8:aa35859c02ea
144 tag: tip
144 tag: tip
145 parent: 3:eebf5a27f8ca
145 parent: 3:eebf5a27f8ca
146 user: test
146 user: test
147 date: Thu Jan 01 00:00:00 1970 +0000
147 date: Thu Jan 01 00:00:00 1970 +0000
148 summary: 0.3m
148 summary: 0.3m
149
149
150 changeset: 7:a6a34bfa0076
150 changeset: 7:a6a34bfa0076
151 user: test
151 user: test
152 date: Thu Jan 01 00:00:00 1970 +0000
152 date: Thu Jan 01 00:00:00 1970 +0000
153 summary: 1.3m
153 summary: 1.3m
154
154
155 changeset: 6:7373c1169842
155 changeset: 6:7373c1169842
156 user: test
156 user: test
157 date: Thu Jan 01 00:00:00 1970 +0000
157 date: Thu Jan 01 00:00:00 1970 +0000
158 summary: 1.3
158 summary: 1.3
159
159
160 changeset: 5:1bb50a9436a7
160 changeset: 5:1bb50a9436a7
161 user: test
161 user: test
162 date: Thu Jan 01 00:00:00 1970 +0000
162 date: Thu Jan 01 00:00:00 1970 +0000
163 summary: 1.2
163 summary: 1.2
164
164
165 changeset: 4:095197eb4973
165 changeset: 4:095197eb4973
166 parent: 0:f9ee2f85a263
166 parent: 0:f9ee2f85a263
167 user: test
167 user: test
168 date: Thu Jan 01 00:00:00 1970 +0000
168 date: Thu Jan 01 00:00:00 1970 +0000
169 summary: 1.1
169 summary: 1.1
170
170
171 changeset: 3:eebf5a27f8ca
171 changeset: 3:eebf5a27f8ca
172 user: test
172 user: test
173 date: Thu Jan 01 00:00:00 1970 +0000
173 date: Thu Jan 01 00:00:00 1970 +0000
174 summary: 0.3
174 summary: 0.3
175
175
176 changeset: 2:e38ba6f5b7e0
176 changeset: 2:e38ba6f5b7e0
177 user: test
177 user: test
178 date: Thu Jan 01 00:00:00 1970 +0000
178 date: Thu Jan 01 00:00:00 1970 +0000
179 summary: 0.2
179 summary: 0.2
180
180
181 changeset: 1:34c2bf6b0626
181 changeset: 1:34c2bf6b0626
182 user: test
182 user: test
183 date: Thu Jan 01 00:00:00 1970 +0000
183 date: Thu Jan 01 00:00:00 1970 +0000
184 summary: 0.1
184 summary: 0.1
185
185
186 changeset: 0:f9ee2f85a263
186 changeset: 0:f9ee2f85a263
187 user: test
187 user: test
188 date: Thu Jan 01 00:00:00 1970 +0000
188 date: Thu Jan 01 00:00:00 1970 +0000
189 summary: 0.0
189 summary: 0.0
190
190
191
191
192 Pull ../full.hg into empty (with hook)
192 Pull ../full.hg into empty (with hook)
193
193
194 $ echo '[hooks]' >> .hg/hgrc
194 $ echo '[hooks]' >> .hg/hgrc
195 $ echo 'changegroup = python ../printenv.py changegroup' >> .hg/hgrc
195 $ echo 'changegroup = python ../printenv.py changegroup' >> .hg/hgrc
196
196
197 doesn't work (yet ?)
197 doesn't work (yet ?)
198
198
199 hg -R bundle://../full.hg verify
199 hg -R bundle://../full.hg verify
200
200
201 $ hg pull bundle://../full.hg
201 $ hg pull bundle://../full.hg
202 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:../full.hg
202 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:../full.hg
203 pulling from bundle://../full.hg
203 pulling from bundle://../full.hg
204 requesting all changes
204 requesting all changes
205 adding changesets
205 adding changesets
206 adding manifests
206 adding manifests
207 adding file changes
207 adding file changes
208 added 9 changesets with 7 changes to 4 files (+1 heads)
208 added 9 changesets with 7 changes to 4 files (+1 heads)
209 (run 'hg heads' to see heads, 'hg merge' to merge)
209 (run 'hg heads' to see heads, 'hg merge' to merge)
210
210
211 Rollback empty
211 Rollback empty
212
212
213 $ hg rollback
213 $ hg rollback
214 rolling back to revision -1 (undo pull)
214 rolling back to revision -1 (undo pull)
215 $ cd ..
215 $ cd ..
216
216
217 Log -R bundle:empty+full.hg
217 Log -R bundle:empty+full.hg
218
218
219 $ hg -R bundle:empty+full.hg log --template="{rev} "; echo ""
219 $ hg -R bundle:empty+full.hg log --template="{rev} "; echo ""
220 8 7 6 5 4 3 2 1 0
220 8 7 6 5 4 3 2 1 0
221
221
222 Pull full.hg into empty again (using -R; with hook)
222 Pull full.hg into empty again (using -R; with hook)
223
223
224 $ hg -R empty pull full.hg
224 $ hg -R empty pull full.hg
225 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
225 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
226 pulling from full.hg
226 pulling from full.hg
227 requesting all changes
227 requesting all changes
228 adding changesets
228 adding changesets
229 adding manifests
229 adding manifests
230 adding file changes
230 adding file changes
231 added 9 changesets with 7 changes to 4 files (+1 heads)
231 added 9 changesets with 7 changes to 4 files (+1 heads)
232 (run 'hg heads' to see heads, 'hg merge' to merge)
232 (run 'hg heads' to see heads, 'hg merge' to merge)
233
233
234 Create partial clones
234 Create partial clones
235
235
236 $ rm -r empty
236 $ rm -r empty
237 $ hg init empty
237 $ hg init empty
238 $ hg clone -r 3 test partial
238 $ hg clone -r 3 test partial
239 adding changesets
239 adding changesets
240 adding manifests
240 adding manifests
241 adding file changes
241 adding file changes
242 added 4 changesets with 4 changes to 1 files
242 added 4 changesets with 4 changes to 1 files
243 updating to branch default
243 updating to branch default
244 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
244 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 $ hg clone partial partial2
245 $ hg clone partial partial2
246 updating to branch default
246 updating to branch default
247 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
247 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 $ cd partial
248 $ cd partial
249
249
250 Log -R full.hg in partial
250 Log -R full.hg in partial
251
251
252 $ hg -R bundle://../full.hg log
252 $ hg -R bundle://../full.hg log
253 changeset: 8:aa35859c02ea
253 changeset: 8:aa35859c02ea
254 tag: tip
254 tag: tip
255 parent: 3:eebf5a27f8ca
255 parent: 3:eebf5a27f8ca
256 user: test
256 user: test
257 date: Thu Jan 01 00:00:00 1970 +0000
257 date: Thu Jan 01 00:00:00 1970 +0000
258 summary: 0.3m
258 summary: 0.3m
259
259
260 changeset: 7:a6a34bfa0076
260 changeset: 7:a6a34bfa0076
261 user: test
261 user: test
262 date: Thu Jan 01 00:00:00 1970 +0000
262 date: Thu Jan 01 00:00:00 1970 +0000
263 summary: 1.3m
263 summary: 1.3m
264
264
265 changeset: 6:7373c1169842
265 changeset: 6:7373c1169842
266 user: test
266 user: test
267 date: Thu Jan 01 00:00:00 1970 +0000
267 date: Thu Jan 01 00:00:00 1970 +0000
268 summary: 1.3
268 summary: 1.3
269
269
270 changeset: 5:1bb50a9436a7
270 changeset: 5:1bb50a9436a7
271 user: test
271 user: test
272 date: Thu Jan 01 00:00:00 1970 +0000
272 date: Thu Jan 01 00:00:00 1970 +0000
273 summary: 1.2
273 summary: 1.2
274
274
275 changeset: 4:095197eb4973
275 changeset: 4:095197eb4973
276 parent: 0:f9ee2f85a263
276 parent: 0:f9ee2f85a263
277 user: test
277 user: test
278 date: Thu Jan 01 00:00:00 1970 +0000
278 date: Thu Jan 01 00:00:00 1970 +0000
279 summary: 1.1
279 summary: 1.1
280
280
281 changeset: 3:eebf5a27f8ca
281 changeset: 3:eebf5a27f8ca
282 user: test
282 user: test
283 date: Thu Jan 01 00:00:00 1970 +0000
283 date: Thu Jan 01 00:00:00 1970 +0000
284 summary: 0.3
284 summary: 0.3
285
285
286 changeset: 2:e38ba6f5b7e0
286 changeset: 2:e38ba6f5b7e0
287 user: test
287 user: test
288 date: Thu Jan 01 00:00:00 1970 +0000
288 date: Thu Jan 01 00:00:00 1970 +0000
289 summary: 0.2
289 summary: 0.2
290
290
291 changeset: 1:34c2bf6b0626
291 changeset: 1:34c2bf6b0626
292 user: test
292 user: test
293 date: Thu Jan 01 00:00:00 1970 +0000
293 date: Thu Jan 01 00:00:00 1970 +0000
294 summary: 0.1
294 summary: 0.1
295
295
296 changeset: 0:f9ee2f85a263
296 changeset: 0:f9ee2f85a263
297 user: test
297 user: test
298 date: Thu Jan 01 00:00:00 1970 +0000
298 date: Thu Jan 01 00:00:00 1970 +0000
299 summary: 0.0
299 summary: 0.0
300
300
301
301
302 Incoming full.hg in partial
302 Incoming full.hg in partial
303
303
304 $ hg incoming bundle://../full.hg
304 $ hg incoming bundle://../full.hg
305 comparing with bundle://../full.hg
305 comparing with bundle://../full.hg
306 searching for changes
306 searching for changes
307 changeset: 4:095197eb4973
307 changeset: 4:095197eb4973
308 parent: 0:f9ee2f85a263
308 parent: 0:f9ee2f85a263
309 user: test
309 user: test
310 date: Thu Jan 01 00:00:00 1970 +0000
310 date: Thu Jan 01 00:00:00 1970 +0000
311 summary: 1.1
311 summary: 1.1
312
312
313 changeset: 5:1bb50a9436a7
313 changeset: 5:1bb50a9436a7
314 user: test
314 user: test
315 date: Thu Jan 01 00:00:00 1970 +0000
315 date: Thu Jan 01 00:00:00 1970 +0000
316 summary: 1.2
316 summary: 1.2
317
317
318 changeset: 6:7373c1169842
318 changeset: 6:7373c1169842
319 user: test
319 user: test
320 date: Thu Jan 01 00:00:00 1970 +0000
320 date: Thu Jan 01 00:00:00 1970 +0000
321 summary: 1.3
321 summary: 1.3
322
322
323 changeset: 7:a6a34bfa0076
323 changeset: 7:a6a34bfa0076
324 user: test
324 user: test
325 date: Thu Jan 01 00:00:00 1970 +0000
325 date: Thu Jan 01 00:00:00 1970 +0000
326 summary: 1.3m
326 summary: 1.3m
327
327
328 changeset: 8:aa35859c02ea
328 changeset: 8:aa35859c02ea
329 tag: tip
329 tag: tip
330 parent: 3:eebf5a27f8ca
330 parent: 3:eebf5a27f8ca
331 user: test
331 user: test
332 date: Thu Jan 01 00:00:00 1970 +0000
332 date: Thu Jan 01 00:00:00 1970 +0000
333 summary: 0.3m
333 summary: 0.3m
334
334
335
335
336 Outgoing -R full.hg vs partial2 in partial
336 Outgoing -R full.hg vs partial2 in partial
337
337
338 $ hg -R bundle://../full.hg outgoing ../partial2
338 $ hg -R bundle://../full.hg outgoing ../partial2
339 comparing with ../partial2
339 comparing with ../partial2
340 searching for changes
340 searching for changes
341 changeset: 4:095197eb4973
341 changeset: 4:095197eb4973
342 parent: 0:f9ee2f85a263
342 parent: 0:f9ee2f85a263
343 user: test
343 user: test
344 date: Thu Jan 01 00:00:00 1970 +0000
344 date: Thu Jan 01 00:00:00 1970 +0000
345 summary: 1.1
345 summary: 1.1
346
346
347 changeset: 5:1bb50a9436a7
347 changeset: 5:1bb50a9436a7
348 user: test
348 user: test
349 date: Thu Jan 01 00:00:00 1970 +0000
349 date: Thu Jan 01 00:00:00 1970 +0000
350 summary: 1.2
350 summary: 1.2
351
351
352 changeset: 6:7373c1169842
352 changeset: 6:7373c1169842
353 user: test
353 user: test
354 date: Thu Jan 01 00:00:00 1970 +0000
354 date: Thu Jan 01 00:00:00 1970 +0000
355 summary: 1.3
355 summary: 1.3
356
356
357 changeset: 7:a6a34bfa0076
357 changeset: 7:a6a34bfa0076
358 user: test
358 user: test
359 date: Thu Jan 01 00:00:00 1970 +0000
359 date: Thu Jan 01 00:00:00 1970 +0000
360 summary: 1.3m
360 summary: 1.3m
361
361
362 changeset: 8:aa35859c02ea
362 changeset: 8:aa35859c02ea
363 tag: tip
363 tag: tip
364 parent: 3:eebf5a27f8ca
364 parent: 3:eebf5a27f8ca
365 user: test
365 user: test
366 date: Thu Jan 01 00:00:00 1970 +0000
366 date: Thu Jan 01 00:00:00 1970 +0000
367 summary: 0.3m
367 summary: 0.3m
368
368
369
369
370 Outgoing -R does-not-exist.hg vs partial2 in partial
370 Outgoing -R does-not-exist.hg vs partial2 in partial
371
371
372 $ hg -R bundle://../does-not-exist.hg outgoing ../partial2
372 $ hg -R bundle://../does-not-exist.hg outgoing ../partial2
373 abort: No such file or directory: ../does-not-exist.hg
373 abort: No such file or directory: ../does-not-exist.hg
374 [255]
374 [255]
375 $ cd ..
375 $ cd ..
376
376
377 Direct clone from bundle (all-history)
377 Direct clone from bundle (all-history)
378
378
379 $ hg clone full.hg full-clone
379 $ hg clone full.hg full-clone
380 requesting all changes
380 requesting all changes
381 adding changesets
381 adding changesets
382 adding manifests
382 adding manifests
383 adding file changes
383 adding file changes
384 added 9 changesets with 7 changes to 4 files (+1 heads)
384 added 9 changesets with 7 changes to 4 files (+1 heads)
385 updating to branch default
385 updating to branch default
386 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
386 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
387 $ hg -R full-clone heads
387 $ hg -R full-clone heads
388 changeset: 8:aa35859c02ea
388 changeset: 8:aa35859c02ea
389 tag: tip
389 tag: tip
390 parent: 3:eebf5a27f8ca
390 parent: 3:eebf5a27f8ca
391 user: test
391 user: test
392 date: Thu Jan 01 00:00:00 1970 +0000
392 date: Thu Jan 01 00:00:00 1970 +0000
393 summary: 0.3m
393 summary: 0.3m
394
394
395 changeset: 7:a6a34bfa0076
395 changeset: 7:a6a34bfa0076
396 user: test
396 user: test
397 date: Thu Jan 01 00:00:00 1970 +0000
397 date: Thu Jan 01 00:00:00 1970 +0000
398 summary: 1.3m
398 summary: 1.3m
399
399
400 $ rm -r full-clone
400 $ rm -r full-clone
401
401
402 When cloning from a non-copiable repository into '', do not
403 recurse infinitely (issue 2528)
404
405 $ hg clone full.hg ''
406 abort: No such file or directory
407 [255]
408
402 test for http://mercurial.selenic.com/bts/issue216
409 test for http://mercurial.selenic.com/bts/issue216
403
410
404 Unbundle incremental bundles into fresh empty in one go
411 Unbundle incremental bundles into fresh empty in one go
405
412
406 $ rm -r empty
413 $ rm -r empty
407 $ hg init empty
414 $ hg init empty
408 $ hg -R test bundle --base null -r 0 ../0.hg
415 $ hg -R test bundle --base null -r 0 ../0.hg
409 1 changesets found
416 1 changesets found
410 $ hg -R test bundle --base 0 -r 1 ../1.hg
417 $ hg -R test bundle --base 0 -r 1 ../1.hg
411 1 changesets found
418 1 changesets found
412 $ hg -R empty unbundle -u ../0.hg ../1.hg
419 $ hg -R empty unbundle -u ../0.hg ../1.hg
413 adding changesets
420 adding changesets
414 adding manifests
421 adding manifests
415 adding file changes
422 adding file changes
416 added 1 changesets with 1 changes to 1 files
423 added 1 changesets with 1 changes to 1 files
417 adding changesets
424 adding changesets
418 adding manifests
425 adding manifests
419 adding file changes
426 adding file changes
420 added 1 changesets with 1 changes to 1 files
427 added 1 changesets with 1 changes to 1 files
421 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
428 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
422
429
423 test for 540d1059c802
430 test for 540d1059c802
424
431
425 test for 540d1059c802
432 test for 540d1059c802
426
433
427 $ hg init orig
434 $ hg init orig
428 $ cd orig
435 $ cd orig
429 $ echo foo > foo
436 $ echo foo > foo
430 $ hg add foo
437 $ hg add foo
431 $ hg ci -m 'add foo'
438 $ hg ci -m 'add foo'
432
439
433 $ hg clone . ../copy
440 $ hg clone . ../copy
434 updating to branch default
441 updating to branch default
435 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
442 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 $ hg tag foo
443 $ hg tag foo
437
444
438 $ cd ../copy
445 $ cd ../copy
439 $ echo >> foo
446 $ echo >> foo
440 $ hg ci -m 'change foo'
447 $ hg ci -m 'change foo'
441 $ hg bundle ../bundle.hg ../orig
448 $ hg bundle ../bundle.hg ../orig
442 searching for changes
449 searching for changes
443 1 changesets found
450 1 changesets found
444
451
445 $ cd ../orig
452 $ cd ../orig
446 $ hg incoming ../bundle.hg
453 $ hg incoming ../bundle.hg
447 comparing with ../bundle.hg
454 comparing with ../bundle.hg
448 searching for changes
455 searching for changes
449 changeset: 2:ed1b79f46b9a
456 changeset: 2:ed1b79f46b9a
450 tag: tip
457 tag: tip
451 parent: 0:bbd179dfa0a7
458 parent: 0:bbd179dfa0a7
452 user: test
459 user: test
453 date: Thu Jan 01 00:00:00 1970 +0000
460 date: Thu Jan 01 00:00:00 1970 +0000
454 summary: change foo
461 summary: change foo
455
462
456 $ cd ..
463 $ cd ..
457
464
458 test for http://mercurial.selenic.com/bts/issue1144
465 test for http://mercurial.selenic.com/bts/issue1144
459
466
460 test that verify bundle does not traceback
467 test that verify bundle does not traceback
461
468
462 partial history bundle, fails w/ unkown parent
469 partial history bundle, fails w/ unkown parent
463
470
464 $ hg -R bundle.hg verify
471 $ hg -R bundle.hg verify
465 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
472 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
466 [255]
473 [255]
467
474
468 full history bundle, refuses to verify non-local repo
475 full history bundle, refuses to verify non-local repo
469
476
470 $ hg -R all.hg verify
477 $ hg -R all.hg verify
471 abort: cannot verify bundle or remote repos
478 abort: cannot verify bundle or remote repos
472 [255]
479 [255]
473
480
474 but, regular verify must continue to work
481 but, regular verify must continue to work
475
482
476 $ hg -R orig verify
483 $ hg -R orig verify
477 checking changesets
484 checking changesets
478 checking manifests
485 checking manifests
479 crosschecking files in changesets and manifests
486 crosschecking files in changesets and manifests
480 checking files
487 checking files
481 2 files, 2 changesets, 2 total revisions
488 2 files, 2 changesets, 2 total revisions
482
489
483 diff against bundle
490 diff against bundle
484
491
485 $ hg init b
492 $ hg init b
486 $ cd b
493 $ cd b
487 $ hg -R ../all.hg diff -r tip
494 $ hg -R ../all.hg diff -r tip
488 diff -r aa35859c02ea anotherfile
495 diff -r aa35859c02ea anotherfile
489 --- a/anotherfile Thu Jan 01 00:00:00 1970 +0000
496 --- a/anotherfile Thu Jan 01 00:00:00 1970 +0000
490 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
497 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
491 @@ -1,4 +0,0 @@
498 @@ -1,4 +0,0 @@
492 -0
499 -0
493 -1
500 -1
494 -2
501 -2
495 -3
502 -3
496 $ cd ..
503 $ cd ..
497
504
498 bundle single branch
505 bundle single branch
499
506
500 $ hg init branchy
507 $ hg init branchy
501 $ cd branchy
508 $ cd branchy
502 $ echo a >a
509 $ echo a >a
503 $ hg ci -Ama
510 $ hg ci -Ama
504 adding a
511 adding a
505 $ echo b >b
512 $ echo b >b
506 $ hg ci -Amb
513 $ hg ci -Amb
507 adding b
514 adding b
508 $ echo b1 >b1
515 $ echo b1 >b1
509 $ hg ci -Amb1
516 $ hg ci -Amb1
510 adding b1
517 adding b1
511 $ hg up 0
518 $ hg up 0
512 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
519 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
513 $ echo c >c
520 $ echo c >c
514 $ hg ci -Amc
521 $ hg ci -Amc
515 adding c
522 adding c
516 created new head
523 created new head
517 $ echo c1 >c1
524 $ echo c1 >c1
518 $ hg ci -Amc1
525 $ hg ci -Amc1
519 adding c1
526 adding c1
520 $ hg clone -q .#tip part
527 $ hg clone -q .#tip part
521
528
522 == bundling via incoming
529 == bundling via incoming
523
530
524 $ hg in -R part --bundle incoming.hg --template "{node}\n" .
531 $ hg in -R part --bundle incoming.hg --template "{node}\n" .
525 comparing with .
532 comparing with .
526 searching for changes
533 searching for changes
527 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
534 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
528 5ece8e77363e2b5269e27c66828b72da29e4341a
535 5ece8e77363e2b5269e27c66828b72da29e4341a
529
536
530 == bundling
537 == bundling
531
538
532 $ hg bundle bundle.hg part --debug
539 $ hg bundle bundle.hg part --debug
533 searching for changes
540 searching for changes
534 common changesets up to c0025332f9ed
541 common changesets up to c0025332f9ed
535 2 changesets found
542 2 changesets found
536 list of changesets:
543 list of changesets:
537 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
544 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
538 5ece8e77363e2b5269e27c66828b72da29e4341a
545 5ece8e77363e2b5269e27c66828b72da29e4341a
539 bundling changes: 0 chunks
546 bundling changes: 0 chunks
540 bundling changes: 1 chunks
547 bundling changes: 1 chunks
541 bundling changes: 2 chunks
548 bundling changes: 2 chunks
542 bundling changes: 3 chunks
549 bundling changes: 3 chunks
543 bundling changes: 4 chunks
550 bundling changes: 4 chunks
544 bundling changes: 5 chunks
551 bundling changes: 5 chunks
545 bundling changes: 6 chunks
552 bundling changes: 6 chunks
546 bundling manifests: 0 chunks
553 bundling manifests: 0 chunks
547 bundling manifests: 1 chunks
554 bundling manifests: 1 chunks
548 bundling manifests: 2 chunks
555 bundling manifests: 2 chunks
549 bundling manifests: 3 chunks
556 bundling manifests: 3 chunks
550 bundling manifests: 4 chunks
557 bundling manifests: 4 chunks
551 bundling manifests: 5 chunks
558 bundling manifests: 5 chunks
552 bundling manifests: 6 chunks
559 bundling manifests: 6 chunks
553 bundling files: b 0 chunks
560 bundling files: b 0 chunks
554 bundling files: b 1 chunks
561 bundling files: b 1 chunks
555 bundling files: b 2 chunks
562 bundling files: b 2 chunks
556 bundling files: b 3 chunks
563 bundling files: b 3 chunks
557 bundling files: b1 4 chunks
564 bundling files: b1 4 chunks
558 bundling files: b1 5 chunks
565 bundling files: b1 5 chunks
559 bundling files: b1 6 chunks
566 bundling files: b1 6 chunks
560 bundling files: b1 7 chunks
567 bundling files: b1 7 chunks
561
568
General Comments 0
You need to be logged in to leave comments. Login now