##// END OF EJS Templates
merge crew and main
Benoit Boissinot -
r18652:a5e94bee merge default
parent child Browse files
Show More
@@ -1,783 +1,800 b''
1 # dirstate.py - working directory tracking for mercurial
1 # dirstate.py - working directory tracking for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 import errno
7 import errno
8
8
9 from node import nullid
9 from node import nullid
10 from i18n import _
10 from i18n import _
11 import scmutil, util, ignore, osutil, parsers, encoding
11 import scmutil, util, ignore, osutil, parsers, encoding
12 import os, stat, errno
12 import os, stat, errno
13
13
14 propertycache = util.propertycache
14 propertycache = util.propertycache
15 filecache = scmutil.filecache
15 filecache = scmutil.filecache
16 _rangemask = 0x7fffffff
16 _rangemask = 0x7fffffff
17
17
18 class repocache(filecache):
18 class repocache(filecache):
19 """filecache for files in .hg/"""
19 """filecache for files in .hg/"""
20 def join(self, obj, fname):
20 def join(self, obj, fname):
21 return obj._opener.join(fname)
21 return obj._opener.join(fname)
22
22
23 class rootcache(filecache):
23 class rootcache(filecache):
24 """filecache for files in the repository root"""
24 """filecache for files in the repository root"""
25 def join(self, obj, fname):
25 def join(self, obj, fname):
26 return obj._join(fname)
26 return obj._join(fname)
27
27
28 def _finddirs(path):
28 def _finddirs(path):
29 pos = path.rfind('/')
29 pos = path.rfind('/')
30 while pos != -1:
30 while pos != -1:
31 yield path[:pos]
31 yield path[:pos]
32 pos = path.rfind('/', 0, pos)
32 pos = path.rfind('/', 0, pos)
33
33
34 def _incdirs(dirs, path):
34 def _incdirs(dirs, path):
35 for base in _finddirs(path):
35 for base in _finddirs(path):
36 if base in dirs:
36 if base in dirs:
37 dirs[base] += 1
37 dirs[base] += 1
38 return
38 return
39 dirs[base] = 1
39 dirs[base] = 1
40
40
41 def _decdirs(dirs, path):
41 def _decdirs(dirs, path):
42 for base in _finddirs(path):
42 for base in _finddirs(path):
43 if dirs[base] > 1:
43 if dirs[base] > 1:
44 dirs[base] -= 1
44 dirs[base] -= 1
45 return
45 return
46 del dirs[base]
46 del dirs[base]
47
47
48 class dirstate(object):
48 class dirstate(object):
49
49
50 def __init__(self, opener, ui, root, validate):
50 def __init__(self, opener, ui, root, validate):
51 '''Create a new dirstate object.
51 '''Create a new dirstate object.
52
52
53 opener is an open()-like callable that can be used to open the
53 opener is an open()-like callable that can be used to open the
54 dirstate file; root is the root of the directory tracked by
54 dirstate file; root is the root of the directory tracked by
55 the dirstate.
55 the dirstate.
56 '''
56 '''
57 self._opener = opener
57 self._opener = opener
58 self._validate = validate
58 self._validate = validate
59 self._root = root
59 self._root = root
60 self._rootdir = os.path.join(root, '')
60 self._rootdir = os.path.join(root, '')
61 self._dirty = False
61 self._dirty = False
62 self._dirtypl = False
62 self._dirtypl = False
63 self._lastnormaltime = 0
63 self._lastnormaltime = 0
64 self._ui = ui
64 self._ui = ui
65 self._filecache = {}
65 self._filecache = {}
66
66
67 @propertycache
67 @propertycache
68 def _map(self):
68 def _map(self):
69 '''Return the dirstate contents as a map from filename to
69 '''Return the dirstate contents as a map from filename to
70 (state, mode, size, time).'''
70 (state, mode, size, time).'''
71 self._read()
71 self._read()
72 return self._map
72 return self._map
73
73
74 @propertycache
74 @propertycache
75 def _copymap(self):
75 def _copymap(self):
76 self._read()
76 self._read()
77 return self._copymap
77 return self._copymap
78
78
79 @propertycache
79 @propertycache
80 def _foldmap(self):
80 def _foldmap(self):
81 f = {}
81 f = {}
82 for name in self._map:
82 for name in self._map:
83 f[util.normcase(name)] = name
83 f[util.normcase(name)] = name
84 for name in self._dirs:
84 for name in self._dirs:
85 f[util.normcase(name)] = name
85 f[util.normcase(name)] = name
86 f['.'] = '.' # prevents useless util.fspath() invocation
86 f['.'] = '.' # prevents useless util.fspath() invocation
87 return f
87 return f
88
88
89 @repocache('branch')
89 @repocache('branch')
90 def _branch(self):
90 def _branch(self):
91 try:
91 try:
92 return self._opener.read("branch").strip() or "default"
92 return self._opener.read("branch").strip() or "default"
93 except IOError, inst:
93 except IOError, inst:
94 if inst.errno != errno.ENOENT:
94 if inst.errno != errno.ENOENT:
95 raise
95 raise
96 return "default"
96 return "default"
97
97
98 @propertycache
98 @propertycache
99 def _pl(self):
99 def _pl(self):
100 try:
100 try:
101 fp = self._opener("dirstate")
101 fp = self._opener("dirstate")
102 st = fp.read(40)
102 st = fp.read(40)
103 fp.close()
103 fp.close()
104 l = len(st)
104 l = len(st)
105 if l == 40:
105 if l == 40:
106 return st[:20], st[20:40]
106 return st[:20], st[20:40]
107 elif l > 0 and l < 40:
107 elif l > 0 and l < 40:
108 raise util.Abort(_('working directory state appears damaged!'))
108 raise util.Abort(_('working directory state appears damaged!'))
109 except IOError, err:
109 except IOError, err:
110 if err.errno != errno.ENOENT:
110 if err.errno != errno.ENOENT:
111 raise
111 raise
112 return [nullid, nullid]
112 return [nullid, nullid]
113
113
114 @propertycache
114 @propertycache
115 def _dirs(self):
115 def _dirs(self):
116 dirs = {}
116 dirs = {}
117 for f, s in self._map.iteritems():
117 for f, s in self._map.iteritems():
118 if s[0] != 'r':
118 if s[0] != 'r':
119 _incdirs(dirs, f)
119 _incdirs(dirs, f)
120 return dirs
120 return dirs
121
121
122 def dirs(self):
122 def dirs(self):
123 return self._dirs
123 return self._dirs
124
124
125 @rootcache('.hgignore')
125 @rootcache('.hgignore')
126 def _ignore(self):
126 def _ignore(self):
127 files = [self._join('.hgignore')]
127 files = [self._join('.hgignore')]
128 for name, path in self._ui.configitems("ui"):
128 for name, path in self._ui.configitems("ui"):
129 if name == 'ignore' or name.startswith('ignore.'):
129 if name == 'ignore' or name.startswith('ignore.'):
130 files.append(util.expandpath(path))
130 files.append(util.expandpath(path))
131 return ignore.ignore(self._root, files, self._ui.warn)
131 return ignore.ignore(self._root, files, self._ui.warn)
132
132
133 @propertycache
133 @propertycache
134 def _slash(self):
134 def _slash(self):
135 return self._ui.configbool('ui', 'slash') and os.sep != '/'
135 return self._ui.configbool('ui', 'slash') and os.sep != '/'
136
136
137 @propertycache
137 @propertycache
138 def _checklink(self):
138 def _checklink(self):
139 return util.checklink(self._root)
139 return util.checklink(self._root)
140
140
141 @propertycache
141 @propertycache
142 def _checkexec(self):
142 def _checkexec(self):
143 return util.checkexec(self._root)
143 return util.checkexec(self._root)
144
144
145 @propertycache
145 @propertycache
146 def _checkcase(self):
146 def _checkcase(self):
147 return not util.checkcase(self._join('.hg'))
147 return not util.checkcase(self._join('.hg'))
148
148
149 def _join(self, f):
149 def _join(self, f):
150 # much faster than os.path.join()
150 # much faster than os.path.join()
151 # it's safe because f is always a relative path
151 # it's safe because f is always a relative path
152 return self._rootdir + f
152 return self._rootdir + f
153
153
154 def flagfunc(self, buildfallback):
154 def flagfunc(self, buildfallback):
155 if self._checklink and self._checkexec:
155 if self._checklink and self._checkexec:
156 def f(x):
156 def f(x):
157 p = self._join(x)
157 p = self._join(x)
158 if os.path.islink(p):
158 if os.path.islink(p):
159 return 'l'
159 return 'l'
160 if util.isexec(p):
160 if util.isexec(p):
161 return 'x'
161 return 'x'
162 return ''
162 return ''
163 return f
163 return f
164
164
165 fallback = buildfallback()
165 fallback = buildfallback()
166 if self._checklink:
166 if self._checklink:
167 def f(x):
167 def f(x):
168 if os.path.islink(self._join(x)):
168 if os.path.islink(self._join(x)):
169 return 'l'
169 return 'l'
170 if 'x' in fallback(x):
170 if 'x' in fallback(x):
171 return 'x'
171 return 'x'
172 return ''
172 return ''
173 return f
173 return f
174 if self._checkexec:
174 if self._checkexec:
175 def f(x):
175 def f(x):
176 if 'l' in fallback(x):
176 if 'l' in fallback(x):
177 return 'l'
177 return 'l'
178 if util.isexec(self._join(x)):
178 if util.isexec(self._join(x)):
179 return 'x'
179 return 'x'
180 return ''
180 return ''
181 return f
181 return f
182 else:
182 else:
183 return fallback
183 return fallback
184
184
185 def getcwd(self):
185 def getcwd(self):
186 cwd = os.getcwd()
186 cwd = os.getcwd()
187 if cwd == self._root:
187 if cwd == self._root:
188 return ''
188 return ''
189 # self._root ends with a path separator if self._root is '/' or 'C:\'
189 # self._root ends with a path separator if self._root is '/' or 'C:\'
190 rootsep = self._root
190 rootsep = self._root
191 if not util.endswithsep(rootsep):
191 if not util.endswithsep(rootsep):
192 rootsep += os.sep
192 rootsep += os.sep
193 if cwd.startswith(rootsep):
193 if cwd.startswith(rootsep):
194 return cwd[len(rootsep):]
194 return cwd[len(rootsep):]
195 else:
195 else:
196 # we're outside the repo. return an absolute path.
196 # we're outside the repo. return an absolute path.
197 return cwd
197 return cwd
198
198
199 def pathto(self, f, cwd=None):
199 def pathto(self, f, cwd=None):
200 if cwd is None:
200 if cwd is None:
201 cwd = self.getcwd()
201 cwd = self.getcwd()
202 path = util.pathto(self._root, cwd, f)
202 path = util.pathto(self._root, cwd, f)
203 if self._slash:
203 if self._slash:
204 return util.normpath(path)
204 return util.normpath(path)
205 return path
205 return path
206
206
207 def __getitem__(self, key):
207 def __getitem__(self, key):
208 '''Return the current state of key (a filename) in the dirstate.
208 '''Return the current state of key (a filename) in the dirstate.
209
209
210 States are:
210 States are:
211 n normal
211 n normal
212 m needs merging
212 m needs merging
213 r marked for removal
213 r marked for removal
214 a marked for addition
214 a marked for addition
215 ? not tracked
215 ? not tracked
216 '''
216 '''
217 return self._map.get(key, ("?",))[0]
217 return self._map.get(key, ("?",))[0]
218
218
219 def __contains__(self, key):
219 def __contains__(self, key):
220 return key in self._map
220 return key in self._map
221
221
222 def __iter__(self):
222 def __iter__(self):
223 for x in sorted(self._map):
223 for x in sorted(self._map):
224 yield x
224 yield x
225
225
226 def parents(self):
226 def parents(self):
227 return [self._validate(p) for p in self._pl]
227 return [self._validate(p) for p in self._pl]
228
228
229 def p1(self):
229 def p1(self):
230 return self._validate(self._pl[0])
230 return self._validate(self._pl[0])
231
231
232 def p2(self):
232 def p2(self):
233 return self._validate(self._pl[1])
233 return self._validate(self._pl[1])
234
234
235 def branch(self):
235 def branch(self):
236 return encoding.tolocal(self._branch)
236 return encoding.tolocal(self._branch)
237
237
238 def setparents(self, p1, p2=nullid):
238 def setparents(self, p1, p2=nullid):
239 """Set dirstate parents to p1 and p2.
239 """Set dirstate parents to p1 and p2.
240
240
241 When moving from two parents to one, 'm' merged entries a
241 When moving from two parents to one, 'm' merged entries a
242 adjusted to normal and previous copy records discarded and
242 adjusted to normal and previous copy records discarded and
243 returned by the call.
243 returned by the call.
244
244
245 See localrepo.setparents()
245 See localrepo.setparents()
246 """
246 """
247 self._dirty = self._dirtypl = True
247 self._dirty = self._dirtypl = True
248 oldp2 = self._pl[1]
248 oldp2 = self._pl[1]
249 self._pl = p1, p2
249 self._pl = p1, p2
250 copies = {}
250 copies = {}
251 if oldp2 != nullid and p2 == nullid:
251 if oldp2 != nullid and p2 == nullid:
252 # Discard 'm' markers when moving away from a merge state
252 # Discard 'm' markers when moving away from a merge state
253 for f, s in self._map.iteritems():
253 for f, s in self._map.iteritems():
254 if s[0] == 'm':
254 if s[0] == 'm':
255 if f in self._copymap:
255 if f in self._copymap:
256 copies[f] = self._copymap[f]
256 copies[f] = self._copymap[f]
257 self.normallookup(f)
257 self.normallookup(f)
258 return copies
258 return copies
259
259
260 def setbranch(self, branch):
260 def setbranch(self, branch):
261 self._branch = encoding.fromlocal(branch)
261 self._branch = encoding.fromlocal(branch)
262 f = self._opener('branch', 'w', atomictemp=True)
262 f = self._opener('branch', 'w', atomictemp=True)
263 try:
263 try:
264 f.write(self._branch + '\n')
264 f.write(self._branch + '\n')
265 f.close()
265 f.close()
266
266
267 # make sure filecache has the correct stat info for _branch after
267 # make sure filecache has the correct stat info for _branch after
268 # replacing the underlying file
268 # replacing the underlying file
269 ce = self._filecache['_branch']
269 ce = self._filecache['_branch']
270 if ce:
270 if ce:
271 ce.refresh()
271 ce.refresh()
272 except: # re-raises
272 except: # re-raises
273 f.discard()
273 f.discard()
274 raise
274 raise
275
275
276 def _read(self):
276 def _read(self):
277 self._map = {}
277 self._map = {}
278 self._copymap = {}
278 self._copymap = {}
279 try:
279 try:
280 st = self._opener.read("dirstate")
280 st = self._opener.read("dirstate")
281 except IOError, err:
281 except IOError, err:
282 if err.errno != errno.ENOENT:
282 if err.errno != errno.ENOENT:
283 raise
283 raise
284 return
284 return
285 if not st:
285 if not st:
286 return
286 return
287
287
288 p = parsers.parse_dirstate(self._map, self._copymap, st)
288 p = parsers.parse_dirstate(self._map, self._copymap, st)
289 if not self._dirtypl:
289 if not self._dirtypl:
290 self._pl = p
290 self._pl = p
291
291
292 def invalidate(self):
292 def invalidate(self):
293 for a in ("_map", "_copymap", "_foldmap", "_branch", "_pl", "_dirs",
293 for a in ("_map", "_copymap", "_foldmap", "_branch", "_pl", "_dirs",
294 "_ignore"):
294 "_ignore"):
295 if a in self.__dict__:
295 if a in self.__dict__:
296 delattr(self, a)
296 delattr(self, a)
297 self._lastnormaltime = 0
297 self._lastnormaltime = 0
298 self._dirty = False
298 self._dirty = False
299
299
300 def copy(self, source, dest):
300 def copy(self, source, dest):
301 """Mark dest as a copy of source. Unmark dest if source is None."""
301 """Mark dest as a copy of source. Unmark dest if source is None."""
302 if source == dest:
302 if source == dest:
303 return
303 return
304 self._dirty = True
304 self._dirty = True
305 if source is not None:
305 if source is not None:
306 self._copymap[dest] = source
306 self._copymap[dest] = source
307 elif dest in self._copymap:
307 elif dest in self._copymap:
308 del self._copymap[dest]
308 del self._copymap[dest]
309
309
310 def copied(self, file):
310 def copied(self, file):
311 return self._copymap.get(file, None)
311 return self._copymap.get(file, None)
312
312
313 def copies(self):
313 def copies(self):
314 return self._copymap
314 return self._copymap
315
315
316 def _droppath(self, f):
316 def _droppath(self, f):
317 if self[f] not in "?r" and "_dirs" in self.__dict__:
317 if self[f] not in "?r" and "_dirs" in self.__dict__:
318 _decdirs(self._dirs, f)
318 _decdirs(self._dirs, f)
319
319
320 def _addpath(self, f, state, mode, size, mtime):
320 def _addpath(self, f, state, mode, size, mtime):
321 oldstate = self[f]
321 oldstate = self[f]
322 if state == 'a' or oldstate == 'r':
322 if state == 'a' or oldstate == 'r':
323 scmutil.checkfilename(f)
323 scmutil.checkfilename(f)
324 if f in self._dirs:
324 if f in self._dirs:
325 raise util.Abort(_('directory %r already in dirstate') % f)
325 raise util.Abort(_('directory %r already in dirstate') % f)
326 # shadows
326 # shadows
327 for d in _finddirs(f):
327 for d in _finddirs(f):
328 if d in self._dirs:
328 if d in self._dirs:
329 break
329 break
330 if d in self._map and self[d] != 'r':
330 if d in self._map and self[d] != 'r':
331 raise util.Abort(
331 raise util.Abort(
332 _('file %r in dirstate clashes with %r') % (d, f))
332 _('file %r in dirstate clashes with %r') % (d, f))
333 if oldstate in "?r" and "_dirs" in self.__dict__:
333 if oldstate in "?r" and "_dirs" in self.__dict__:
334 _incdirs(self._dirs, f)
334 _incdirs(self._dirs, f)
335 self._dirty = True
335 self._dirty = True
336 self._map[f] = (state, mode, size, mtime)
336 self._map[f] = (state, mode, size, mtime)
337
337
338 def normal(self, f):
338 def normal(self, f):
339 '''Mark a file normal and clean.'''
339 '''Mark a file normal and clean.'''
340 s = os.lstat(self._join(f))
340 s = os.lstat(self._join(f))
341 mtime = int(s.st_mtime)
341 mtime = int(s.st_mtime)
342 self._addpath(f, 'n', s.st_mode,
342 self._addpath(f, 'n', s.st_mode,
343 s.st_size & _rangemask, mtime & _rangemask)
343 s.st_size & _rangemask, mtime & _rangemask)
344 if f in self._copymap:
344 if f in self._copymap:
345 del self._copymap[f]
345 del self._copymap[f]
346 if mtime > self._lastnormaltime:
346 if mtime > self._lastnormaltime:
347 # Remember the most recent modification timeslot for status(),
347 # Remember the most recent modification timeslot for status(),
348 # to make sure we won't miss future size-preserving file content
348 # to make sure we won't miss future size-preserving file content
349 # modifications that happen within the same timeslot.
349 # modifications that happen within the same timeslot.
350 self._lastnormaltime = mtime
350 self._lastnormaltime = mtime
351
351
352 def normallookup(self, f):
352 def normallookup(self, f):
353 '''Mark a file normal, but possibly dirty.'''
353 '''Mark a file normal, but possibly dirty.'''
354 if self._pl[1] != nullid and f in self._map:
354 if self._pl[1] != nullid and f in self._map:
355 # if there is a merge going on and the file was either
355 # if there is a merge going on and the file was either
356 # in state 'm' (-1) or coming from other parent (-2) before
356 # in state 'm' (-1) or coming from other parent (-2) before
357 # being removed, restore that state.
357 # being removed, restore that state.
358 entry = self._map[f]
358 entry = self._map[f]
359 if entry[0] == 'r' and entry[2] in (-1, -2):
359 if entry[0] == 'r' and entry[2] in (-1, -2):
360 source = self._copymap.get(f)
360 source = self._copymap.get(f)
361 if entry[2] == -1:
361 if entry[2] == -1:
362 self.merge(f)
362 self.merge(f)
363 elif entry[2] == -2:
363 elif entry[2] == -2:
364 self.otherparent(f)
364 self.otherparent(f)
365 if source:
365 if source:
366 self.copy(source, f)
366 self.copy(source, f)
367 return
367 return
368 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
368 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
369 return
369 return
370 self._addpath(f, 'n', 0, -1, -1)
370 self._addpath(f, 'n', 0, -1, -1)
371 if f in self._copymap:
371 if f in self._copymap:
372 del self._copymap[f]
372 del self._copymap[f]
373
373
374 def otherparent(self, f):
374 def otherparent(self, f):
375 '''Mark as coming from the other parent, always dirty.'''
375 '''Mark as coming from the other parent, always dirty.'''
376 if self._pl[1] == nullid:
376 if self._pl[1] == nullid:
377 raise util.Abort(_("setting %r to other parent "
377 raise util.Abort(_("setting %r to other parent "
378 "only allowed in merges") % f)
378 "only allowed in merges") % f)
379 self._addpath(f, 'n', 0, -2, -1)
379 self._addpath(f, 'n', 0, -2, -1)
380 if f in self._copymap:
380 if f in self._copymap:
381 del self._copymap[f]
381 del self._copymap[f]
382
382
383 def add(self, f):
383 def add(self, f):
384 '''Mark a file added.'''
384 '''Mark a file added.'''
385 self._addpath(f, 'a', 0, -1, -1)
385 self._addpath(f, 'a', 0, -1, -1)
386 if f in self._copymap:
386 if f in self._copymap:
387 del self._copymap[f]
387 del self._copymap[f]
388
388
389 def remove(self, f):
389 def remove(self, f):
390 '''Mark a file removed.'''
390 '''Mark a file removed.'''
391 self._dirty = True
391 self._dirty = True
392 self._droppath(f)
392 self._droppath(f)
393 size = 0
393 size = 0
394 if self._pl[1] != nullid and f in self._map:
394 if self._pl[1] != nullid and f in self._map:
395 # backup the previous state
395 # backup the previous state
396 entry = self._map[f]
396 entry = self._map[f]
397 if entry[0] == 'm': # merge
397 if entry[0] == 'm': # merge
398 size = -1
398 size = -1
399 elif entry[0] == 'n' and entry[2] == -2: # other parent
399 elif entry[0] == 'n' and entry[2] == -2: # other parent
400 size = -2
400 size = -2
401 self._map[f] = ('r', 0, size, 0)
401 self._map[f] = ('r', 0, size, 0)
402 if size == 0 and f in self._copymap:
402 if size == 0 and f in self._copymap:
403 del self._copymap[f]
403 del self._copymap[f]
404
404
405 def merge(self, f):
405 def merge(self, f):
406 '''Mark a file merged.'''
406 '''Mark a file merged.'''
407 if self._pl[1] == nullid:
407 if self._pl[1] == nullid:
408 return self.normallookup(f)
408 return self.normallookup(f)
409 s = os.lstat(self._join(f))
409 s = os.lstat(self._join(f))
410 self._addpath(f, 'm', s.st_mode,
410 self._addpath(f, 'm', s.st_mode,
411 s.st_size & _rangemask, int(s.st_mtime) & _rangemask)
411 s.st_size & _rangemask, int(s.st_mtime) & _rangemask)
412 if f in self._copymap:
412 if f in self._copymap:
413 del self._copymap[f]
413 del self._copymap[f]
414
414
415 def drop(self, f):
415 def drop(self, f):
416 '''Drop a file from the dirstate'''
416 '''Drop a file from the dirstate'''
417 if f in self._map:
417 if f in self._map:
418 self._dirty = True
418 self._dirty = True
419 self._droppath(f)
419 self._droppath(f)
420 del self._map[f]
420 del self._map[f]
421
421
422 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
422 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
423 normed = util.normcase(path)
423 normed = util.normcase(path)
424 folded = self._foldmap.get(normed, None)
424 folded = self._foldmap.get(normed, None)
425 if folded is None:
425 if folded is None:
426 if isknown:
426 if isknown:
427 folded = path
427 folded = path
428 else:
428 else:
429 if exists is None:
429 if exists is None:
430 exists = os.path.lexists(os.path.join(self._root, path))
430 exists = os.path.lexists(os.path.join(self._root, path))
431 if not exists:
431 if not exists:
432 # Maybe a path component exists
432 # Maybe a path component exists
433 if not ignoremissing and '/' in path:
433 if not ignoremissing and '/' in path:
434 d, f = path.rsplit('/', 1)
434 d, f = path.rsplit('/', 1)
435 d = self._normalize(d, isknown, ignoremissing, None)
435 d = self._normalize(d, isknown, ignoremissing, None)
436 folded = d + "/" + f
436 folded = d + "/" + f
437 else:
437 else:
438 # No path components, preserve original case
438 # No path components, preserve original case
439 folded = path
439 folded = path
440 else:
440 else:
441 # recursively normalize leading directory components
441 # recursively normalize leading directory components
442 # against dirstate
442 # against dirstate
443 if '/' in normed:
443 if '/' in normed:
444 d, f = normed.rsplit('/', 1)
444 d, f = normed.rsplit('/', 1)
445 d = self._normalize(d, isknown, ignoremissing, True)
445 d = self._normalize(d, isknown, ignoremissing, True)
446 r = self._root + "/" + d
446 r = self._root + "/" + d
447 folded = d + "/" + util.fspath(f, r)
447 folded = d + "/" + util.fspath(f, r)
448 else:
448 else:
449 folded = util.fspath(normed, self._root)
449 folded = util.fspath(normed, self._root)
450 self._foldmap[normed] = folded
450 self._foldmap[normed] = folded
451
451
452 return folded
452 return folded
453
453
454 def normalize(self, path, isknown=False, ignoremissing=False):
454 def normalize(self, path, isknown=False, ignoremissing=False):
455 '''
455 '''
456 normalize the case of a pathname when on a casefolding filesystem
456 normalize the case of a pathname when on a casefolding filesystem
457
457
458 isknown specifies whether the filename came from walking the
458 isknown specifies whether the filename came from walking the
459 disk, to avoid extra filesystem access.
459 disk, to avoid extra filesystem access.
460
460
461 If ignoremissing is True, missing path are returned
461 If ignoremissing is True, missing path are returned
462 unchanged. Otherwise, we try harder to normalize possibly
462 unchanged. Otherwise, we try harder to normalize possibly
463 existing path components.
463 existing path components.
464
464
465 The normalized case is determined based on the following precedence:
465 The normalized case is determined based on the following precedence:
466
466
467 - version of name already stored in the dirstate
467 - version of name already stored in the dirstate
468 - version of name stored on disk
468 - version of name stored on disk
469 - version provided via command arguments
469 - version provided via command arguments
470 '''
470 '''
471
471
472 if self._checkcase:
472 if self._checkcase:
473 return self._normalize(path, isknown, ignoremissing)
473 return self._normalize(path, isknown, ignoremissing)
474 return path
474 return path
475
475
476 def clear(self):
476 def clear(self):
477 self._map = {}
477 self._map = {}
478 if "_dirs" in self.__dict__:
478 if "_dirs" in self.__dict__:
479 delattr(self, "_dirs")
479 delattr(self, "_dirs")
480 self._copymap = {}
480 self._copymap = {}
481 self._pl = [nullid, nullid]
481 self._pl = [nullid, nullid]
482 self._lastnormaltime = 0
482 self._lastnormaltime = 0
483 self._dirty = True
483 self._dirty = True
484
484
485 def rebuild(self, parent, files):
485 def rebuild(self, parent, files):
486 self.clear()
486 self.clear()
487 for f in files:
487 for f in files:
488 if 'x' in files.flags(f):
488 if 'x' in files.flags(f):
489 self._map[f] = ('n', 0777, -1, 0)
489 self._map[f] = ('n', 0777, -1, 0)
490 else:
490 else:
491 self._map[f] = ('n', 0666, -1, 0)
491 self._map[f] = ('n', 0666, -1, 0)
492 self._pl = (parent, nullid)
492 self._pl = (parent, nullid)
493 self._dirty = True
493 self._dirty = True
494
494
495 def write(self):
495 def write(self):
496 if not self._dirty:
496 if not self._dirty:
497 return
497 return
498 st = self._opener("dirstate", "w", atomictemp=True)
498 st = self._opener("dirstate", "w", atomictemp=True)
499
499
500 def finish(s):
500 def finish(s):
501 st.write(s)
501 st.write(s)
502 st.close()
502 st.close()
503 self._lastnormaltime = 0
503 self._lastnormaltime = 0
504 self._dirty = self._dirtypl = False
504 self._dirty = self._dirtypl = False
505
505
506 # use the modification time of the newly created temporary file as the
506 # use the modification time of the newly created temporary file as the
507 # filesystem's notion of 'now'
507 # filesystem's notion of 'now'
508 now = util.fstat(st).st_mtime
508 now = util.fstat(st).st_mtime
509 finish(parsers.pack_dirstate(self._map, self._copymap, self._pl, now))
509 finish(parsers.pack_dirstate(self._map, self._copymap, self._pl, now))
510
510
511 def _dirignore(self, f):
511 def _dirignore(self, f):
512 if f == '.':
512 if f == '.':
513 return False
513 return False
514 if self._ignore(f):
514 if self._ignore(f):
515 return True
515 return True
516 for p in _finddirs(f):
516 for p in _finddirs(f):
517 if self._ignore(p):
517 if self._ignore(p):
518 return True
518 return True
519 return False
519 return False
520
520
521 def walk(self, match, subrepos, unknown, ignored):
521 def walk(self, match, subrepos, unknown, ignored):
522 '''
522 '''
523 Walk recursively through the directory tree, finding all files
523 Walk recursively through the directory tree, finding all files
524 matched by match.
524 matched by match.
525
525
526 Return a dict mapping filename to stat-like object (either
526 Return a dict mapping filename to stat-like object (either
527 mercurial.osutil.stat instance or return value of os.stat()).
527 mercurial.osutil.stat instance or return value of os.stat()).
528 '''
528 '''
529
529
530 def fwarn(f, msg):
530 def fwarn(f, msg):
531 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
531 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
532 return False
532 return False
533
533
534 def badtype(mode):
534 def badtype(mode):
535 kind = _('unknown')
535 kind = _('unknown')
536 if stat.S_ISCHR(mode):
536 if stat.S_ISCHR(mode):
537 kind = _('character device')
537 kind = _('character device')
538 elif stat.S_ISBLK(mode):
538 elif stat.S_ISBLK(mode):
539 kind = _('block device')
539 kind = _('block device')
540 elif stat.S_ISFIFO(mode):
540 elif stat.S_ISFIFO(mode):
541 kind = _('fifo')
541 kind = _('fifo')
542 elif stat.S_ISSOCK(mode):
542 elif stat.S_ISSOCK(mode):
543 kind = _('socket')
543 kind = _('socket')
544 elif stat.S_ISDIR(mode):
544 elif stat.S_ISDIR(mode):
545 kind = _('directory')
545 kind = _('directory')
546 return _('unsupported file type (type is %s)') % kind
546 return _('unsupported file type (type is %s)') % kind
547
547
548 ignore = self._ignore
548 ignore = self._ignore
549 dirignore = self._dirignore
549 dirignore = self._dirignore
550 if ignored:
550 if ignored:
551 ignore = util.never
551 ignore = util.never
552 dirignore = util.never
552 dirignore = util.never
553 elif not unknown:
553 elif not unknown:
554 # if unknown and ignored are False, skip step 2
554 # if unknown and ignored are False, skip step 2
555 ignore = util.always
555 ignore = util.always
556 dirignore = util.always
556 dirignore = util.always
557
557
558 matchfn = match.matchfn
558 matchfn = match.matchfn
559 badfn = match.bad
559 badfn = match.bad
560 dmap = self._map
560 dmap = self._map
561 normpath = util.normpath
561 normpath = util.normpath
562 listdir = osutil.listdir
562 listdir = osutil.listdir
563 lstat = os.lstat
563 lstat = os.lstat
564 getkind = stat.S_IFMT
564 getkind = stat.S_IFMT
565 dirkind = stat.S_IFDIR
565 dirkind = stat.S_IFDIR
566 regkind = stat.S_IFREG
566 regkind = stat.S_IFREG
567 lnkkind = stat.S_IFLNK
567 lnkkind = stat.S_IFLNK
568 join = self._join
568 join = self._join
569 work = []
569 work = []
570 wadd = work.append
570 wadd = work.append
571
571
572 exact = skipstep3 = False
572 exact = skipstep3 = False
573 if matchfn == match.exact: # match.exact
573 if matchfn == match.exact: # match.exact
574 exact = True
574 exact = True
575 dirignore = util.always # skip step 2
575 dirignore = util.always # skip step 2
576 elif match.files() and not match.anypats(): # match.match, no patterns
576 elif match.files() and not match.anypats(): # match.match, no patterns
577 skipstep3 = True
577 skipstep3 = True
578
578
579 if not exact and self._checkcase:
579 if not exact and self._checkcase:
580 normalize = self._normalize
580 normalize = self._normalize
581 skipstep3 = False
581 skipstep3 = False
582 else:
582 else:
583 normalize = None
583 normalize = None
584
584
585 files = sorted(match.files())
585 files = sorted(match.files())
586 subrepos.sort()
586 subrepos.sort()
587 i, j = 0, 0
587 i, j = 0, 0
588 while i < len(files) and j < len(subrepos):
588 while i < len(files) and j < len(subrepos):
589 subpath = subrepos[j] + "/"
589 subpath = subrepos[j] + "/"
590 if files[i] < subpath:
590 if files[i] < subpath:
591 i += 1
591 i += 1
592 continue
592 continue
593 while i < len(files) and files[i].startswith(subpath):
593 while i < len(files) and files[i].startswith(subpath):
594 del files[i]
594 del files[i]
595 j += 1
595 j += 1
596
596
597 if not files or '.' in files:
597 if not files or '.' in files:
598 files = ['']
598 files = ['']
599 results = dict.fromkeys(subrepos)
599 results = dict.fromkeys(subrepos)
600 results['.hg'] = None
600 results['.hg'] = None
601
601
602 # step 1: find all explicit files
602 # step 1: find all explicit files
603 for ff in files:
603 for ff in files:
604 if normalize:
604 if normalize:
605 nf = normalize(normpath(ff), False, True)
605 nf = normalize(normpath(ff), False, True)
606 else:
606 else:
607 nf = normpath(ff)
607 nf = normpath(ff)
608 if nf in results:
608 if nf in results:
609 continue
609 continue
610
610
611 try:
611 try:
612 st = lstat(join(nf))
612 st = lstat(join(nf))
613 kind = getkind(st.st_mode)
613 kind = getkind(st.st_mode)
614 if kind == dirkind:
614 if kind == dirkind:
615 skipstep3 = False
615 skipstep3 = False
616 if nf in dmap:
616 if nf in dmap:
617 #file deleted on disk but still in dirstate
617 #file deleted on disk but still in dirstate
618 results[nf] = None
618 results[nf] = None
619 match.dir(nf)
619 match.dir(nf)
620 if not dirignore(nf):
620 if not dirignore(nf):
621 wadd(nf)
621 wadd(nf)
622 elif kind == regkind or kind == lnkkind:
622 elif kind == regkind or kind == lnkkind:
623 results[nf] = st
623 results[nf] = st
624 else:
624 else:
625 badfn(ff, badtype(kind))
625 badfn(ff, badtype(kind))
626 if nf in dmap:
626 if nf in dmap:
627 results[nf] = None
627 results[nf] = None
628 except OSError, inst:
628 except OSError, inst:
629 if nf in dmap: # does it exactly match a file?
629 if nf in dmap: # does it exactly match a file?
630 results[nf] = None
630 results[nf] = None
631 else: # does it match a directory?
631 else: # does it match a directory?
632 prefix = nf + "/"
632 prefix = nf + "/"
633 for fn in dmap:
633 for fn in dmap:
634 if fn.startswith(prefix):
634 if fn.startswith(prefix):
635 match.dir(nf)
635 match.dir(nf)
636 skipstep3 = False
636 skipstep3 = False
637 break
637 break
638 else:
638 else:
639 badfn(ff, inst.strerror)
639 badfn(ff, inst.strerror)
640
640
641 # step 2: visit subdirectories
641 # step 2: visit subdirectories
642 while work:
642 while work:
643 nd = work.pop()
643 nd = work.pop()
644 skip = None
644 skip = None
645 if nd == '.':
645 if nd == '.':
646 nd = ''
646 nd = ''
647 else:
647 else:
648 skip = '.hg'
648 skip = '.hg'
649 try:
649 try:
650 entries = listdir(join(nd), stat=True, skip=skip)
650 entries = listdir(join(nd), stat=True, skip=skip)
651 except OSError, inst:
651 except OSError, inst:
652 if inst.errno in (errno.EACCES, errno.ENOENT):
652 if inst.errno in (errno.EACCES, errno.ENOENT):
653 fwarn(nd, inst.strerror)
653 fwarn(nd, inst.strerror)
654 continue
654 continue
655 raise
655 raise
656 for f, kind, st in entries:
656 for f, kind, st in entries:
657 if normalize:
657 if normalize:
658 nf = normalize(nd and (nd + "/" + f) or f, True, True)
658 nf = normalize(nd and (nd + "/" + f) or f, True, True)
659 else:
659 else:
660 nf = nd and (nd + "/" + f) or f
660 nf = nd and (nd + "/" + f) or f
661 if nf not in results:
661 if nf not in results:
662 if kind == dirkind:
662 if kind == dirkind:
663 if not ignore(nf):
663 if not ignore(nf):
664 match.dir(nf)
664 match.dir(nf)
665 wadd(nf)
665 wadd(nf)
666 if nf in dmap and matchfn(nf):
666 if nf in dmap and matchfn(nf):
667 results[nf] = None
667 results[nf] = None
668 elif kind == regkind or kind == lnkkind:
668 elif kind == regkind or kind == lnkkind:
669 if nf in dmap:
669 if nf in dmap:
670 if matchfn(nf):
670 if matchfn(nf):
671 results[nf] = st
671 results[nf] = st
672 elif matchfn(nf) and not ignore(nf):
672 elif matchfn(nf) and not ignore(nf):
673 results[nf] = st
673 results[nf] = st
674 elif nf in dmap and matchfn(nf):
674 elif nf in dmap and matchfn(nf):
675 results[nf] = None
675 results[nf] = None
676
676
677 # step 3: report unseen items in the dmap hash
677 # step 3: report unseen items in the dmap hash
678 if not skipstep3 and not exact:
678 if not skipstep3 and not exact:
679 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
679 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
680 nf = iter(visit).next
680 if unknown:
681 for st in util.statfiles([join(i) for i in visit]):
681 # unknown == True means we walked the full directory tree above.
682 results[nf()] = st
682 # So if a file is not seen it was either a) not matching matchfn
683 # b) ignored, c) missing, or d) under a symlink directory.
684 audit_path = scmutil.pathauditor(self._root)
685
686 for nf in iter(visit):
687 # Report ignored items in the dmap as long as they are not
688 # under a symlink directory.
689 if ignore(nf) and audit_path.check(nf):
690 results[nf] = util.statfiles([join(nf)])[0]
691 else:
692 # It's either missing or under a symlink directory
693 results[nf] = None
694 else:
695 # We may not have walked the full directory tree above,
696 # so stat everything we missed.
697 nf = iter(visit).next
698 for st in util.statfiles([join(i) for i in visit]):
699 results[nf()] = st
683 for s in subrepos:
700 for s in subrepos:
684 del results[s]
701 del results[s]
685 del results['.hg']
702 del results['.hg']
686 return results
703 return results
687
704
688 def status(self, match, subrepos, ignored, clean, unknown):
705 def status(self, match, subrepos, ignored, clean, unknown):
689 '''Determine the status of the working copy relative to the
706 '''Determine the status of the working copy relative to the
690 dirstate and return a tuple of lists (unsure, modified, added,
707 dirstate and return a tuple of lists (unsure, modified, added,
691 removed, deleted, unknown, ignored, clean), where:
708 removed, deleted, unknown, ignored, clean), where:
692
709
693 unsure:
710 unsure:
694 files that might have been modified since the dirstate was
711 files that might have been modified since the dirstate was
695 written, but need to be read to be sure (size is the same
712 written, but need to be read to be sure (size is the same
696 but mtime differs)
713 but mtime differs)
697 modified:
714 modified:
698 files that have definitely been modified since the dirstate
715 files that have definitely been modified since the dirstate
699 was written (different size or mode)
716 was written (different size or mode)
700 added:
717 added:
701 files that have been explicitly added with hg add
718 files that have been explicitly added with hg add
702 removed:
719 removed:
703 files that have been explicitly removed with hg remove
720 files that have been explicitly removed with hg remove
704 deleted:
721 deleted:
705 files that have been deleted through other means ("missing")
722 files that have been deleted through other means ("missing")
706 unknown:
723 unknown:
707 files not in the dirstate that are not ignored
724 files not in the dirstate that are not ignored
708 ignored:
725 ignored:
709 files not in the dirstate that are ignored
726 files not in the dirstate that are ignored
710 (by _dirignore())
727 (by _dirignore())
711 clean:
728 clean:
712 files that have definitely not been modified since the
729 files that have definitely not been modified since the
713 dirstate was written
730 dirstate was written
714 '''
731 '''
715 listignored, listclean, listunknown = ignored, clean, unknown
732 listignored, listclean, listunknown = ignored, clean, unknown
716 lookup, modified, added, unknown, ignored = [], [], [], [], []
733 lookup, modified, added, unknown, ignored = [], [], [], [], []
717 removed, deleted, clean = [], [], []
734 removed, deleted, clean = [], [], []
718
735
719 dmap = self._map
736 dmap = self._map
720 ladd = lookup.append # aka "unsure"
737 ladd = lookup.append # aka "unsure"
721 madd = modified.append
738 madd = modified.append
722 aadd = added.append
739 aadd = added.append
723 uadd = unknown.append
740 uadd = unknown.append
724 iadd = ignored.append
741 iadd = ignored.append
725 radd = removed.append
742 radd = removed.append
726 dadd = deleted.append
743 dadd = deleted.append
727 cadd = clean.append
744 cadd = clean.append
728 mexact = match.exact
745 mexact = match.exact
729 dirignore = self._dirignore
746 dirignore = self._dirignore
730 checkexec = self._checkexec
747 checkexec = self._checkexec
731 checklink = self._checklink
748 checklink = self._checklink
732 copymap = self._copymap
749 copymap = self._copymap
733 lastnormaltime = self._lastnormaltime
750 lastnormaltime = self._lastnormaltime
734
751
735 lnkkind = stat.S_IFLNK
752 lnkkind = stat.S_IFLNK
736
753
737 for fn, st in self.walk(match, subrepos, listunknown,
754 for fn, st in self.walk(match, subrepos, listunknown,
738 listignored).iteritems():
755 listignored).iteritems():
739 if fn not in dmap:
756 if fn not in dmap:
740 if (listignored or mexact(fn)) and dirignore(fn):
757 if (listignored or mexact(fn)) and dirignore(fn):
741 if listignored:
758 if listignored:
742 iadd(fn)
759 iadd(fn)
743 elif listunknown:
760 elif listunknown:
744 uadd(fn)
761 uadd(fn)
745 continue
762 continue
746
763
747 state, mode, size, time = dmap[fn]
764 state, mode, size, time = dmap[fn]
748
765
749 if not st and state in "nma":
766 if not st and state in "nma":
750 dadd(fn)
767 dadd(fn)
751 elif state == 'n':
768 elif state == 'n':
752 # The "mode & lnkkind != lnkkind or self._checklink"
769 # The "mode & lnkkind != lnkkind or self._checklink"
753 # lines are an expansion of "islink => checklink"
770 # lines are an expansion of "islink => checklink"
754 # where islink means "is this a link?" and checklink
771 # where islink means "is this a link?" and checklink
755 # means "can we check links?".
772 # means "can we check links?".
756 mtime = int(st.st_mtime)
773 mtime = int(st.st_mtime)
757 if (size >= 0 and
774 if (size >= 0 and
758 ((size != st.st_size and size != st.st_size & _rangemask)
775 ((size != st.st_size and size != st.st_size & _rangemask)
759 or ((mode ^ st.st_mode) & 0100 and checkexec))
776 or ((mode ^ st.st_mode) & 0100 and checkexec))
760 and (mode & lnkkind != lnkkind or checklink)
777 and (mode & lnkkind != lnkkind or checklink)
761 or size == -2 # other parent
778 or size == -2 # other parent
762 or fn in copymap):
779 or fn in copymap):
763 madd(fn)
780 madd(fn)
764 elif ((time != mtime and time != mtime & _rangemask)
781 elif ((time != mtime and time != mtime & _rangemask)
765 and (mode & lnkkind != lnkkind or checklink)):
782 and (mode & lnkkind != lnkkind or checklink)):
766 ladd(fn)
783 ladd(fn)
767 elif mtime == lastnormaltime:
784 elif mtime == lastnormaltime:
768 # fn may have been changed in the same timeslot without
785 # fn may have been changed in the same timeslot without
769 # changing its size. This can happen if we quickly do
786 # changing its size. This can happen if we quickly do
770 # multiple commits in a single transaction.
787 # multiple commits in a single transaction.
771 # Force lookup, so we don't miss such a racy file change.
788 # Force lookup, so we don't miss such a racy file change.
772 ladd(fn)
789 ladd(fn)
773 elif listclean:
790 elif listclean:
774 cadd(fn)
791 cadd(fn)
775 elif state == 'm':
792 elif state == 'm':
776 madd(fn)
793 madd(fn)
777 elif state == 'a':
794 elif state == 'a':
778 aadd(fn)
795 aadd(fn)
779 elif state == 'r':
796 elif state == 'r':
780 radd(fn)
797 radd(fn)
781
798
782 return (lookup, modified, added, removed, deleted, unknown, ignored,
799 return (lookup, modified, added, removed, deleted, unknown, ignored,
783 clean)
800 clean)
@@ -1,361 +1,361 b''
1 # extensions.py - extension handling for mercurial
1 # extensions.py - extension handling for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import imp, os
8 import imp, os
9 import util, cmdutil, error
9 import util, cmdutil, error
10 from i18n import _, gettext
10 from i18n import _, gettext
11
11
12 _extensions = {}
12 _extensions = {}
13 _order = []
13 _order = []
14 _ignore = ['hbisect', 'bookmarks', 'parentrevspec']
14 _ignore = ['hbisect', 'bookmarks', 'parentrevspec', 'interhg']
15
15
16 def extensions():
16 def extensions():
17 for name in _order:
17 for name in _order:
18 module = _extensions[name]
18 module = _extensions[name]
19 if module:
19 if module:
20 yield name, module
20 yield name, module
21
21
22 def find(name):
22 def find(name):
23 '''return module with given extension name'''
23 '''return module with given extension name'''
24 mod = None
24 mod = None
25 try:
25 try:
26 mod = _extensions[name]
26 mod = _extensions[name]
27 except KeyError:
27 except KeyError:
28 for k, v in _extensions.iteritems():
28 for k, v in _extensions.iteritems():
29 if k.endswith('.' + name) or k.endswith('/' + name):
29 if k.endswith('.' + name) or k.endswith('/' + name):
30 mod = v
30 mod = v
31 break
31 break
32 if not mod:
32 if not mod:
33 raise KeyError(name)
33 raise KeyError(name)
34 return mod
34 return mod
35
35
36 def loadpath(path, module_name):
36 def loadpath(path, module_name):
37 module_name = module_name.replace('.', '_')
37 module_name = module_name.replace('.', '_')
38 path = util.expandpath(path)
38 path = util.expandpath(path)
39 if os.path.isdir(path):
39 if os.path.isdir(path):
40 # module/__init__.py style
40 # module/__init__.py style
41 d, f = os.path.split(path.rstrip('/'))
41 d, f = os.path.split(path.rstrip('/'))
42 fd, fpath, desc = imp.find_module(f, [d])
42 fd, fpath, desc = imp.find_module(f, [d])
43 return imp.load_module(module_name, fd, fpath, desc)
43 return imp.load_module(module_name, fd, fpath, desc)
44 else:
44 else:
45 try:
45 try:
46 return imp.load_source(module_name, path)
46 return imp.load_source(module_name, path)
47 except IOError, exc:
47 except IOError, exc:
48 if not exc.filename:
48 if not exc.filename:
49 exc.filename = path # python does not fill this
49 exc.filename = path # python does not fill this
50 raise
50 raise
51
51
52 def load(ui, name, path):
52 def load(ui, name, path):
53 # unused ui argument kept for backwards compatibility
53 # unused ui argument kept for backwards compatibility
54 if name.startswith('hgext.') or name.startswith('hgext/'):
54 if name.startswith('hgext.') or name.startswith('hgext/'):
55 shortname = name[6:]
55 shortname = name[6:]
56 else:
56 else:
57 shortname = name
57 shortname = name
58 if shortname in _ignore:
58 if shortname in _ignore:
59 return None
59 return None
60 if shortname in _extensions:
60 if shortname in _extensions:
61 return _extensions[shortname]
61 return _extensions[shortname]
62 _extensions[shortname] = None
62 _extensions[shortname] = None
63 if path:
63 if path:
64 # the module will be loaded in sys.modules
64 # the module will be loaded in sys.modules
65 # choose an unique name so that it doesn't
65 # choose an unique name so that it doesn't
66 # conflicts with other modules
66 # conflicts with other modules
67 mod = loadpath(path, 'hgext.%s' % name)
67 mod = loadpath(path, 'hgext.%s' % name)
68 else:
68 else:
69 def importh(name):
69 def importh(name):
70 mod = __import__(name)
70 mod = __import__(name)
71 components = name.split('.')
71 components = name.split('.')
72 for comp in components[1:]:
72 for comp in components[1:]:
73 mod = getattr(mod, comp)
73 mod = getattr(mod, comp)
74 return mod
74 return mod
75 try:
75 try:
76 mod = importh("hgext.%s" % name)
76 mod = importh("hgext.%s" % name)
77 except ImportError, err:
77 except ImportError, err:
78 ui.debug('could not import hgext.%s (%s): trying %s\n'
78 ui.debug('could not import hgext.%s (%s): trying %s\n'
79 % (name, err, name))
79 % (name, err, name))
80 mod = importh(name)
80 mod = importh(name)
81 _extensions[shortname] = mod
81 _extensions[shortname] = mod
82 _order.append(shortname)
82 _order.append(shortname)
83 return mod
83 return mod
84
84
85 def loadall(ui):
85 def loadall(ui):
86 result = ui.configitems("extensions")
86 result = ui.configitems("extensions")
87 newindex = len(_order)
87 newindex = len(_order)
88 for (name, path) in result:
88 for (name, path) in result:
89 if path:
89 if path:
90 if path[0] == '!':
90 if path[0] == '!':
91 continue
91 continue
92 try:
92 try:
93 load(ui, name, path)
93 load(ui, name, path)
94 except KeyboardInterrupt:
94 except KeyboardInterrupt:
95 raise
95 raise
96 except Exception, inst:
96 except Exception, inst:
97 if path:
97 if path:
98 ui.warn(_("*** failed to import extension %s from %s: %s\n")
98 ui.warn(_("*** failed to import extension %s from %s: %s\n")
99 % (name, path, inst))
99 % (name, path, inst))
100 else:
100 else:
101 ui.warn(_("*** failed to import extension %s: %s\n")
101 ui.warn(_("*** failed to import extension %s: %s\n")
102 % (name, inst))
102 % (name, inst))
103 if ui.traceback():
103 if ui.traceback():
104 return 1
104 return 1
105
105
106 for name in _order[newindex:]:
106 for name in _order[newindex:]:
107 uisetup = getattr(_extensions[name], 'uisetup', None)
107 uisetup = getattr(_extensions[name], 'uisetup', None)
108 if uisetup:
108 if uisetup:
109 uisetup(ui)
109 uisetup(ui)
110
110
111 for name in _order[newindex:]:
111 for name in _order[newindex:]:
112 extsetup = getattr(_extensions[name], 'extsetup', None)
112 extsetup = getattr(_extensions[name], 'extsetup', None)
113 if extsetup:
113 if extsetup:
114 try:
114 try:
115 extsetup(ui)
115 extsetup(ui)
116 except TypeError:
116 except TypeError:
117 if extsetup.func_code.co_argcount != 0:
117 if extsetup.func_code.co_argcount != 0:
118 raise
118 raise
119 extsetup() # old extsetup with no ui argument
119 extsetup() # old extsetup with no ui argument
120
120
121 def wrapcommand(table, command, wrapper):
121 def wrapcommand(table, command, wrapper):
122 '''Wrap the command named `command' in table
122 '''Wrap the command named `command' in table
123
123
124 Replace command in the command table with wrapper. The wrapped command will
124 Replace command in the command table with wrapper. The wrapped command will
125 be inserted into the command table specified by the table argument.
125 be inserted into the command table specified by the table argument.
126
126
127 The wrapper will be called like
127 The wrapper will be called like
128
128
129 wrapper(orig, *args, **kwargs)
129 wrapper(orig, *args, **kwargs)
130
130
131 where orig is the original (wrapped) function, and *args, **kwargs
131 where orig is the original (wrapped) function, and *args, **kwargs
132 are the arguments passed to it.
132 are the arguments passed to it.
133 '''
133 '''
134 assert util.safehasattr(wrapper, '__call__')
134 assert util.safehasattr(wrapper, '__call__')
135 aliases, entry = cmdutil.findcmd(command, table)
135 aliases, entry = cmdutil.findcmd(command, table)
136 for alias, e in table.iteritems():
136 for alias, e in table.iteritems():
137 if e is entry:
137 if e is entry:
138 key = alias
138 key = alias
139 break
139 break
140
140
141 origfn = entry[0]
141 origfn = entry[0]
142 def wrap(*args, **kwargs):
142 def wrap(*args, **kwargs):
143 return util.checksignature(wrapper)(
143 return util.checksignature(wrapper)(
144 util.checksignature(origfn), *args, **kwargs)
144 util.checksignature(origfn), *args, **kwargs)
145
145
146 wrap.__doc__ = getattr(origfn, '__doc__')
146 wrap.__doc__ = getattr(origfn, '__doc__')
147 wrap.__module__ = getattr(origfn, '__module__')
147 wrap.__module__ = getattr(origfn, '__module__')
148
148
149 newentry = list(entry)
149 newentry = list(entry)
150 newentry[0] = wrap
150 newentry[0] = wrap
151 table[key] = tuple(newentry)
151 table[key] = tuple(newentry)
152 return entry
152 return entry
153
153
154 def wrapfunction(container, funcname, wrapper):
154 def wrapfunction(container, funcname, wrapper):
155 '''Wrap the function named funcname in container
155 '''Wrap the function named funcname in container
156
156
157 Replace the funcname member in the given container with the specified
157 Replace the funcname member in the given container with the specified
158 wrapper. The container is typically a module, class, or instance.
158 wrapper. The container is typically a module, class, or instance.
159
159
160 The wrapper will be called like
160 The wrapper will be called like
161
161
162 wrapper(orig, *args, **kwargs)
162 wrapper(orig, *args, **kwargs)
163
163
164 where orig is the original (wrapped) function, and *args, **kwargs
164 where orig is the original (wrapped) function, and *args, **kwargs
165 are the arguments passed to it.
165 are the arguments passed to it.
166
166
167 Wrapping methods of the repository object is not recommended since
167 Wrapping methods of the repository object is not recommended since
168 it conflicts with extensions that extend the repository by
168 it conflicts with extensions that extend the repository by
169 subclassing. All extensions that need to extend methods of
169 subclassing. All extensions that need to extend methods of
170 localrepository should use this subclassing trick: namely,
170 localrepository should use this subclassing trick: namely,
171 reposetup() should look like
171 reposetup() should look like
172
172
173 def reposetup(ui, repo):
173 def reposetup(ui, repo):
174 class myrepo(repo.__class__):
174 class myrepo(repo.__class__):
175 def whatever(self, *args, **kwargs):
175 def whatever(self, *args, **kwargs):
176 [...extension stuff...]
176 [...extension stuff...]
177 super(myrepo, self).whatever(*args, **kwargs)
177 super(myrepo, self).whatever(*args, **kwargs)
178 [...extension stuff...]
178 [...extension stuff...]
179
179
180 repo.__class__ = myrepo
180 repo.__class__ = myrepo
181
181
182 In general, combining wrapfunction() with subclassing does not
182 In general, combining wrapfunction() with subclassing does not
183 work. Since you cannot control what other extensions are loaded by
183 work. Since you cannot control what other extensions are loaded by
184 your end users, you should play nicely with others by using the
184 your end users, you should play nicely with others by using the
185 subclass trick.
185 subclass trick.
186 '''
186 '''
187 assert util.safehasattr(wrapper, '__call__')
187 assert util.safehasattr(wrapper, '__call__')
188 def wrap(*args, **kwargs):
188 def wrap(*args, **kwargs):
189 return wrapper(origfn, *args, **kwargs)
189 return wrapper(origfn, *args, **kwargs)
190
190
191 origfn = getattr(container, funcname)
191 origfn = getattr(container, funcname)
192 assert util.safehasattr(origfn, '__call__')
192 assert util.safehasattr(origfn, '__call__')
193 setattr(container, funcname, wrap)
193 setattr(container, funcname, wrap)
194 return origfn
194 return origfn
195
195
196 def _disabledpaths(strip_init=False):
196 def _disabledpaths(strip_init=False):
197 '''find paths of disabled extensions. returns a dict of {name: path}
197 '''find paths of disabled extensions. returns a dict of {name: path}
198 removes /__init__.py from packages if strip_init is True'''
198 removes /__init__.py from packages if strip_init is True'''
199 import hgext
199 import hgext
200 extpath = os.path.dirname(os.path.abspath(hgext.__file__))
200 extpath = os.path.dirname(os.path.abspath(hgext.__file__))
201 try: # might not be a filesystem path
201 try: # might not be a filesystem path
202 files = os.listdir(extpath)
202 files = os.listdir(extpath)
203 except OSError:
203 except OSError:
204 return {}
204 return {}
205
205
206 exts = {}
206 exts = {}
207 for e in files:
207 for e in files:
208 if e.endswith('.py'):
208 if e.endswith('.py'):
209 name = e.rsplit('.', 1)[0]
209 name = e.rsplit('.', 1)[0]
210 path = os.path.join(extpath, e)
210 path = os.path.join(extpath, e)
211 else:
211 else:
212 name = e
212 name = e
213 path = os.path.join(extpath, e, '__init__.py')
213 path = os.path.join(extpath, e, '__init__.py')
214 if not os.path.exists(path):
214 if not os.path.exists(path):
215 continue
215 continue
216 if strip_init:
216 if strip_init:
217 path = os.path.dirname(path)
217 path = os.path.dirname(path)
218 if name in exts or name in _order or name == '__init__':
218 if name in exts or name in _order or name == '__init__':
219 continue
219 continue
220 exts[name] = path
220 exts[name] = path
221 return exts
221 return exts
222
222
223 def _moduledoc(file):
223 def _moduledoc(file):
224 '''return the top-level python documentation for the given file
224 '''return the top-level python documentation for the given file
225
225
226 Loosely inspired by pydoc.source_synopsis(), but rewritten to
226 Loosely inspired by pydoc.source_synopsis(), but rewritten to
227 handle triple quotes and to return the whole text instead of just
227 handle triple quotes and to return the whole text instead of just
228 the synopsis'''
228 the synopsis'''
229 result = []
229 result = []
230
230
231 line = file.readline()
231 line = file.readline()
232 while line[:1] == '#' or not line.strip():
232 while line[:1] == '#' or not line.strip():
233 line = file.readline()
233 line = file.readline()
234 if not line:
234 if not line:
235 break
235 break
236
236
237 start = line[:3]
237 start = line[:3]
238 if start == '"""' or start == "'''":
238 if start == '"""' or start == "'''":
239 line = line[3:]
239 line = line[3:]
240 while line:
240 while line:
241 if line.rstrip().endswith(start):
241 if line.rstrip().endswith(start):
242 line = line.split(start)[0]
242 line = line.split(start)[0]
243 if line:
243 if line:
244 result.append(line)
244 result.append(line)
245 break
245 break
246 elif not line:
246 elif not line:
247 return None # unmatched delimiter
247 return None # unmatched delimiter
248 result.append(line)
248 result.append(line)
249 line = file.readline()
249 line = file.readline()
250 else:
250 else:
251 return None
251 return None
252
252
253 return ''.join(result)
253 return ''.join(result)
254
254
255 def _disabledhelp(path):
255 def _disabledhelp(path):
256 '''retrieve help synopsis of a disabled extension (without importing)'''
256 '''retrieve help synopsis of a disabled extension (without importing)'''
257 try:
257 try:
258 file = open(path)
258 file = open(path)
259 except IOError:
259 except IOError:
260 return
260 return
261 else:
261 else:
262 doc = _moduledoc(file)
262 doc = _moduledoc(file)
263 file.close()
263 file.close()
264
264
265 if doc: # extracting localized synopsis
265 if doc: # extracting localized synopsis
266 return gettext(doc).splitlines()[0]
266 return gettext(doc).splitlines()[0]
267 else:
267 else:
268 return _('(no help text available)')
268 return _('(no help text available)')
269
269
270 def disabled():
270 def disabled():
271 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
271 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
272 try:
272 try:
273 from hgext import __index__
273 from hgext import __index__
274 return dict((name, gettext(desc))
274 return dict((name, gettext(desc))
275 for name, desc in __index__.docs.iteritems()
275 for name, desc in __index__.docs.iteritems()
276 if name not in _order)
276 if name not in _order)
277 except ImportError:
277 except ImportError:
278 pass
278 pass
279
279
280 paths = _disabledpaths()
280 paths = _disabledpaths()
281 if not paths:
281 if not paths:
282 return {}
282 return {}
283
283
284 exts = {}
284 exts = {}
285 for name, path in paths.iteritems():
285 for name, path in paths.iteritems():
286 doc = _disabledhelp(path)
286 doc = _disabledhelp(path)
287 if doc:
287 if doc:
288 exts[name] = doc
288 exts[name] = doc
289
289
290 return exts
290 return exts
291
291
292 def disabledext(name):
292 def disabledext(name):
293 '''find a specific disabled extension from hgext. returns desc'''
293 '''find a specific disabled extension from hgext. returns desc'''
294 try:
294 try:
295 from hgext import __index__
295 from hgext import __index__
296 if name in _order: # enabled
296 if name in _order: # enabled
297 return
297 return
298 else:
298 else:
299 return gettext(__index__.docs.get(name))
299 return gettext(__index__.docs.get(name))
300 except ImportError:
300 except ImportError:
301 pass
301 pass
302
302
303 paths = _disabledpaths()
303 paths = _disabledpaths()
304 if name in paths:
304 if name in paths:
305 return _disabledhelp(paths[name])
305 return _disabledhelp(paths[name])
306
306
307 def disabledcmd(ui, cmd, strict=False):
307 def disabledcmd(ui, cmd, strict=False):
308 '''import disabled extensions until cmd is found.
308 '''import disabled extensions until cmd is found.
309 returns (cmdname, extname, module)'''
309 returns (cmdname, extname, module)'''
310
310
311 paths = _disabledpaths(strip_init=True)
311 paths = _disabledpaths(strip_init=True)
312 if not paths:
312 if not paths:
313 raise error.UnknownCommand(cmd)
313 raise error.UnknownCommand(cmd)
314
314
315 def findcmd(cmd, name, path):
315 def findcmd(cmd, name, path):
316 try:
316 try:
317 mod = loadpath(path, 'hgext.%s' % name)
317 mod = loadpath(path, 'hgext.%s' % name)
318 except Exception:
318 except Exception:
319 return
319 return
320 try:
320 try:
321 aliases, entry = cmdutil.findcmd(cmd,
321 aliases, entry = cmdutil.findcmd(cmd,
322 getattr(mod, 'cmdtable', {}), strict)
322 getattr(mod, 'cmdtable', {}), strict)
323 except (error.AmbiguousCommand, error.UnknownCommand):
323 except (error.AmbiguousCommand, error.UnknownCommand):
324 return
324 return
325 except Exception:
325 except Exception:
326 ui.warn(_('warning: error finding commands in %s\n') % path)
326 ui.warn(_('warning: error finding commands in %s\n') % path)
327 ui.traceback()
327 ui.traceback()
328 return
328 return
329 for c in aliases:
329 for c in aliases:
330 if c.startswith(cmd):
330 if c.startswith(cmd):
331 cmd = c
331 cmd = c
332 break
332 break
333 else:
333 else:
334 cmd = aliases[0]
334 cmd = aliases[0]
335 return (cmd, name, mod)
335 return (cmd, name, mod)
336
336
337 ext = None
337 ext = None
338 # first, search for an extension with the same name as the command
338 # first, search for an extension with the same name as the command
339 path = paths.pop(cmd, None)
339 path = paths.pop(cmd, None)
340 if path:
340 if path:
341 ext = findcmd(cmd, cmd, path)
341 ext = findcmd(cmd, cmd, path)
342 if not ext:
342 if not ext:
343 # otherwise, interrogate each extension until there's a match
343 # otherwise, interrogate each extension until there's a match
344 for name, path in paths.iteritems():
344 for name, path in paths.iteritems():
345 ext = findcmd(cmd, name, path)
345 ext = findcmd(cmd, name, path)
346 if ext:
346 if ext:
347 break
347 break
348 if ext and 'DEPRECATED' not in ext.__doc__:
348 if ext and 'DEPRECATED' not in ext.__doc__:
349 return ext
349 return ext
350
350
351 raise error.UnknownCommand(cmd)
351 raise error.UnknownCommand(cmd)
352
352
353 def enabled():
353 def enabled():
354 '''return a dict of {name: desc} of extensions'''
354 '''return a dict of {name: desc} of extensions'''
355 exts = {}
355 exts = {}
356 for ename, ext in extensions():
356 for ename, ext in extensions():
357 doc = (gettext(ext.__doc__) or _('(no help text available)'))
357 doc = (gettext(ext.__doc__) or _('(no help text available)'))
358 ename = ename.split('.')[-1]
358 ename = ename.split('.')[-1]
359 exts[ename] = doc.splitlines()[0].strip()
359 exts[ename] = doc.splitlines()[0].strip()
360
360
361 return exts
361 return exts
@@ -1,1476 +1,1509 b''
1 The Mercurial system uses a set of configuration files to control
1 The Mercurial system uses a set of configuration files to control
2 aspects of its behavior.
2 aspects of its behavior.
3
3
4 The configuration files use a simple ini-file format. A configuration
4 The configuration files use a simple ini-file format. A configuration
5 file consists of sections, led by a ``[section]`` header and followed
5 file consists of sections, led by a ``[section]`` header and followed
6 by ``name = value`` entries::
6 by ``name = value`` entries::
7
7
8 [ui]
8 [ui]
9 username = Firstname Lastname <firstname.lastname@example.net>
9 username = Firstname Lastname <firstname.lastname@example.net>
10 verbose = True
10 verbose = True
11
11
12 The above entries will be referred to as ``ui.username`` and
12 The above entries will be referred to as ``ui.username`` and
13 ``ui.verbose``, respectively. See the Syntax section below.
13 ``ui.verbose``, respectively. See the Syntax section below.
14
14
15 Files
15 Files
16 =====
16 =====
17
17
18 Mercurial reads configuration data from several files, if they exist.
18 Mercurial reads configuration data from several files, if they exist.
19 These files do not exist by default and you will have to create the
19 These files do not exist by default and you will have to create the
20 appropriate configuration files yourself: global configuration like
20 appropriate configuration files yourself: global configuration like
21 the username setting is typically put into
21 the username setting is typically put into
22 ``%USERPROFILE%\mercurial.ini`` or ``$HOME/.hgrc`` and local
22 ``%USERPROFILE%\mercurial.ini`` or ``$HOME/.hgrc`` and local
23 configuration is put into the per-repository ``<repo>/.hg/hgrc`` file.
23 configuration is put into the per-repository ``<repo>/.hg/hgrc`` file.
24
24
25 The names of these files depend on the system on which Mercurial is
25 The names of these files depend on the system on which Mercurial is
26 installed. ``*.rc`` files from a single directory are read in
26 installed. ``*.rc`` files from a single directory are read in
27 alphabetical order, later ones overriding earlier ones. Where multiple
27 alphabetical order, later ones overriding earlier ones. Where multiple
28 paths are given below, settings from earlier paths override later
28 paths are given below, settings from earlier paths override later
29 ones.
29 ones.
30
30
31 | (All) ``<repo>/.hg/hgrc``
31 | (All) ``<repo>/.hg/hgrc``
32
32
33 Per-repository configuration options that only apply in a
33 Per-repository configuration options that only apply in a
34 particular repository. This file is not version-controlled, and
34 particular repository. This file is not version-controlled, and
35 will not get transferred during a "clone" operation. Options in
35 will not get transferred during a "clone" operation. Options in
36 this file override options in all other configuration files. On
36 this file override options in all other configuration files. On
37 Plan 9 and Unix, most of this file will be ignored if it doesn't
37 Plan 9 and Unix, most of this file will be ignored if it doesn't
38 belong to a trusted user or to a trusted group. See the documentation
38 belong to a trusted user or to a trusted group. See the documentation
39 for the ``[trusted]`` section below for more details.
39 for the ``[trusted]`` section below for more details.
40
40
41 | (Plan 9) ``$home/lib/hgrc``
41 | (Plan 9) ``$home/lib/hgrc``
42 | (Unix) ``$HOME/.hgrc``
42 | (Unix) ``$HOME/.hgrc``
43 | (Windows) ``%USERPROFILE%\.hgrc``
43 | (Windows) ``%USERPROFILE%\.hgrc``
44 | (Windows) ``%USERPROFILE%\Mercurial.ini``
44 | (Windows) ``%USERPROFILE%\Mercurial.ini``
45 | (Windows) ``%HOME%\.hgrc``
45 | (Windows) ``%HOME%\.hgrc``
46 | (Windows) ``%HOME%\Mercurial.ini``
46 | (Windows) ``%HOME%\Mercurial.ini``
47
47
48 Per-user configuration file(s), for the user running Mercurial. On
48 Per-user configuration file(s), for the user running Mercurial. On
49 Windows 9x, ``%HOME%`` is replaced by ``%APPDATA%``. Options in these
49 Windows 9x, ``%HOME%`` is replaced by ``%APPDATA%``. Options in these
50 files apply to all Mercurial commands executed by this user in any
50 files apply to all Mercurial commands executed by this user in any
51 directory. Options in these files override per-system and per-installation
51 directory. Options in these files override per-system and per-installation
52 options.
52 options.
53
53
54 | (Plan 9) ``/lib/mercurial/hgrc``
54 | (Plan 9) ``/lib/mercurial/hgrc``
55 | (Plan 9) ``/lib/mercurial/hgrc.d/*.rc``
55 | (Plan 9) ``/lib/mercurial/hgrc.d/*.rc``
56 | (Unix) ``/etc/mercurial/hgrc``
56 | (Unix) ``/etc/mercurial/hgrc``
57 | (Unix) ``/etc/mercurial/hgrc.d/*.rc``
57 | (Unix) ``/etc/mercurial/hgrc.d/*.rc``
58
58
59 Per-system configuration files, for the system on which Mercurial
59 Per-system configuration files, for the system on which Mercurial
60 is running. Options in these files apply to all Mercurial commands
60 is running. Options in these files apply to all Mercurial commands
61 executed by any user in any directory. Options in these files
61 executed by any user in any directory. Options in these files
62 override per-installation options.
62 override per-installation options.
63
63
64 | (Plan 9) ``<install-root>/lib/mercurial/hgrc``
64 | (Plan 9) ``<install-root>/lib/mercurial/hgrc``
65 | (Plan 9) ``<install-root>/lib/mercurial/hgrc.d/*.rc``
65 | (Plan 9) ``<install-root>/lib/mercurial/hgrc.d/*.rc``
66 | (Unix) ``<install-root>/etc/mercurial/hgrc``
66 | (Unix) ``<install-root>/etc/mercurial/hgrc``
67 | (Unix) ``<install-root>/etc/mercurial/hgrc.d/*.rc``
67 | (Unix) ``<install-root>/etc/mercurial/hgrc.d/*.rc``
68
68
69 Per-installation configuration files, searched for in the
69 Per-installation configuration files, searched for in the
70 directory where Mercurial is installed. ``<install-root>`` is the
70 directory where Mercurial is installed. ``<install-root>`` is the
71 parent directory of the **hg** executable (or symlink) being run. For
71 parent directory of the **hg** executable (or symlink) being run. For
72 example, if installed in ``/shared/tools/bin/hg``, Mercurial will look
72 example, if installed in ``/shared/tools/bin/hg``, Mercurial will look
73 in ``/shared/tools/etc/mercurial/hgrc``. Options in these files apply
73 in ``/shared/tools/etc/mercurial/hgrc``. Options in these files apply
74 to all Mercurial commands executed by any user in any directory.
74 to all Mercurial commands executed by any user in any directory.
75
75
76 | (Windows) ``<install-dir>\Mercurial.ini`` **or**
76 | (Windows) ``<install-dir>\Mercurial.ini`` **or**
77 | (Windows) ``<install-dir>\hgrc.d\*.rc`` **or**
77 | (Windows) ``<install-dir>\hgrc.d\*.rc`` **or**
78 | (Windows) ``HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial``
78 | (Windows) ``HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial``
79
79
80 Per-installation/system configuration files, for the system on
80 Per-installation/system configuration files, for the system on
81 which Mercurial is running. Options in these files apply to all
81 which Mercurial is running. Options in these files apply to all
82 Mercurial commands executed by any user in any directory. Registry
82 Mercurial commands executed by any user in any directory. Registry
83 keys contain PATH-like strings, every part of which must reference
83 keys contain PATH-like strings, every part of which must reference
84 a ``Mercurial.ini`` file or be a directory where ``*.rc`` files will
84 a ``Mercurial.ini`` file or be a directory where ``*.rc`` files will
85 be read. Mercurial checks each of these locations in the specified
85 be read. Mercurial checks each of these locations in the specified
86 order until one or more configuration files are detected.
86 order until one or more configuration files are detected.
87
87
88 Syntax
88 Syntax
89 ======
89 ======
90
90
91 A configuration file consists of sections, led by a ``[section]`` header
91 A configuration file consists of sections, led by a ``[section]`` header
92 and followed by ``name = value`` entries (sometimes called
92 and followed by ``name = value`` entries (sometimes called
93 ``configuration keys``)::
93 ``configuration keys``)::
94
94
95 [spam]
95 [spam]
96 eggs=ham
96 eggs=ham
97 green=
97 green=
98 eggs
98 eggs
99
99
100 Each line contains one entry. If the lines that follow are indented,
100 Each line contains one entry. If the lines that follow are indented,
101 they are treated as continuations of that entry. Leading whitespace is
101 they are treated as continuations of that entry. Leading whitespace is
102 removed from values. Empty lines are skipped. Lines beginning with
102 removed from values. Empty lines are skipped. Lines beginning with
103 ``#`` or ``;`` are ignored and may be used to provide comments.
103 ``#`` or ``;`` are ignored and may be used to provide comments.
104
104
105 Configuration keys can be set multiple times, in which case Mercurial
105 Configuration keys can be set multiple times, in which case Mercurial
106 will use the value that was configured last. As an example::
106 will use the value that was configured last. As an example::
107
107
108 [spam]
108 [spam]
109 eggs=large
109 eggs=large
110 ham=serrano
110 ham=serrano
111 eggs=small
111 eggs=small
112
112
113 This would set the configuration key named ``eggs`` to ``small``.
113 This would set the configuration key named ``eggs`` to ``small``.
114
114
115 It is also possible to define a section multiple times. A section can
115 It is also possible to define a section multiple times. A section can
116 be redefined on the same and/or on different configuration files. For
116 be redefined on the same and/or on different configuration files. For
117 example::
117 example::
118
118
119 [foo]
119 [foo]
120 eggs=large
120 eggs=large
121 ham=serrano
121 ham=serrano
122 eggs=small
122 eggs=small
123
123
124 [bar]
124 [bar]
125 eggs=ham
125 eggs=ham
126 green=
126 green=
127 eggs
127 eggs
128
128
129 [foo]
129 [foo]
130 ham=prosciutto
130 ham=prosciutto
131 eggs=medium
131 eggs=medium
132 bread=toasted
132 bread=toasted
133
133
134 This would set the ``eggs``, ``ham``, and ``bread`` configuration keys
134 This would set the ``eggs``, ``ham``, and ``bread`` configuration keys
135 of the ``foo`` section to ``medium``, ``prosciutto``, and ``toasted``,
135 of the ``foo`` section to ``medium``, ``prosciutto``, and ``toasted``,
136 respectively. As you can see there only thing that matters is the last
136 respectively. As you can see there only thing that matters is the last
137 value that was set for each of the configuration keys.
137 value that was set for each of the configuration keys.
138
138
139 If a configuration key is set multiple times in different
139 If a configuration key is set multiple times in different
140 configuration files the final value will depend on the order in which
140 configuration files the final value will depend on the order in which
141 the different configuration files are read, with settings from earlier
141 the different configuration files are read, with settings from earlier
142 paths overriding later ones as described on the ``Files`` section
142 paths overriding later ones as described on the ``Files`` section
143 above.
143 above.
144
144
145 A line of the form ``%include file`` will include ``file`` into the
145 A line of the form ``%include file`` will include ``file`` into the
146 current configuration file. The inclusion is recursive, which means
146 current configuration file. The inclusion is recursive, which means
147 that included files can include other files. Filenames are relative to
147 that included files can include other files. Filenames are relative to
148 the configuration file in which the ``%include`` directive is found.
148 the configuration file in which the ``%include`` directive is found.
149 Environment variables and ``~user`` constructs are expanded in
149 Environment variables and ``~user`` constructs are expanded in
150 ``file``. This lets you do something like::
150 ``file``. This lets you do something like::
151
151
152 %include ~/.hgrc.d/$HOST.rc
152 %include ~/.hgrc.d/$HOST.rc
153
153
154 to include a different configuration file on each computer you use.
154 to include a different configuration file on each computer you use.
155
155
156 A line with ``%unset name`` will remove ``name`` from the current
156 A line with ``%unset name`` will remove ``name`` from the current
157 section, if it has been set previously.
157 section, if it has been set previously.
158
158
159 The values are either free-form text strings, lists of text strings,
159 The values are either free-form text strings, lists of text strings,
160 or Boolean values. Boolean values can be set to true using any of "1",
160 or Boolean values. Boolean values can be set to true using any of "1",
161 "yes", "true", or "on" and to false using "0", "no", "false", or "off"
161 "yes", "true", or "on" and to false using "0", "no", "false", or "off"
162 (all case insensitive).
162 (all case insensitive).
163
163
164 List values are separated by whitespace or comma, except when values are
164 List values are separated by whitespace or comma, except when values are
165 placed in double quotation marks::
165 placed in double quotation marks::
166
166
167 allow_read = "John Doe, PhD", brian, betty
167 allow_read = "John Doe, PhD", brian, betty
168
168
169 Quotation marks can be escaped by prefixing them with a backslash. Only
169 Quotation marks can be escaped by prefixing them with a backslash. Only
170 quotation marks at the beginning of a word is counted as a quotation
170 quotation marks at the beginning of a word is counted as a quotation
171 (e.g., ``foo"bar baz`` is the list of ``foo"bar`` and ``baz``).
171 (e.g., ``foo"bar baz`` is the list of ``foo"bar`` and ``baz``).
172
172
173 Sections
173 Sections
174 ========
174 ========
175
175
176 This section describes the different sections that may appear in a
176 This section describes the different sections that may appear in a
177 Mercurial configuration file, the purpose of each section, its possible
177 Mercurial configuration file, the purpose of each section, its possible
178 keys, and their possible values.
178 keys, and their possible values.
179
179
180 ``alias``
180 ``alias``
181 ---------
181 ---------
182
182
183 Defines command aliases.
183 Defines command aliases.
184 Aliases allow you to define your own commands in terms of other
184 Aliases allow you to define your own commands in terms of other
185 commands (or aliases), optionally including arguments. Positional
185 commands (or aliases), optionally including arguments. Positional
186 arguments in the form of ``$1``, ``$2``, etc in the alias definition
186 arguments in the form of ``$1``, ``$2``, etc in the alias definition
187 are expanded by Mercurial before execution. Positional arguments not
187 are expanded by Mercurial before execution. Positional arguments not
188 already used by ``$N`` in the definition are put at the end of the
188 already used by ``$N`` in the definition are put at the end of the
189 command to be executed.
189 command to be executed.
190
190
191 Alias definitions consist of lines of the form::
191 Alias definitions consist of lines of the form::
192
192
193 <alias> = <command> [<argument>]...
193 <alias> = <command> [<argument>]...
194
194
195 For example, this definition::
195 For example, this definition::
196
196
197 latest = log --limit 5
197 latest = log --limit 5
198
198
199 creates a new command ``latest`` that shows only the five most recent
199 creates a new command ``latest`` that shows only the five most recent
200 changesets. You can define subsequent aliases using earlier ones::
200 changesets. You can define subsequent aliases using earlier ones::
201
201
202 stable5 = latest -b stable
202 stable5 = latest -b stable
203
203
204 .. note:: It is possible to create aliases with the same names as
204 .. note:: It is possible to create aliases with the same names as
205 existing commands, which will then override the original
205 existing commands, which will then override the original
206 definitions. This is almost always a bad idea!
206 definitions. This is almost always a bad idea!
207
207
208 An alias can start with an exclamation point (``!``) to make it a
208 An alias can start with an exclamation point (``!``) to make it a
209 shell alias. A shell alias is executed with the shell and will let you
209 shell alias. A shell alias is executed with the shell and will let you
210 run arbitrary commands. As an example, ::
210 run arbitrary commands. As an example, ::
211
211
212 echo = !echo $@
212 echo = !echo $@
213
213
214 will let you do ``hg echo foo`` to have ``foo`` printed in your
214 will let you do ``hg echo foo`` to have ``foo`` printed in your
215 terminal. A better example might be::
215 terminal. A better example might be::
216
216
217 purge = !$HG status --no-status --unknown -0 | xargs -0 rm
217 purge = !$HG status --no-status --unknown -0 | xargs -0 rm
218
218
219 which will make ``hg purge`` delete all unknown files in the
219 which will make ``hg purge`` delete all unknown files in the
220 repository in the same manner as the purge extension.
220 repository in the same manner as the purge extension.
221
221
222 Positional arguments like ``$1``, ``$2``, etc. in the alias definition
222 Positional arguments like ``$1``, ``$2``, etc. in the alias definition
223 expand to the command arguments. Unmatched arguments are
223 expand to the command arguments. Unmatched arguments are
224 removed. ``$0`` expands to the alias name and ``$@`` expands to all
224 removed. ``$0`` expands to the alias name and ``$@`` expands to all
225 arguments separated by a space. These expansions happen before the
225 arguments separated by a space. These expansions happen before the
226 command is passed to the shell.
226 command is passed to the shell.
227
227
228 Shell aliases are executed in an environment where ``$HG`` expands to
228 Shell aliases are executed in an environment where ``$HG`` expands to
229 the path of the Mercurial that was used to execute the alias. This is
229 the path of the Mercurial that was used to execute the alias. This is
230 useful when you want to call further Mercurial commands in a shell
230 useful when you want to call further Mercurial commands in a shell
231 alias, as was done above for the purge alias. In addition,
231 alias, as was done above for the purge alias. In addition,
232 ``$HG_ARGS`` expands to the arguments given to Mercurial. In the ``hg
232 ``$HG_ARGS`` expands to the arguments given to Mercurial. In the ``hg
233 echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``.
233 echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``.
234
234
235 .. note:: Some global configuration options such as ``-R`` are
235 .. note:: Some global configuration options such as ``-R`` are
236 processed before shell aliases and will thus not be passed to
236 processed before shell aliases and will thus not be passed to
237 aliases.
237 aliases.
238
238
239
239
240 ``annotate``
240 ``annotate``
241 ------------
241 ------------
242
242
243 Settings used when displaying file annotations. All values are
243 Settings used when displaying file annotations. All values are
244 Booleans and default to False. See ``diff`` section for related
244 Booleans and default to False. See ``diff`` section for related
245 options for the diff command.
245 options for the diff command.
246
246
247 ``ignorews``
247 ``ignorews``
248 Ignore white space when comparing lines.
248 Ignore white space when comparing lines.
249
249
250 ``ignorewsamount``
250 ``ignorewsamount``
251 Ignore changes in the amount of white space.
251 Ignore changes in the amount of white space.
252
252
253 ``ignoreblanklines``
253 ``ignoreblanklines``
254 Ignore changes whose lines are all blank.
254 Ignore changes whose lines are all blank.
255
255
256
256
257 ``auth``
257 ``auth``
258 --------
258 --------
259
259
260 Authentication credentials for HTTP authentication. This section
260 Authentication credentials for HTTP authentication. This section
261 allows you to store usernames and passwords for use when logging
261 allows you to store usernames and passwords for use when logging
262 *into* HTTP servers. See the ``[web]`` configuration section if
262 *into* HTTP servers. See the ``[web]`` configuration section if
263 you want to configure *who* can login to your HTTP server.
263 you want to configure *who* can login to your HTTP server.
264
264
265 Each line has the following format::
265 Each line has the following format::
266
266
267 <name>.<argument> = <value>
267 <name>.<argument> = <value>
268
268
269 where ``<name>`` is used to group arguments into authentication
269 where ``<name>`` is used to group arguments into authentication
270 entries. Example::
270 entries. Example::
271
271
272 foo.prefix = hg.intevation.org/mercurial
272 foo.prefix = hg.intevation.org/mercurial
273 foo.username = foo
273 foo.username = foo
274 foo.password = bar
274 foo.password = bar
275 foo.schemes = http https
275 foo.schemes = http https
276
276
277 bar.prefix = secure.example.org
277 bar.prefix = secure.example.org
278 bar.key = path/to/file.key
278 bar.key = path/to/file.key
279 bar.cert = path/to/file.cert
279 bar.cert = path/to/file.cert
280 bar.schemes = https
280 bar.schemes = https
281
281
282 Supported arguments:
282 Supported arguments:
283
283
284 ``prefix``
284 ``prefix``
285 Either ``*`` or a URI prefix with or without the scheme part.
285 Either ``*`` or a URI prefix with or without the scheme part.
286 The authentication entry with the longest matching prefix is used
286 The authentication entry with the longest matching prefix is used
287 (where ``*`` matches everything and counts as a match of length
287 (where ``*`` matches everything and counts as a match of length
288 1). If the prefix doesn't include a scheme, the match is performed
288 1). If the prefix doesn't include a scheme, the match is performed
289 against the URI with its scheme stripped as well, and the schemes
289 against the URI with its scheme stripped as well, and the schemes
290 argument, q.v., is then subsequently consulted.
290 argument, q.v., is then subsequently consulted.
291
291
292 ``username``
292 ``username``
293 Optional. Username to authenticate with. If not given, and the
293 Optional. Username to authenticate with. If not given, and the
294 remote site requires basic or digest authentication, the user will
294 remote site requires basic or digest authentication, the user will
295 be prompted for it. Environment variables are expanded in the
295 be prompted for it. Environment variables are expanded in the
296 username letting you do ``foo.username = $USER``. If the URI
296 username letting you do ``foo.username = $USER``. If the URI
297 includes a username, only ``[auth]`` entries with a matching
297 includes a username, only ``[auth]`` entries with a matching
298 username or without a username will be considered.
298 username or without a username will be considered.
299
299
300 ``password``
300 ``password``
301 Optional. Password to authenticate with. If not given, and the
301 Optional. Password to authenticate with. If not given, and the
302 remote site requires basic or digest authentication, the user
302 remote site requires basic or digest authentication, the user
303 will be prompted for it.
303 will be prompted for it.
304
304
305 ``key``
305 ``key``
306 Optional. PEM encoded client certificate key file. Environment
306 Optional. PEM encoded client certificate key file. Environment
307 variables are expanded in the filename.
307 variables are expanded in the filename.
308
308
309 ``cert``
309 ``cert``
310 Optional. PEM encoded client certificate chain file. Environment
310 Optional. PEM encoded client certificate chain file. Environment
311 variables are expanded in the filename.
311 variables are expanded in the filename.
312
312
313 ``schemes``
313 ``schemes``
314 Optional. Space separated list of URI schemes to use this
314 Optional. Space separated list of URI schemes to use this
315 authentication entry with. Only used if the prefix doesn't include
315 authentication entry with. Only used if the prefix doesn't include
316 a scheme. Supported schemes are http and https. They will match
316 a scheme. Supported schemes are http and https. They will match
317 static-http and static-https respectively, as well.
317 static-http and static-https respectively, as well.
318 Default: https.
318 Default: https.
319
319
320 If no suitable authentication entry is found, the user is prompted
320 If no suitable authentication entry is found, the user is prompted
321 for credentials as usual if required by the remote.
321 for credentials as usual if required by the remote.
322
322
323
323
324 ``decode/encode``
324 ``decode/encode``
325 -----------------
325 -----------------
326
326
327 Filters for transforming files on checkout/checkin. This would
327 Filters for transforming files on checkout/checkin. This would
328 typically be used for newline processing or other
328 typically be used for newline processing or other
329 localization/canonicalization of files.
329 localization/canonicalization of files.
330
330
331 Filters consist of a filter pattern followed by a filter command.
331 Filters consist of a filter pattern followed by a filter command.
332 Filter patterns are globs by default, rooted at the repository root.
332 Filter patterns are globs by default, rooted at the repository root.
333 For example, to match any file ending in ``.txt`` in the root
333 For example, to match any file ending in ``.txt`` in the root
334 directory only, use the pattern ``*.txt``. To match any file ending
334 directory only, use the pattern ``*.txt``. To match any file ending
335 in ``.c`` anywhere in the repository, use the pattern ``**.c``.
335 in ``.c`` anywhere in the repository, use the pattern ``**.c``.
336 For each file only the first matching filter applies.
336 For each file only the first matching filter applies.
337
337
338 The filter command can start with a specifier, either ``pipe:`` or
338 The filter command can start with a specifier, either ``pipe:`` or
339 ``tempfile:``. If no specifier is given, ``pipe:`` is used by default.
339 ``tempfile:``. If no specifier is given, ``pipe:`` is used by default.
340
340
341 A ``pipe:`` command must accept data on stdin and return the transformed
341 A ``pipe:`` command must accept data on stdin and return the transformed
342 data on stdout.
342 data on stdout.
343
343
344 Pipe example::
344 Pipe example::
345
345
346 [encode]
346 [encode]
347 # uncompress gzip files on checkin to improve delta compression
347 # uncompress gzip files on checkin to improve delta compression
348 # note: not necessarily a good idea, just an example
348 # note: not necessarily a good idea, just an example
349 *.gz = pipe: gunzip
349 *.gz = pipe: gunzip
350
350
351 [decode]
351 [decode]
352 # recompress gzip files when writing them to the working dir (we
352 # recompress gzip files when writing them to the working dir (we
353 # can safely omit "pipe:", because it's the default)
353 # can safely omit "pipe:", because it's the default)
354 *.gz = gzip
354 *.gz = gzip
355
355
356 A ``tempfile:`` command is a template. The string ``INFILE`` is replaced
356 A ``tempfile:`` command is a template. The string ``INFILE`` is replaced
357 with the name of a temporary file that contains the data to be
357 with the name of a temporary file that contains the data to be
358 filtered by the command. The string ``OUTFILE`` is replaced with the name
358 filtered by the command. The string ``OUTFILE`` is replaced with the name
359 of an empty temporary file, where the filtered data must be written by
359 of an empty temporary file, where the filtered data must be written by
360 the command.
360 the command.
361
361
362 .. note:: The tempfile mechanism is recommended for Windows systems,
362 .. note:: The tempfile mechanism is recommended for Windows systems,
363 where the standard shell I/O redirection operators often have
363 where the standard shell I/O redirection operators often have
364 strange effects and may corrupt the contents of your files.
364 strange effects and may corrupt the contents of your files.
365
365
366 This filter mechanism is used internally by the ``eol`` extension to
366 This filter mechanism is used internally by the ``eol`` extension to
367 translate line ending characters between Windows (CRLF) and Unix (LF)
367 translate line ending characters between Windows (CRLF) and Unix (LF)
368 format. We suggest you use the ``eol`` extension for convenience.
368 format. We suggest you use the ``eol`` extension for convenience.
369
369
370
370
371 ``defaults``
371 ``defaults``
372 ------------
372 ------------
373
373
374 (defaults are deprecated. Don't use them. Use aliases instead)
374 (defaults are deprecated. Don't use them. Use aliases instead)
375
375
376 Use the ``[defaults]`` section to define command defaults, i.e. the
376 Use the ``[defaults]`` section to define command defaults, i.e. the
377 default options/arguments to pass to the specified commands.
377 default options/arguments to pass to the specified commands.
378
378
379 The following example makes :hg:`log` run in verbose mode, and
379 The following example makes :hg:`log` run in verbose mode, and
380 :hg:`status` show only the modified files, by default::
380 :hg:`status` show only the modified files, by default::
381
381
382 [defaults]
382 [defaults]
383 log = -v
383 log = -v
384 status = -m
384 status = -m
385
385
386 The actual commands, instead of their aliases, must be used when
386 The actual commands, instead of their aliases, must be used when
387 defining command defaults. The command defaults will also be applied
387 defining command defaults. The command defaults will also be applied
388 to the aliases of the commands defined.
388 to the aliases of the commands defined.
389
389
390
390
391 ``diff``
391 ``diff``
392 --------
392 --------
393
393
394 Settings used when displaying diffs. Everything except for ``unified``
394 Settings used when displaying diffs. Everything except for ``unified``
395 is a Boolean and defaults to False. See ``annotate`` section for
395 is a Boolean and defaults to False. See ``annotate`` section for
396 related options for the annotate command.
396 related options for the annotate command.
397
397
398 ``git``
398 ``git``
399 Use git extended diff format.
399 Use git extended diff format.
400
400
401 ``nodates``
401 ``nodates``
402 Don't include dates in diff headers.
402 Don't include dates in diff headers.
403
403
404 ``showfunc``
404 ``showfunc``
405 Show which function each change is in.
405 Show which function each change is in.
406
406
407 ``ignorews``
407 ``ignorews``
408 Ignore white space when comparing lines.
408 Ignore white space when comparing lines.
409
409
410 ``ignorewsamount``
410 ``ignorewsamount``
411 Ignore changes in the amount of white space.
411 Ignore changes in the amount of white space.
412
412
413 ``ignoreblanklines``
413 ``ignoreblanklines``
414 Ignore changes whose lines are all blank.
414 Ignore changes whose lines are all blank.
415
415
416 ``unified``
416 ``unified``
417 Number of lines of context to show.
417 Number of lines of context to show.
418
418
419 ``email``
419 ``email``
420 ---------
420 ---------
421
421
422 Settings for extensions that send email messages.
422 Settings for extensions that send email messages.
423
423
424 ``from``
424 ``from``
425 Optional. Email address to use in "From" header and SMTP envelope
425 Optional. Email address to use in "From" header and SMTP envelope
426 of outgoing messages.
426 of outgoing messages.
427
427
428 ``to``
428 ``to``
429 Optional. Comma-separated list of recipients' email addresses.
429 Optional. Comma-separated list of recipients' email addresses.
430
430
431 ``cc``
431 ``cc``
432 Optional. Comma-separated list of carbon copy recipients'
432 Optional. Comma-separated list of carbon copy recipients'
433 email addresses.
433 email addresses.
434
434
435 ``bcc``
435 ``bcc``
436 Optional. Comma-separated list of blind carbon copy recipients'
436 Optional. Comma-separated list of blind carbon copy recipients'
437 email addresses.
437 email addresses.
438
438
439 ``method``
439 ``method``
440 Optional. Method to use to send email messages. If value is ``smtp``
440 Optional. Method to use to send email messages. If value is ``smtp``
441 (default), use SMTP (see the ``[smtp]`` section for configuration).
441 (default), use SMTP (see the ``[smtp]`` section for configuration).
442 Otherwise, use as name of program to run that acts like sendmail
442 Otherwise, use as name of program to run that acts like sendmail
443 (takes ``-f`` option for sender, list of recipients on command line,
443 (takes ``-f`` option for sender, list of recipients on command line,
444 message on stdin). Normally, setting this to ``sendmail`` or
444 message on stdin). Normally, setting this to ``sendmail`` or
445 ``/usr/sbin/sendmail`` is enough to use sendmail to send messages.
445 ``/usr/sbin/sendmail`` is enough to use sendmail to send messages.
446
446
447 ``charsets``
447 ``charsets``
448 Optional. Comma-separated list of character sets considered
448 Optional. Comma-separated list of character sets considered
449 convenient for recipients. Addresses, headers, and parts not
449 convenient for recipients. Addresses, headers, and parts not
450 containing patches of outgoing messages will be encoded in the
450 containing patches of outgoing messages will be encoded in the
451 first character set to which conversion from local encoding
451 first character set to which conversion from local encoding
452 (``$HGENCODING``, ``ui.fallbackencoding``) succeeds. If correct
452 (``$HGENCODING``, ``ui.fallbackencoding``) succeeds. If correct
453 conversion fails, the text in question is sent as is. Defaults to
453 conversion fails, the text in question is sent as is. Defaults to
454 empty (explicit) list.
454 empty (explicit) list.
455
455
456 Order of outgoing email character sets:
456 Order of outgoing email character sets:
457
457
458 1. ``us-ascii``: always first, regardless of settings
458 1. ``us-ascii``: always first, regardless of settings
459 2. ``email.charsets``: in order given by user
459 2. ``email.charsets``: in order given by user
460 3. ``ui.fallbackencoding``: if not in email.charsets
460 3. ``ui.fallbackencoding``: if not in email.charsets
461 4. ``$HGENCODING``: if not in email.charsets
461 4. ``$HGENCODING``: if not in email.charsets
462 5. ``utf-8``: always last, regardless of settings
462 5. ``utf-8``: always last, regardless of settings
463
463
464 Email example::
464 Email example::
465
465
466 [email]
466 [email]
467 from = Joseph User <joe.user@example.com>
467 from = Joseph User <joe.user@example.com>
468 method = /usr/sbin/sendmail
468 method = /usr/sbin/sendmail
469 # charsets for western Europeans
469 # charsets for western Europeans
470 # us-ascii, utf-8 omitted, as they are tried first and last
470 # us-ascii, utf-8 omitted, as they are tried first and last
471 charsets = iso-8859-1, iso-8859-15, windows-1252
471 charsets = iso-8859-1, iso-8859-15, windows-1252
472
472
473
473
474 ``extensions``
474 ``extensions``
475 --------------
475 --------------
476
476
477 Mercurial has an extension mechanism for adding new features. To
477 Mercurial has an extension mechanism for adding new features. To
478 enable an extension, create an entry for it in this section.
478 enable an extension, create an entry for it in this section.
479
479
480 If you know that the extension is already in Python's search path,
480 If you know that the extension is already in Python's search path,
481 you can give the name of the module, followed by ``=``, with nothing
481 you can give the name of the module, followed by ``=``, with nothing
482 after the ``=``.
482 after the ``=``.
483
483
484 Otherwise, give a name that you choose, followed by ``=``, followed by
484 Otherwise, give a name that you choose, followed by ``=``, followed by
485 the path to the ``.py`` file (including the file name extension) that
485 the path to the ``.py`` file (including the file name extension) that
486 defines the extension.
486 defines the extension.
487
487
488 To explicitly disable an extension that is enabled in an hgrc of
488 To explicitly disable an extension that is enabled in an hgrc of
489 broader scope, prepend its path with ``!``, as in ``foo = !/ext/path``
489 broader scope, prepend its path with ``!``, as in ``foo = !/ext/path``
490 or ``foo = !`` when path is not supplied.
490 or ``foo = !`` when path is not supplied.
491
491
492 Example for ``~/.hgrc``::
492 Example for ``~/.hgrc``::
493
493
494 [extensions]
494 [extensions]
495 # (the mq extension will get loaded from Mercurial's path)
495 # (the mq extension will get loaded from Mercurial's path)
496 mq =
496 mq =
497 # (this extension will get loaded from the file specified)
497 # (this extension will get loaded from the file specified)
498 myfeature = ~/.hgext/myfeature.py
498 myfeature = ~/.hgext/myfeature.py
499
499
500
500
501 ``format``
501 ``format``
502 ----------
502 ----------
503
503
504 ``usestore``
504 ``usestore``
505 Enable or disable the "store" repository format which improves
505 Enable or disable the "store" repository format which improves
506 compatibility with systems that fold case or otherwise mangle
506 compatibility with systems that fold case or otherwise mangle
507 filenames. Enabled by default. Disabling this option will allow
507 filenames. Enabled by default. Disabling this option will allow
508 you to store longer filenames in some situations at the expense of
508 you to store longer filenames in some situations at the expense of
509 compatibility and ensures that the on-disk format of newly created
509 compatibility and ensures that the on-disk format of newly created
510 repositories will be compatible with Mercurial before version 0.9.4.
510 repositories will be compatible with Mercurial before version 0.9.4.
511
511
512 ``usefncache``
512 ``usefncache``
513 Enable or disable the "fncache" repository format which enhances
513 Enable or disable the "fncache" repository format which enhances
514 the "store" repository format (which has to be enabled to use
514 the "store" repository format (which has to be enabled to use
515 fncache) to allow longer filenames and avoids using Windows
515 fncache) to allow longer filenames and avoids using Windows
516 reserved names, e.g. "nul". Enabled by default. Disabling this
516 reserved names, e.g. "nul". Enabled by default. Disabling this
517 option ensures that the on-disk format of newly created
517 option ensures that the on-disk format of newly created
518 repositories will be compatible with Mercurial before version 1.1.
518 repositories will be compatible with Mercurial before version 1.1.
519
519
520 ``dotencode``
520 ``dotencode``
521 Enable or disable the "dotencode" repository format which enhances
521 Enable or disable the "dotencode" repository format which enhances
522 the "fncache" repository format (which has to be enabled to use
522 the "fncache" repository format (which has to be enabled to use
523 dotencode) to avoid issues with filenames starting with ._ on
523 dotencode) to avoid issues with filenames starting with ._ on
524 Mac OS X and spaces on Windows. Enabled by default. Disabling this
524 Mac OS X and spaces on Windows. Enabled by default. Disabling this
525 option ensures that the on-disk format of newly created
525 option ensures that the on-disk format of newly created
526 repositories will be compatible with Mercurial before version 1.7.
526 repositories will be compatible with Mercurial before version 1.7.
527
527
528 ``graph``
528 ``graph``
529 ---------
529 ---------
530
530
531 Web graph view configuration. This section let you change graph
531 Web graph view configuration. This section let you change graph
532 elements display properties by branches, for instance to make the
532 elements display properties by branches, for instance to make the
533 ``default`` branch stand out.
533 ``default`` branch stand out.
534
534
535 Each line has the following format::
535 Each line has the following format::
536
536
537 <branch>.<argument> = <value>
537 <branch>.<argument> = <value>
538
538
539 where ``<branch>`` is the name of the branch being
539 where ``<branch>`` is the name of the branch being
540 customized. Example::
540 customized. Example::
541
541
542 [graph]
542 [graph]
543 # 2px width
543 # 2px width
544 default.width = 2
544 default.width = 2
545 # red color
545 # red color
546 default.color = FF0000
546 default.color = FF0000
547
547
548 Supported arguments:
548 Supported arguments:
549
549
550 ``width``
550 ``width``
551 Set branch edges width in pixels.
551 Set branch edges width in pixels.
552
552
553 ``color``
553 ``color``
554 Set branch edges color in hexadecimal RGB notation.
554 Set branch edges color in hexadecimal RGB notation.
555
555
556 ``hooks``
556 ``hooks``
557 ---------
557 ---------
558
558
559 Commands or Python functions that get automatically executed by
559 Commands or Python functions that get automatically executed by
560 various actions such as starting or finishing a commit. Multiple
560 various actions such as starting or finishing a commit. Multiple
561 hooks can be run for the same action by appending a suffix to the
561 hooks can be run for the same action by appending a suffix to the
562 action. Overriding a site-wide hook can be done by changing its
562 action. Overriding a site-wide hook can be done by changing its
563 value or setting it to an empty string. Hooks can be prioritized
563 value or setting it to an empty string. Hooks can be prioritized
564 by adding a prefix of ``priority`` to the hook name on a new line
564 by adding a prefix of ``priority`` to the hook name on a new line
565 and setting the priority. The default priority is 0 if
565 and setting the priority. The default priority is 0 if
566 not specified.
566 not specified.
567
567
568 Example ``.hg/hgrc``::
568 Example ``.hg/hgrc``::
569
569
570 [hooks]
570 [hooks]
571 # update working directory after adding changesets
571 # update working directory after adding changesets
572 changegroup.update = hg update
572 changegroup.update = hg update
573 # do not use the site-wide hook
573 # do not use the site-wide hook
574 incoming =
574 incoming =
575 incoming.email = /my/email/hook
575 incoming.email = /my/email/hook
576 incoming.autobuild = /my/build/hook
576 incoming.autobuild = /my/build/hook
577 # force autobuild hook to run before other incoming hooks
577 # force autobuild hook to run before other incoming hooks
578 priority.incoming.autobuild = 1
578 priority.incoming.autobuild = 1
579
579
580 Most hooks are run with environment variables set that give useful
580 Most hooks are run with environment variables set that give useful
581 additional information. For each hook below, the environment
581 additional information. For each hook below, the environment
582 variables it is passed are listed with names of the form ``$HG_foo``.
582 variables it is passed are listed with names of the form ``$HG_foo``.
583
583
584 ``changegroup``
584 ``changegroup``
585 Run after a changegroup has been added via push, pull or unbundle.
585 Run after a changegroup has been added via push, pull or unbundle.
586 ID of the first new changeset is in ``$HG_NODE``. URL from which
586 ID of the first new changeset is in ``$HG_NODE``. URL from which
587 changes came is in ``$HG_URL``.
587 changes came is in ``$HG_URL``.
588
588
589 ``commit``
589 ``commit``
590 Run after a changeset has been created in the local repository. ID
590 Run after a changeset has been created in the local repository. ID
591 of the newly created changeset is in ``$HG_NODE``. Parent changeset
591 of the newly created changeset is in ``$HG_NODE``. Parent changeset
592 IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
592 IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
593
593
594 ``incoming``
594 ``incoming``
595 Run after a changeset has been pulled, pushed, or unbundled into
595 Run after a changeset has been pulled, pushed, or unbundled into
596 the local repository. The ID of the newly arrived changeset is in
596 the local repository. The ID of the newly arrived changeset is in
597 ``$HG_NODE``. URL that was source of changes came is in ``$HG_URL``.
597 ``$HG_NODE``. URL that was source of changes came is in ``$HG_URL``.
598
598
599 ``outgoing``
599 ``outgoing``
600 Run after sending changes from local repository to another. ID of
600 Run after sending changes from local repository to another. ID of
601 first changeset sent is in ``$HG_NODE``. Source of operation is in
601 first changeset sent is in ``$HG_NODE``. Source of operation is in
602 ``$HG_SOURCE``; see "preoutgoing" hook for description.
602 ``$HG_SOURCE``; see "preoutgoing" hook for description.
603
603
604 ``post-<command>``
604 ``post-<command>``
605 Run after successful invocations of the associated command. The
605 Run after successful invocations of the associated command. The
606 contents of the command line are passed as ``$HG_ARGS`` and the result
606 contents of the command line are passed as ``$HG_ARGS`` and the result
607 code in ``$HG_RESULT``. Parsed command line arguments are passed as
607 code in ``$HG_RESULT``. Parsed command line arguments are passed as
608 ``$HG_PATS`` and ``$HG_OPTS``. These contain string representations of
608 ``$HG_PATS`` and ``$HG_OPTS``. These contain string representations of
609 the python data internally passed to <command>. ``$HG_OPTS`` is a
609 the python data internally passed to <command>. ``$HG_OPTS`` is a
610 dictionary of options (with unspecified options set to their defaults).
610 dictionary of options (with unspecified options set to their defaults).
611 ``$HG_PATS`` is a list of arguments. Hook failure is ignored.
611 ``$HG_PATS`` is a list of arguments. Hook failure is ignored.
612
612
613 ``pre-<command>``
613 ``pre-<command>``
614 Run before executing the associated command. The contents of the
614 Run before executing the associated command. The contents of the
615 command line are passed as ``$HG_ARGS``. Parsed command line arguments
615 command line are passed as ``$HG_ARGS``. Parsed command line arguments
616 are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain string
616 are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain string
617 representations of the data internally passed to <command>. ``$HG_OPTS``
617 representations of the data internally passed to <command>. ``$HG_OPTS``
618 is a dictionary of options (with unspecified options set to their
618 is a dictionary of options (with unspecified options set to their
619 defaults). ``$HG_PATS`` is a list of arguments. If the hook returns
619 defaults). ``$HG_PATS`` is a list of arguments. If the hook returns
620 failure, the command doesn't execute and Mercurial returns the failure
620 failure, the command doesn't execute and Mercurial returns the failure
621 code.
621 code.
622
622
623 ``prechangegroup``
623 ``prechangegroup``
624 Run before a changegroup is added via push, pull or unbundle. Exit
624 Run before a changegroup is added via push, pull or unbundle. Exit
625 status 0 allows the changegroup to proceed. Non-zero status will
625 status 0 allows the changegroup to proceed. Non-zero status will
626 cause the push, pull or unbundle to fail. URL from which changes
626 cause the push, pull or unbundle to fail. URL from which changes
627 will come is in ``$HG_URL``.
627 will come is in ``$HG_URL``.
628
628
629 ``precommit``
629 ``precommit``
630 Run before starting a local commit. Exit status 0 allows the
630 Run before starting a local commit. Exit status 0 allows the
631 commit to proceed. Non-zero status will cause the commit to fail.
631 commit to proceed. Non-zero status will cause the commit to fail.
632 Parent changeset IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
632 Parent changeset IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
633
633
634 ``prelistkeys``
634 ``prelistkeys``
635 Run before listing pushkeys (like bookmarks) in the
635 Run before listing pushkeys (like bookmarks) in the
636 repository. Non-zero status will cause failure. The key namespace is
636 repository. Non-zero status will cause failure. The key namespace is
637 in ``$HG_NAMESPACE``.
637 in ``$HG_NAMESPACE``.
638
638
639 ``preoutgoing``
639 ``preoutgoing``
640 Run before collecting changes to send from the local repository to
640 Run before collecting changes to send from the local repository to
641 another. Non-zero status will cause failure. This lets you prevent
641 another. Non-zero status will cause failure. This lets you prevent
642 pull over HTTP or SSH. Also prevents against local pull, push
642 pull over HTTP or SSH. Also prevents against local pull, push
643 (outbound) or bundle commands, but not effective, since you can
643 (outbound) or bundle commands, but not effective, since you can
644 just copy files instead then. Source of operation is in
644 just copy files instead then. Source of operation is in
645 ``$HG_SOURCE``. If "serve", operation is happening on behalf of remote
645 ``$HG_SOURCE``. If "serve", operation is happening on behalf of remote
646 SSH or HTTP repository. If "push", "pull" or "bundle", operation
646 SSH or HTTP repository. If "push", "pull" or "bundle", operation
647 is happening on behalf of repository on same system.
647 is happening on behalf of repository on same system.
648
648
649 ``prepushkey``
649 ``prepushkey``
650 Run before a pushkey (like a bookmark) is added to the
650 Run before a pushkey (like a bookmark) is added to the
651 repository. Non-zero status will cause the key to be rejected. The
651 repository. Non-zero status will cause the key to be rejected. The
652 key namespace is in ``$HG_NAMESPACE``, the key is in ``$HG_KEY``,
652 key namespace is in ``$HG_NAMESPACE``, the key is in ``$HG_KEY``,
653 the old value (if any) is in ``$HG_OLD``, and the new value is in
653 the old value (if any) is in ``$HG_OLD``, and the new value is in
654 ``$HG_NEW``.
654 ``$HG_NEW``.
655
655
656 ``pretag``
656 ``pretag``
657 Run before creating a tag. Exit status 0 allows the tag to be
657 Run before creating a tag. Exit status 0 allows the tag to be
658 created. Non-zero status will cause the tag to fail. ID of
658 created. Non-zero status will cause the tag to fail. ID of
659 changeset to tag is in ``$HG_NODE``. Name of tag is in ``$HG_TAG``. Tag is
659 changeset to tag is in ``$HG_NODE``. Name of tag is in ``$HG_TAG``. Tag is
660 local if ``$HG_LOCAL=1``, in repository if ``$HG_LOCAL=0``.
660 local if ``$HG_LOCAL=1``, in repository if ``$HG_LOCAL=0``.
661
661
662 ``pretxnchangegroup``
662 ``pretxnchangegroup``
663 Run after a changegroup has been added via push, pull or unbundle,
663 Run after a changegroup has been added via push, pull or unbundle,
664 but before the transaction has been committed. Changegroup is
664 but before the transaction has been committed. Changegroup is
665 visible to hook program. This lets you validate incoming changes
665 visible to hook program. This lets you validate incoming changes
666 before accepting them. Passed the ID of the first new changeset in
666 before accepting them. Passed the ID of the first new changeset in
667 ``$HG_NODE``. Exit status 0 allows the transaction to commit. Non-zero
667 ``$HG_NODE``. Exit status 0 allows the transaction to commit. Non-zero
668 status will cause the transaction to be rolled back and the push,
668 status will cause the transaction to be rolled back and the push,
669 pull or unbundle will fail. URL that was source of changes is in
669 pull or unbundle will fail. URL that was source of changes is in
670 ``$HG_URL``.
670 ``$HG_URL``.
671
671
672 ``pretxncommit``
672 ``pretxncommit``
673 Run after a changeset has been created but the transaction not yet
673 Run after a changeset has been created but the transaction not yet
674 committed. Changeset is visible to hook program. This lets you
674 committed. Changeset is visible to hook program. This lets you
675 validate commit message and changes. Exit status 0 allows the
675 validate commit message and changes. Exit status 0 allows the
676 commit to proceed. Non-zero status will cause the transaction to
676 commit to proceed. Non-zero status will cause the transaction to
677 be rolled back. ID of changeset is in ``$HG_NODE``. Parent changeset
677 be rolled back. ID of changeset is in ``$HG_NODE``. Parent changeset
678 IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
678 IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
679
679
680 ``preupdate``
680 ``preupdate``
681 Run before updating the working directory. Exit status 0 allows
681 Run before updating the working directory. Exit status 0 allows
682 the update to proceed. Non-zero status will prevent the update.
682 the update to proceed. Non-zero status will prevent the update.
683 Changeset ID of first new parent is in ``$HG_PARENT1``. If merge, ID
683 Changeset ID of first new parent is in ``$HG_PARENT1``. If merge, ID
684 of second new parent is in ``$HG_PARENT2``.
684 of second new parent is in ``$HG_PARENT2``.
685
685
686 ``listkeys``
686 ``listkeys``
687 Run after listing pushkeys (like bookmarks) in the repository. The
687 Run after listing pushkeys (like bookmarks) in the repository. The
688 key namespace is in ``$HG_NAMESPACE``. ``$HG_VALUES`` is a
688 key namespace is in ``$HG_NAMESPACE``. ``$HG_VALUES`` is a
689 dictionary containing the keys and values.
689 dictionary containing the keys and values.
690
690
691 ``pushkey``
691 ``pushkey``
692 Run after a pushkey (like a bookmark) is added to the
692 Run after a pushkey (like a bookmark) is added to the
693 repository. The key namespace is in ``$HG_NAMESPACE``, the key is in
693 repository. The key namespace is in ``$HG_NAMESPACE``, the key is in
694 ``$HG_KEY``, the old value (if any) is in ``$HG_OLD``, and the new
694 ``$HG_KEY``, the old value (if any) is in ``$HG_OLD``, and the new
695 value is in ``$HG_NEW``.
695 value is in ``$HG_NEW``.
696
696
697 ``tag``
697 ``tag``
698 Run after a tag is created. ID of tagged changeset is in ``$HG_NODE``.
698 Run after a tag is created. ID of tagged changeset is in ``$HG_NODE``.
699 Name of tag is in ``$HG_TAG``. Tag is local if ``$HG_LOCAL=1``, in
699 Name of tag is in ``$HG_TAG``. Tag is local if ``$HG_LOCAL=1``, in
700 repository if ``$HG_LOCAL=0``.
700 repository if ``$HG_LOCAL=0``.
701
701
702 ``update``
702 ``update``
703 Run after updating the working directory. Changeset ID of first
703 Run after updating the working directory. Changeset ID of first
704 new parent is in ``$HG_PARENT1``. If merge, ID of second new parent is
704 new parent is in ``$HG_PARENT1``. If merge, ID of second new parent is
705 in ``$HG_PARENT2``. If the update succeeded, ``$HG_ERROR=0``. If the
705 in ``$HG_PARENT2``. If the update succeeded, ``$HG_ERROR=0``. If the
706 update failed (e.g. because conflicts not resolved), ``$HG_ERROR=1``.
706 update failed (e.g. because conflicts not resolved), ``$HG_ERROR=1``.
707
707
708 .. note:: It is generally better to use standard hooks rather than the
708 .. note:: It is generally better to use standard hooks rather than the
709 generic pre- and post- command hooks as they are guaranteed to be
709 generic pre- and post- command hooks as they are guaranteed to be
710 called in the appropriate contexts for influencing transactions.
710 called in the appropriate contexts for influencing transactions.
711 Also, hooks like "commit" will be called in all contexts that
711 Also, hooks like "commit" will be called in all contexts that
712 generate a commit (e.g. tag) and not just the commit command.
712 generate a commit (e.g. tag) and not just the commit command.
713
713
714 .. note:: Environment variables with empty values may not be passed to
714 .. note:: Environment variables with empty values may not be passed to
715 hooks on platforms such as Windows. As an example, ``$HG_PARENT2``
715 hooks on platforms such as Windows. As an example, ``$HG_PARENT2``
716 will have an empty value under Unix-like platforms for non-merge
716 will have an empty value under Unix-like platforms for non-merge
717 changesets, while it will not be available at all under Windows.
717 changesets, while it will not be available at all under Windows.
718
718
719 The syntax for Python hooks is as follows::
719 The syntax for Python hooks is as follows::
720
720
721 hookname = python:modulename.submodule.callable
721 hookname = python:modulename.submodule.callable
722 hookname = python:/path/to/python/module.py:callable
722 hookname = python:/path/to/python/module.py:callable
723
723
724 Python hooks are run within the Mercurial process. Each hook is
724 Python hooks are run within the Mercurial process. Each hook is
725 called with at least three keyword arguments: a ui object (keyword
725 called with at least three keyword arguments: a ui object (keyword
726 ``ui``), a repository object (keyword ``repo``), and a ``hooktype``
726 ``ui``), a repository object (keyword ``repo``), and a ``hooktype``
727 keyword that tells what kind of hook is used. Arguments listed as
727 keyword that tells what kind of hook is used. Arguments listed as
728 environment variables above are passed as keyword arguments, with no
728 environment variables above are passed as keyword arguments, with no
729 ``HG_`` prefix, and names in lower case.
729 ``HG_`` prefix, and names in lower case.
730
730
731 If a Python hook returns a "true" value or raises an exception, this
731 If a Python hook returns a "true" value or raises an exception, this
732 is treated as a failure.
732 is treated as a failure.
733
733
734
734
735 ``hostfingerprints``
735 ``hostfingerprints``
736 --------------------
736 --------------------
737
737
738 Fingerprints of the certificates of known HTTPS servers.
738 Fingerprints of the certificates of known HTTPS servers.
739 A HTTPS connection to a server with a fingerprint configured here will
739 A HTTPS connection to a server with a fingerprint configured here will
740 only succeed if the servers certificate matches the fingerprint.
740 only succeed if the servers certificate matches the fingerprint.
741 This is very similar to how ssh known hosts works.
741 This is very similar to how ssh known hosts works.
742 The fingerprint is the SHA-1 hash value of the DER encoded certificate.
742 The fingerprint is the SHA-1 hash value of the DER encoded certificate.
743 The CA chain and web.cacerts is not used for servers with a fingerprint.
743 The CA chain and web.cacerts is not used for servers with a fingerprint.
744
744
745 For example::
745 For example::
746
746
747 [hostfingerprints]
747 [hostfingerprints]
748 hg.intevation.org = 38:76:52:7c:87:26:9a:8f:4a:f8:d3:de:08:45:3b:ea:d6:4b:ee:cc
748 hg.intevation.org = 38:76:52:7c:87:26:9a:8f:4a:f8:d3:de:08:45:3b:ea:d6:4b:ee:cc
749
749
750 This feature is only supported when using Python 2.6 or later.
750 This feature is only supported when using Python 2.6 or later.
751
751
752
752
753 ``http_proxy``
753 ``http_proxy``
754 --------------
754 --------------
755
755
756 Used to access web-based Mercurial repositories through a HTTP
756 Used to access web-based Mercurial repositories through a HTTP
757 proxy.
757 proxy.
758
758
759 ``host``
759 ``host``
760 Host name and (optional) port of the proxy server, for example
760 Host name and (optional) port of the proxy server, for example
761 "myproxy:8000".
761 "myproxy:8000".
762
762
763 ``no``
763 ``no``
764 Optional. Comma-separated list of host names that should bypass
764 Optional. Comma-separated list of host names that should bypass
765 the proxy.
765 the proxy.
766
766
767 ``passwd``
767 ``passwd``
768 Optional. Password to authenticate with at the proxy server.
768 Optional. Password to authenticate with at the proxy server.
769
769
770 ``user``
770 ``user``
771 Optional. User name to authenticate with at the proxy server.
771 Optional. User name to authenticate with at the proxy server.
772
772
773 ``always``
773 ``always``
774 Optional. Always use the proxy, even for localhost and any entries
774 Optional. Always use the proxy, even for localhost and any entries
775 in ``http_proxy.no``. True or False. Default: False.
775 in ``http_proxy.no``. True or False. Default: False.
776
776
777 ``merge-patterns``
777 ``merge-patterns``
778 ------------------
778 ------------------
779
779
780 This section specifies merge tools to associate with particular file
780 This section specifies merge tools to associate with particular file
781 patterns. Tools matched here will take precedence over the default
781 patterns. Tools matched here will take precedence over the default
782 merge tool. Patterns are globs by default, rooted at the repository
782 merge tool. Patterns are globs by default, rooted at the repository
783 root.
783 root.
784
784
785 Example::
785 Example::
786
786
787 [merge-patterns]
787 [merge-patterns]
788 **.c = kdiff3
788 **.c = kdiff3
789 **.jpg = myimgmerge
789 **.jpg = myimgmerge
790
790
791 ``merge-tools``
791 ``merge-tools``
792 ---------------
792 ---------------
793
793
794 This section configures external merge tools to use for file-level
794 This section configures external merge tools to use for file-level
795 merges.
795 merges.
796
796
797 Example ``~/.hgrc``::
797 Example ``~/.hgrc``::
798
798
799 [merge-tools]
799 [merge-tools]
800 # Override stock tool location
800 # Override stock tool location
801 kdiff3.executable = ~/bin/kdiff3
801 kdiff3.executable = ~/bin/kdiff3
802 # Specify command line
802 # Specify command line
803 kdiff3.args = $base $local $other -o $output
803 kdiff3.args = $base $local $other -o $output
804 # Give higher priority
804 # Give higher priority
805 kdiff3.priority = 1
805 kdiff3.priority = 1
806
806
807 # Define new tool
807 # Define new tool
808 myHtmlTool.args = -m $local $other $base $output
808 myHtmlTool.args = -m $local $other $base $output
809 myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
809 myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
810 myHtmlTool.priority = 1
810 myHtmlTool.priority = 1
811
811
812 Supported arguments:
812 Supported arguments:
813
813
814 ``priority``
814 ``priority``
815 The priority in which to evaluate this tool.
815 The priority in which to evaluate this tool.
816 Default: 0.
816 Default: 0.
817
817
818 ``executable``
818 ``executable``
819 Either just the name of the executable or its pathname. On Windows,
819 Either just the name of the executable or its pathname. On Windows,
820 the path can use environment variables with ${ProgramFiles} syntax.
820 the path can use environment variables with ${ProgramFiles} syntax.
821 Default: the tool name.
821 Default: the tool name.
822
822
823 ``args``
823 ``args``
824 The arguments to pass to the tool executable. You can refer to the
824 The arguments to pass to the tool executable. You can refer to the
825 files being merged as well as the output file through these
825 files being merged as well as the output file through these
826 variables: ``$base``, ``$local``, ``$other``, ``$output``.
826 variables: ``$base``, ``$local``, ``$other``, ``$output``.
827 Default: ``$local $base $other``
827 Default: ``$local $base $other``
828
828
829 ``premerge``
829 ``premerge``
830 Attempt to run internal non-interactive 3-way merge tool before
830 Attempt to run internal non-interactive 3-way merge tool before
831 launching external tool. Options are ``true``, ``false``, or ``keep``
831 launching external tool. Options are ``true``, ``false``, or ``keep``
832 to leave markers in the file if the premerge fails.
832 to leave markers in the file if the premerge fails.
833 Default: True
833 Default: True
834
834
835 ``binary``
835 ``binary``
836 This tool can merge binary files. Defaults to False, unless tool
836 This tool can merge binary files. Defaults to False, unless tool
837 was selected by file pattern match.
837 was selected by file pattern match.
838
838
839 ``symlink``
839 ``symlink``
840 This tool can merge symlinks. Defaults to False, even if tool was
840 This tool can merge symlinks. Defaults to False, even if tool was
841 selected by file pattern match.
841 selected by file pattern match.
842
842
843 ``check``
843 ``check``
844 A list of merge success-checking options:
844 A list of merge success-checking options:
845
845
846 ``changed``
846 ``changed``
847 Ask whether merge was successful when the merged file shows no changes.
847 Ask whether merge was successful when the merged file shows no changes.
848 ``conflicts``
848 ``conflicts``
849 Check whether there are conflicts even though the tool reported success.
849 Check whether there are conflicts even though the tool reported success.
850 ``prompt``
850 ``prompt``
851 Always prompt for merge success, regardless of success reported by tool.
851 Always prompt for merge success, regardless of success reported by tool.
852
852
853 ``fixeol``
853 ``fixeol``
854 Attempt to fix up EOL changes caused by the merge tool.
854 Attempt to fix up EOL changes caused by the merge tool.
855 Default: False
855 Default: False
856
856
857 ``gui``
857 ``gui``
858 This tool requires a graphical interface to run. Default: False
858 This tool requires a graphical interface to run. Default: False
859
859
860 ``regkey``
860 ``regkey``
861 Windows registry key which describes install location of this
861 Windows registry key which describes install location of this
862 tool. Mercurial will search for this key first under
862 tool. Mercurial will search for this key first under
863 ``HKEY_CURRENT_USER`` and then under ``HKEY_LOCAL_MACHINE``.
863 ``HKEY_CURRENT_USER`` and then under ``HKEY_LOCAL_MACHINE``.
864 Default: None
864 Default: None
865
865
866 ``regkeyalt``
866 ``regkeyalt``
867 An alternate Windows registry key to try if the first key is not
867 An alternate Windows registry key to try if the first key is not
868 found. The alternate key uses the same ``regname`` and ``regappend``
868 found. The alternate key uses the same ``regname`` and ``regappend``
869 semantics of the primary key. The most common use for this key
869 semantics of the primary key. The most common use for this key
870 is to search for 32bit applications on 64bit operating systems.
870 is to search for 32bit applications on 64bit operating systems.
871 Default: None
871 Default: None
872
872
873 ``regname``
873 ``regname``
874 Name of value to read from specified registry key. Defaults to the
874 Name of value to read from specified registry key. Defaults to the
875 unnamed (default) value.
875 unnamed (default) value.
876
876
877 ``regappend``
877 ``regappend``
878 String to append to the value read from the registry, typically
878 String to append to the value read from the registry, typically
879 the executable name of the tool.
879 the executable name of the tool.
880 Default: None
880 Default: None
881
881
882
882
883 ``patch``
883 ``patch``
884 ---------
884 ---------
885
885
886 Settings used when applying patches, for instance through the 'import'
886 Settings used when applying patches, for instance through the 'import'
887 command or with Mercurial Queues extension.
887 command or with Mercurial Queues extension.
888
888
889 ``eol``
889 ``eol``
890 When set to 'strict' patch content and patched files end of lines
890 When set to 'strict' patch content and patched files end of lines
891 are preserved. When set to ``lf`` or ``crlf``, both files end of
891 are preserved. When set to ``lf`` or ``crlf``, both files end of
892 lines are ignored when patching and the result line endings are
892 lines are ignored when patching and the result line endings are
893 normalized to either LF (Unix) or CRLF (Windows). When set to
893 normalized to either LF (Unix) or CRLF (Windows). When set to
894 ``auto``, end of lines are again ignored while patching but line
894 ``auto``, end of lines are again ignored while patching but line
895 endings in patched files are normalized to their original setting
895 endings in patched files are normalized to their original setting
896 on a per-file basis. If target file does not exist or has no end
896 on a per-file basis. If target file does not exist or has no end
897 of line, patch line endings are preserved.
897 of line, patch line endings are preserved.
898 Default: strict.
898 Default: strict.
899
899
900
900
901 ``paths``
901 ``paths``
902 ---------
902 ---------
903
903
904 Assigns symbolic names to repositories. The left side is the
904 Assigns symbolic names to repositories. The left side is the
905 symbolic name, and the right gives the directory or URL that is the
905 symbolic name, and the right gives the directory or URL that is the
906 location of the repository. Default paths can be declared by setting
906 location of the repository. Default paths can be declared by setting
907 the following entries.
907 the following entries.
908
908
909 ``default``
909 ``default``
910 Directory or URL to use when pulling if no source is specified.
910 Directory or URL to use when pulling if no source is specified.
911 Default is set to repository from which the current repository was
911 Default is set to repository from which the current repository was
912 cloned.
912 cloned.
913
913
914 ``default-push``
914 ``default-push``
915 Optional. Directory or URL to use when pushing if no destination
915 Optional. Directory or URL to use when pushing if no destination
916 is specified.
916 is specified.
917
917
918 Custom paths can be defined by assigning the path to a name that later can be
918 Custom paths can be defined by assigning the path to a name that later can be
919 used from the command line. Example::
919 used from the command line. Example::
920
920
921 [paths]
921 [paths]
922 my_path = http://example.com/path
922 my_path = http://example.com/path
923
923
924 To push to the path defined in ``my_path`` run the command::
924 To push to the path defined in ``my_path`` run the command::
925
925
926 hg push my_path
926 hg push my_path
927
927
928
928
929 ``phases``
929 ``phases``
930 ----------
930 ----------
931
931
932 Specifies default handling of phases. See :hg:`help phases` for more
932 Specifies default handling of phases. See :hg:`help phases` for more
933 information about working with phases.
933 information about working with phases.
934
934
935 ``publish``
935 ``publish``
936 Controls draft phase behavior when working as a server. When true,
936 Controls draft phase behavior when working as a server. When true,
937 pushed changesets are set to public in both client and server and
937 pushed changesets are set to public in both client and server and
938 pulled or cloned changesets are set to public in the client.
938 pulled or cloned changesets are set to public in the client.
939 Default: True
939 Default: True
940
940
941 ``new-commit``
941 ``new-commit``
942 Phase of newly-created commits.
942 Phase of newly-created commits.
943 Default: draft
943 Default: draft
944
944
945 ``profiling``
945 ``profiling``
946 -------------
946 -------------
947
947
948 Specifies profiling type, format, and file output. Two profilers are
948 Specifies profiling type, format, and file output. Two profilers are
949 supported: an instrumenting profiler (named ``ls``), and a sampling
949 supported: an instrumenting profiler (named ``ls``), and a sampling
950 profiler (named ``stat``).
950 profiler (named ``stat``).
951
951
952 In this section description, 'profiling data' stands for the raw data
952 In this section description, 'profiling data' stands for the raw data
953 collected during profiling, while 'profiling report' stands for a
953 collected during profiling, while 'profiling report' stands for a
954 statistical text report generated from the profiling data. The
954 statistical text report generated from the profiling data. The
955 profiling is done using lsprof.
955 profiling is done using lsprof.
956
956
957 ``type``
957 ``type``
958 The type of profiler to use.
958 The type of profiler to use.
959 Default: ls.
959 Default: ls.
960
960
961 ``ls``
961 ``ls``
962 Use Python's built-in instrumenting profiler. This profiler
962 Use Python's built-in instrumenting profiler. This profiler
963 works on all platforms, but each line number it reports is the
963 works on all platforms, but each line number it reports is the
964 first line of a function. This restriction makes it difficult to
964 first line of a function. This restriction makes it difficult to
965 identify the expensive parts of a non-trivial function.
965 identify the expensive parts of a non-trivial function.
966 ``stat``
966 ``stat``
967 Use a third-party statistical profiler, statprof. This profiler
967 Use a third-party statistical profiler, statprof. This profiler
968 currently runs only on Unix systems, and is most useful for
968 currently runs only on Unix systems, and is most useful for
969 profiling commands that run for longer than about 0.1 seconds.
969 profiling commands that run for longer than about 0.1 seconds.
970
970
971 ``format``
971 ``format``
972 Profiling format. Specific to the ``ls`` instrumenting profiler.
972 Profiling format. Specific to the ``ls`` instrumenting profiler.
973 Default: text.
973 Default: text.
974
974
975 ``text``
975 ``text``
976 Generate a profiling report. When saving to a file, it should be
976 Generate a profiling report. When saving to a file, it should be
977 noted that only the report is saved, and the profiling data is
977 noted that only the report is saved, and the profiling data is
978 not kept.
978 not kept.
979 ``kcachegrind``
979 ``kcachegrind``
980 Format profiling data for kcachegrind use: when saving to a
980 Format profiling data for kcachegrind use: when saving to a
981 file, the generated file can directly be loaded into
981 file, the generated file can directly be loaded into
982 kcachegrind.
982 kcachegrind.
983
983
984 ``frequency``
984 ``frequency``
985 Sampling frequency. Specific to the ``stat`` sampling profiler.
985 Sampling frequency. Specific to the ``stat`` sampling profiler.
986 Default: 1000.
986 Default: 1000.
987
987
988 ``output``
988 ``output``
989 File path where profiling data or report should be saved. If the
989 File path where profiling data or report should be saved. If the
990 file exists, it is replaced. Default: None, data is printed on
990 file exists, it is replaced. Default: None, data is printed on
991 stderr
991 stderr
992
992
993 ``sort``
993 ``sort``
994 Sort field. Specific to the ``ls`` instrumenting profiler.
994 Sort field. Specific to the ``ls`` instrumenting profiler.
995 One of ``callcount``, ``reccallcount``, ``totaltime`` and
995 One of ``callcount``, ``reccallcount``, ``totaltime`` and
996 ``inlinetime``.
996 ``inlinetime``.
997 Default: inlinetime.
997 Default: inlinetime.
998
998
999 ``limit``
999 ``limit``
1000 Number of lines to show. Specific to the ``ls`` instrumenting profiler.
1000 Number of lines to show. Specific to the ``ls`` instrumenting profiler.
1001 Default: 30.
1001 Default: 30.
1002
1002
1003 ``nested``
1003 ``nested``
1004 Show at most this number of lines of drill-down info after each main entry.
1004 Show at most this number of lines of drill-down info after each main entry.
1005 This can help explain the difference between Total and Inline.
1005 This can help explain the difference between Total and Inline.
1006 Specific to the ``ls`` instrumenting profiler.
1006 Specific to the ``ls`` instrumenting profiler.
1007 Default: 5.
1007 Default: 5.
1008
1008
1009 ``revsetalias``
1009 ``revsetalias``
1010 ---------------
1010 ---------------
1011
1011
1012 Alias definitions for revsets. See :hg:`help revsets` for details.
1012 Alias definitions for revsets. See :hg:`help revsets` for details.
1013
1013
1014 ``server``
1014 ``server``
1015 ----------
1015 ----------
1016
1016
1017 Controls generic server settings.
1017 Controls generic server settings.
1018
1018
1019 ``uncompressed``
1019 ``uncompressed``
1020 Whether to allow clients to clone a repository using the
1020 Whether to allow clients to clone a repository using the
1021 uncompressed streaming protocol. This transfers about 40% more
1021 uncompressed streaming protocol. This transfers about 40% more
1022 data than a regular clone, but uses less memory and CPU on both
1022 data than a regular clone, but uses less memory and CPU on both
1023 server and client. Over a LAN (100 Mbps or better) or a very fast
1023 server and client. Over a LAN (100 Mbps or better) or a very fast
1024 WAN, an uncompressed streaming clone is a lot faster (~10x) than a
1024 WAN, an uncompressed streaming clone is a lot faster (~10x) than a
1025 regular clone. Over most WAN connections (anything slower than
1025 regular clone. Over most WAN connections (anything slower than
1026 about 6 Mbps), uncompressed streaming is slower, because of the
1026 about 6 Mbps), uncompressed streaming is slower, because of the
1027 extra data transfer overhead. This mode will also temporarily hold
1027 extra data transfer overhead. This mode will also temporarily hold
1028 the write lock while determining what data to transfer.
1028 the write lock while determining what data to transfer.
1029 Default is True.
1029 Default is True.
1030
1030
1031 ``preferuncompressed``
1031 ``preferuncompressed``
1032 When set, clients will try to use the uncompressed streaming
1032 When set, clients will try to use the uncompressed streaming
1033 protocol. Default is False.
1033 protocol. Default is False.
1034
1034
1035 ``validate``
1035 ``validate``
1036 Whether to validate the completeness of pushed changesets by
1036 Whether to validate the completeness of pushed changesets by
1037 checking that all new file revisions specified in manifests are
1037 checking that all new file revisions specified in manifests are
1038 present. Default is False.
1038 present. Default is False.
1039
1039
1040 ``smtp``
1040 ``smtp``
1041 --------
1041 --------
1042
1042
1043 Configuration for extensions that need to send email messages.
1043 Configuration for extensions that need to send email messages.
1044
1044
1045 ``host``
1045 ``host``
1046 Host name of mail server, e.g. "mail.example.com".
1046 Host name of mail server, e.g. "mail.example.com".
1047
1047
1048 ``port``
1048 ``port``
1049 Optional. Port to connect to on mail server. Default: 25.
1049 Optional. Port to connect to on mail server. Default: 25.
1050
1050
1051 ``tls``
1051 ``tls``
1052 Optional. Method to enable TLS when connecting to mail server: starttls,
1052 Optional. Method to enable TLS when connecting to mail server: starttls,
1053 smtps or none. Default: none.
1053 smtps or none. Default: none.
1054
1054
1055 ``username``
1055 ``username``
1056 Optional. User name for authenticating with the SMTP server.
1056 Optional. User name for authenticating with the SMTP server.
1057 Default: none.
1057 Default: none.
1058
1058
1059 ``password``
1059 ``password``
1060 Optional. Password for authenticating with the SMTP server. If not
1060 Optional. Password for authenticating with the SMTP server. If not
1061 specified, interactive sessions will prompt the user for a
1061 specified, interactive sessions will prompt the user for a
1062 password; non-interactive sessions will fail. Default: none.
1062 password; non-interactive sessions will fail. Default: none.
1063
1063
1064 ``local_hostname``
1064 ``local_hostname``
1065 Optional. It's the hostname that the sender can use to identify
1065 Optional. It's the hostname that the sender can use to identify
1066 itself to the MTA.
1066 itself to the MTA.
1067
1067
1068
1068
1069 ``subpaths``
1069 ``subpaths``
1070 ------------
1070 ------------
1071
1071
1072 Subrepository source URLs can go stale if a remote server changes name
1072 Subrepository source URLs can go stale if a remote server changes name
1073 or becomes temporarily unavailable. This section lets you define
1073 or becomes temporarily unavailable. This section lets you define
1074 rewrite rules of the form::
1074 rewrite rules of the form::
1075
1075
1076 <pattern> = <replacement>
1076 <pattern> = <replacement>
1077
1077
1078 where ``pattern`` is a regular expression matching a subrepository
1078 where ``pattern`` is a regular expression matching a subrepository
1079 source URL and ``replacement`` is the replacement string used to
1079 source URL and ``replacement`` is the replacement string used to
1080 rewrite it. Groups can be matched in ``pattern`` and referenced in
1080 rewrite it. Groups can be matched in ``pattern`` and referenced in
1081 ``replacements``. For instance::
1081 ``replacements``. For instance::
1082
1082
1083 http://server/(.*)-hg/ = http://hg.server/\1/
1083 http://server/(.*)-hg/ = http://hg.server/\1/
1084
1084
1085 rewrites ``http://server/foo-hg/`` into ``http://hg.server/foo/``.
1085 rewrites ``http://server/foo-hg/`` into ``http://hg.server/foo/``.
1086
1086
1087 Relative subrepository paths are first made absolute, and the
1087 Relative subrepository paths are first made absolute, and the
1088 rewrite rules are then applied on the full (absolute) path. The rules
1088 rewrite rules are then applied on the full (absolute) path. The rules
1089 are applied in definition order.
1089 are applied in definition order.
1090
1090
1091 ``trusted``
1091 ``trusted``
1092 -----------
1092 -----------
1093
1093
1094 Mercurial will not use the settings in the
1094 Mercurial will not use the settings in the
1095 ``.hg/hgrc`` file from a repository if it doesn't belong to a trusted
1095 ``.hg/hgrc`` file from a repository if it doesn't belong to a trusted
1096 user or to a trusted group, as various hgrc features allow arbitrary
1096 user or to a trusted group, as various hgrc features allow arbitrary
1097 commands to be run. This issue is often encountered when configuring
1097 commands to be run. This issue is often encountered when configuring
1098 hooks or extensions for shared repositories or servers. However,
1098 hooks or extensions for shared repositories or servers. However,
1099 the web interface will use some safe settings from the ``[web]``
1099 the web interface will use some safe settings from the ``[web]``
1100 section.
1100 section.
1101
1101
1102 This section specifies what users and groups are trusted. The
1102 This section specifies what users and groups are trusted. The
1103 current user is always trusted. To trust everybody, list a user or a
1103 current user is always trusted. To trust everybody, list a user or a
1104 group with name ``*``. These settings must be placed in an
1104 group with name ``*``. These settings must be placed in an
1105 *already-trusted file* to take effect, such as ``$HOME/.hgrc`` of the
1105 *already-trusted file* to take effect, such as ``$HOME/.hgrc`` of the
1106 user or service running Mercurial.
1106 user or service running Mercurial.
1107
1107
1108 ``users``
1108 ``users``
1109 Comma-separated list of trusted users.
1109 Comma-separated list of trusted users.
1110
1110
1111 ``groups``
1111 ``groups``
1112 Comma-separated list of trusted groups.
1112 Comma-separated list of trusted groups.
1113
1113
1114
1114
1115 ``ui``
1115 ``ui``
1116 ------
1116 ------
1117
1117
1118 User interface controls.
1118 User interface controls.
1119
1119
1120 ``archivemeta``
1120 ``archivemeta``
1121 Whether to include the .hg_archival.txt file containing meta data
1121 Whether to include the .hg_archival.txt file containing meta data
1122 (hashes for the repository base and for tip) in archives created
1122 (hashes for the repository base and for tip) in archives created
1123 by the :hg:`archive` command or downloaded via hgweb.
1123 by the :hg:`archive` command or downloaded via hgweb.
1124 Default is True.
1124 Default is True.
1125
1125
1126 ``askusername``
1126 ``askusername``
1127 Whether to prompt for a username when committing. If True, and
1127 Whether to prompt for a username when committing. If True, and
1128 neither ``$HGUSER`` nor ``$EMAIL`` has been specified, then the user will
1128 neither ``$HGUSER`` nor ``$EMAIL`` has been specified, then the user will
1129 be prompted to enter a username. If no username is entered, the
1129 be prompted to enter a username. If no username is entered, the
1130 default ``USER@HOST`` is used instead.
1130 default ``USER@HOST`` is used instead.
1131 Default is False.
1131 Default is False.
1132
1132
1133 ``commitsubrepos``
1133 ``commitsubrepos``
1134 Whether to commit modified subrepositories when committing the
1134 Whether to commit modified subrepositories when committing the
1135 parent repository. If False and one subrepository has uncommitted
1135 parent repository. If False and one subrepository has uncommitted
1136 changes, abort the commit.
1136 changes, abort the commit.
1137 Default is False.
1137 Default is False.
1138
1138
1139 ``debug``
1139 ``debug``
1140 Print debugging information. True or False. Default is False.
1140 Print debugging information. True or False. Default is False.
1141
1141
1142 ``editor``
1142 ``editor``
1143 The editor to use during a commit. Default is ``$EDITOR`` or ``vi``.
1143 The editor to use during a commit. Default is ``$EDITOR`` or ``vi``.
1144
1144
1145 ``fallbackencoding``
1145 ``fallbackencoding``
1146 Encoding to try if it's not possible to decode the changelog using
1146 Encoding to try if it's not possible to decode the changelog using
1147 UTF-8. Default is ISO-8859-1.
1147 UTF-8. Default is ISO-8859-1.
1148
1148
1149 ``ignore``
1149 ``ignore``
1150 A file to read per-user ignore patterns from. This file should be
1150 A file to read per-user ignore patterns from. This file should be
1151 in the same format as a repository-wide .hgignore file. This
1151 in the same format as a repository-wide .hgignore file. This
1152 option supports hook syntax, so if you want to specify multiple
1152 option supports hook syntax, so if you want to specify multiple
1153 ignore files, you can do so by setting something like
1153 ignore files, you can do so by setting something like
1154 ``ignore.other = ~/.hgignore2``. For details of the ignore file
1154 ``ignore.other = ~/.hgignore2``. For details of the ignore file
1155 format, see the ``hgignore(5)`` man page.
1155 format, see the ``hgignore(5)`` man page.
1156
1156
1157 ``interactive``
1157 ``interactive``
1158 Allow to prompt the user. True or False. Default is True.
1158 Allow to prompt the user. True or False. Default is True.
1159
1159
1160 ``logtemplate``
1160 ``logtemplate``
1161 Template string for commands that print changesets.
1161 Template string for commands that print changesets.
1162
1162
1163 ``merge``
1163 ``merge``
1164 The conflict resolution program to use during a manual merge.
1164 The conflict resolution program to use during a manual merge.
1165 For more information on merge tools see :hg:`help merge-tools`.
1165 For more information on merge tools see :hg:`help merge-tools`.
1166 For configuring merge tools see the ``[merge-tools]`` section.
1166 For configuring merge tools see the ``[merge-tools]`` section.
1167
1167
1168 ``portablefilenames``
1168 ``portablefilenames``
1169 Check for portable filenames. Can be ``warn``, ``ignore`` or ``abort``.
1169 Check for portable filenames. Can be ``warn``, ``ignore`` or ``abort``.
1170 Default is ``warn``.
1170 Default is ``warn``.
1171 If set to ``warn`` (or ``true``), a warning message is printed on POSIX
1171 If set to ``warn`` (or ``true``), a warning message is printed on POSIX
1172 platforms, if a file with a non-portable filename is added (e.g. a file
1172 platforms, if a file with a non-portable filename is added (e.g. a file
1173 with a name that can't be created on Windows because it contains reserved
1173 with a name that can't be created on Windows because it contains reserved
1174 parts like ``AUX``, reserved characters like ``:``, or would cause a case
1174 parts like ``AUX``, reserved characters like ``:``, or would cause a case
1175 collision with an existing file).
1175 collision with an existing file).
1176 If set to ``ignore`` (or ``false``), no warning is printed.
1176 If set to ``ignore`` (or ``false``), no warning is printed.
1177 If set to ``abort``, the command is aborted.
1177 If set to ``abort``, the command is aborted.
1178 On Windows, this configuration option is ignored and the command aborted.
1178 On Windows, this configuration option is ignored and the command aborted.
1179
1179
1180 ``quiet``
1180 ``quiet``
1181 Reduce the amount of output printed. True or False. Default is False.
1181 Reduce the amount of output printed. True or False. Default is False.
1182
1182
1183 ``remotecmd``
1183 ``remotecmd``
1184 remote command to use for clone/push/pull operations. Default is ``hg``.
1184 remote command to use for clone/push/pull operations. Default is ``hg``.
1185
1185
1186 ``reportoldssl``
1186 ``reportoldssl``
1187 Warn if an SSL certificate is unable to be due to using Python
1187 Warn if an SSL certificate is unable to be due to using Python
1188 2.5 or earlier. True or False. Default is True.
1188 2.5 or earlier. True or False. Default is True.
1189
1189
1190 ``report_untrusted``
1190 ``report_untrusted``
1191 Warn if a ``.hg/hgrc`` file is ignored due to not being owned by a
1191 Warn if a ``.hg/hgrc`` file is ignored due to not being owned by a
1192 trusted user or group. True or False. Default is True.
1192 trusted user or group. True or False. Default is True.
1193
1193
1194 ``slash``
1194 ``slash``
1195 Display paths using a slash (``/``) as the path separator. This
1195 Display paths using a slash (``/``) as the path separator. This
1196 only makes a difference on systems where the default path
1196 only makes a difference on systems where the default path
1197 separator is not the slash character (e.g. Windows uses the
1197 separator is not the slash character (e.g. Windows uses the
1198 backslash character (``\``)).
1198 backslash character (``\``)).
1199 Default is False.
1199 Default is False.
1200
1200
1201 ``ssh``
1201 ``ssh``
1202 command to use for SSH connections. Default is ``ssh``.
1202 command to use for SSH connections. Default is ``ssh``.
1203
1203
1204 ``strict``
1204 ``strict``
1205 Require exact command names, instead of allowing unambiguous
1205 Require exact command names, instead of allowing unambiguous
1206 abbreviations. True or False. Default is False.
1206 abbreviations. True or False. Default is False.
1207
1207
1208 ``style``
1208 ``style``
1209 Name of style to use for command output.
1209 Name of style to use for command output.
1210
1210
1211 ``timeout``
1211 ``timeout``
1212 The timeout used when a lock is held (in seconds), a negative value
1212 The timeout used when a lock is held (in seconds), a negative value
1213 means no timeout. Default is 600.
1213 means no timeout. Default is 600.
1214
1214
1215 ``traceback``
1215 ``traceback``
1216 Mercurial always prints a traceback when an unknown exception
1216 Mercurial always prints a traceback when an unknown exception
1217 occurs. Setting this to True will make Mercurial print a traceback
1217 occurs. Setting this to True will make Mercurial print a traceback
1218 on all exceptions, even those recognized by Mercurial (such as
1218 on all exceptions, even those recognized by Mercurial (such as
1219 IOError or MemoryError). Default is False.
1219 IOError or MemoryError). Default is False.
1220
1220
1221 ``username``
1221 ``username``
1222 The committer of a changeset created when running "commit".
1222 The committer of a changeset created when running "commit".
1223 Typically a person's name and email address, e.g. ``Fred Widget
1223 Typically a person's name and email address, e.g. ``Fred Widget
1224 <fred@example.com>``. Default is ``$EMAIL`` or ``username@hostname``. If
1224 <fred@example.com>``. Default is ``$EMAIL`` or ``username@hostname``. If
1225 the username in hgrc is empty, it has to be specified manually or
1225 the username in hgrc is empty, it has to be specified manually or
1226 in a different hgrc file (e.g. ``$HOME/.hgrc``, if the admin set
1226 in a different hgrc file (e.g. ``$HOME/.hgrc``, if the admin set
1227 ``username =`` in the system hgrc). Environment variables in the
1227 ``username =`` in the system hgrc). Environment variables in the
1228 username are expanded.
1228 username are expanded.
1229
1229
1230 ``verbose``
1230 ``verbose``
1231 Increase the amount of output printed. True or False. Default is False.
1231 Increase the amount of output printed. True or False. Default is False.
1232
1232
1233
1233
1234 ``web``
1234 ``web``
1235 -------
1235 -------
1236
1236
1237 Web interface configuration. The settings in this section apply to
1237 Web interface configuration. The settings in this section apply to
1238 both the builtin webserver (started by :hg:`serve`) and the script you
1238 both the builtin webserver (started by :hg:`serve`) and the script you
1239 run through a webserver (``hgweb.cgi`` and the derivatives for FastCGI
1239 run through a webserver (``hgweb.cgi`` and the derivatives for FastCGI
1240 and WSGI).
1240 and WSGI).
1241
1241
1242 The Mercurial webserver does no authentication (it does not prompt for
1242 The Mercurial webserver does no authentication (it does not prompt for
1243 usernames and passwords to validate *who* users are), but it does do
1243 usernames and passwords to validate *who* users are), but it does do
1244 authorization (it grants or denies access for *authenticated users*
1244 authorization (it grants or denies access for *authenticated users*
1245 based on settings in this section). You must either configure your
1245 based on settings in this section). You must either configure your
1246 webserver to do authentication for you, or disable the authorization
1246 webserver to do authentication for you, or disable the authorization
1247 checks.
1247 checks.
1248
1248
1249 For a quick setup in a trusted environment, e.g., a private LAN, where
1249 For a quick setup in a trusted environment, e.g., a private LAN, where
1250 you want it to accept pushes from anybody, you can use the following
1250 you want it to accept pushes from anybody, you can use the following
1251 command line::
1251 command line::
1252
1252
1253 $ hg --config web.allow_push=* --config web.push_ssl=False serve
1253 $ hg --config web.allow_push=* --config web.push_ssl=False serve
1254
1254
1255 Note that this will allow anybody to push anything to the server and
1255 Note that this will allow anybody to push anything to the server and
1256 that this should not be used for public servers.
1256 that this should not be used for public servers.
1257
1257
1258 The full set of options is:
1258 The full set of options is:
1259
1259
1260 ``accesslog``
1260 ``accesslog``
1261 Where to output the access log. Default is stdout.
1261 Where to output the access log. Default is stdout.
1262
1262
1263 ``address``
1263 ``address``
1264 Interface address to bind to. Default is all.
1264 Interface address to bind to. Default is all.
1265
1265
1266 ``allow_archive``
1266 ``allow_archive``
1267 List of archive format (bz2, gz, zip) allowed for downloading.
1267 List of archive format (bz2, gz, zip) allowed for downloading.
1268 Default is empty.
1268 Default is empty.
1269
1269
1270 ``allowbz2``
1270 ``allowbz2``
1271 (DEPRECATED) Whether to allow .tar.bz2 downloading of repository
1271 (DEPRECATED) Whether to allow .tar.bz2 downloading of repository
1272 revisions.
1272 revisions.
1273 Default is False.
1273 Default is False.
1274
1274
1275 ``allowgz``
1275 ``allowgz``
1276 (DEPRECATED) Whether to allow .tar.gz downloading of repository
1276 (DEPRECATED) Whether to allow .tar.gz downloading of repository
1277 revisions.
1277 revisions.
1278 Default is False.
1278 Default is False.
1279
1279
1280 ``allowpull``
1280 ``allowpull``
1281 Whether to allow pulling from the repository. Default is True.
1281 Whether to allow pulling from the repository. Default is True.
1282
1282
1283 ``allow_push``
1283 ``allow_push``
1284 Whether to allow pushing to the repository. If empty or not set,
1284 Whether to allow pushing to the repository. If empty or not set,
1285 push is not allowed. If the special value ``*``, any remote user can
1285 push is not allowed. If the special value ``*``, any remote user can
1286 push, including unauthenticated users. Otherwise, the remote user
1286 push, including unauthenticated users. Otherwise, the remote user
1287 must have been authenticated, and the authenticated user name must
1287 must have been authenticated, and the authenticated user name must
1288 be present in this list. The contents of the allow_push list are
1288 be present in this list. The contents of the allow_push list are
1289 examined after the deny_push list.
1289 examined after the deny_push list.
1290
1290
1291 ``allow_read``
1291 ``allow_read``
1292 If the user has not already been denied repository access due to
1292 If the user has not already been denied repository access due to
1293 the contents of deny_read, this list determines whether to grant
1293 the contents of deny_read, this list determines whether to grant
1294 repository access to the user. If this list is not empty, and the
1294 repository access to the user. If this list is not empty, and the
1295 user is unauthenticated or not present in the list, then access is
1295 user is unauthenticated or not present in the list, then access is
1296 denied for the user. If the list is empty or not set, then access
1296 denied for the user. If the list is empty or not set, then access
1297 is permitted to all users by default. Setting allow_read to the
1297 is permitted to all users by default. Setting allow_read to the
1298 special value ``*`` is equivalent to it not being set (i.e. access
1298 special value ``*`` is equivalent to it not being set (i.e. access
1299 is permitted to all users). The contents of the allow_read list are
1299 is permitted to all users). The contents of the allow_read list are
1300 examined after the deny_read list.
1300 examined after the deny_read list.
1301
1301
1302 ``allowzip``
1302 ``allowzip``
1303 (DEPRECATED) Whether to allow .zip downloading of repository
1303 (DEPRECATED) Whether to allow .zip downloading of repository
1304 revisions. Default is False. This feature creates temporary files.
1304 revisions. Default is False. This feature creates temporary files.
1305
1305
1306 ``archivesubrepos``
1306 ``archivesubrepos``
1307 Whether to recurse into subrepositories when archiving. Default is
1307 Whether to recurse into subrepositories when archiving. Default is
1308 False.
1308 False.
1309
1309
1310 ``baseurl``
1310 ``baseurl``
1311 Base URL to use when publishing URLs in other locations, so
1311 Base URL to use when publishing URLs in other locations, so
1312 third-party tools like email notification hooks can construct
1312 third-party tools like email notification hooks can construct
1313 URLs. Example: ``http://hgserver/repos/``.
1313 URLs. Example: ``http://hgserver/repos/``.
1314
1314
1315 ``cacerts``
1315 ``cacerts``
1316 Path to file containing a list of PEM encoded certificate
1316 Path to file containing a list of PEM encoded certificate
1317 authority certificates. Environment variables and ``~user``
1317 authority certificates. Environment variables and ``~user``
1318 constructs are expanded in the filename. If specified on the
1318 constructs are expanded in the filename. If specified on the
1319 client, then it will verify the identity of remote HTTPS servers
1319 client, then it will verify the identity of remote HTTPS servers
1320 with these certificates.
1320 with these certificates.
1321
1321
1322 This feature is only supported when using Python 2.6 or later. If you wish
1322 This feature is only supported when using Python 2.6 or later. If you wish
1323 to use it with earlier versions of Python, install the backported
1323 to use it with earlier versions of Python, install the backported
1324 version of the ssl library that is available from
1324 version of the ssl library that is available from
1325 ``http://pypi.python.org``.
1325 ``http://pypi.python.org``.
1326
1326
1327 To disable SSL verification temporarily, specify ``--insecure`` from
1327 To disable SSL verification temporarily, specify ``--insecure`` from
1328 command line.
1328 command line.
1329
1329
1330 You can use OpenSSL's CA certificate file if your platform has
1330 You can use OpenSSL's CA certificate file if your platform has
1331 one. On most Linux systems this will be
1331 one. On most Linux systems this will be
1332 ``/etc/ssl/certs/ca-certificates.crt``. Otherwise you will have to
1332 ``/etc/ssl/certs/ca-certificates.crt``. Otherwise you will have to
1333 generate this file manually. The form must be as follows::
1333 generate this file manually. The form must be as follows::
1334
1334
1335 -----BEGIN CERTIFICATE-----
1335 -----BEGIN CERTIFICATE-----
1336 ... (certificate in base64 PEM encoding) ...
1336 ... (certificate in base64 PEM encoding) ...
1337 -----END CERTIFICATE-----
1337 -----END CERTIFICATE-----
1338 -----BEGIN CERTIFICATE-----
1338 -----BEGIN CERTIFICATE-----
1339 ... (certificate in base64 PEM encoding) ...
1339 ... (certificate in base64 PEM encoding) ...
1340 -----END CERTIFICATE-----
1340 -----END CERTIFICATE-----
1341
1341
1342 ``cache``
1342 ``cache``
1343 Whether to support caching in hgweb. Defaults to True.
1343 Whether to support caching in hgweb. Defaults to True.
1344
1344
1345 ``collapse``
1345 ``collapse``
1346 With ``descend`` enabled, repositories in subdirectories are shown at
1346 With ``descend`` enabled, repositories in subdirectories are shown at
1347 a single level alongside repositories in the current path. With
1347 a single level alongside repositories in the current path. With
1348 ``collapse`` also enabled, repositories residing at a deeper level than
1348 ``collapse`` also enabled, repositories residing at a deeper level than
1349 the current path are grouped behind navigable directory entries that
1349 the current path are grouped behind navigable directory entries that
1350 lead to the locations of these repositories. In effect, this setting
1350 lead to the locations of these repositories. In effect, this setting
1351 collapses each collection of repositories found within a subdirectory
1351 collapses each collection of repositories found within a subdirectory
1352 into a single entry for that subdirectory. Default is False.
1352 into a single entry for that subdirectory. Default is False.
1353
1353
1354 ``comparisoncontext``
1354 ``comparisoncontext``
1355 Number of lines of context to show in side-by-side file comparison. If
1355 Number of lines of context to show in side-by-side file comparison. If
1356 negative or the value ``full``, whole files are shown. Default is 5.
1356 negative or the value ``full``, whole files are shown. Default is 5.
1357 This setting can be overridden by a ``context`` request parameter to the
1357 This setting can be overridden by a ``context`` request parameter to the
1358 ``comparison`` command, taking the same values.
1358 ``comparison`` command, taking the same values.
1359
1359
1360 ``contact``
1360 ``contact``
1361 Name or email address of the person in charge of the repository.
1361 Name or email address of the person in charge of the repository.
1362 Defaults to ui.username or ``$EMAIL`` or "unknown" if unset or empty.
1362 Defaults to ui.username or ``$EMAIL`` or "unknown" if unset or empty.
1363
1363
1364 ``deny_push``
1364 ``deny_push``
1365 Whether to deny pushing to the repository. If empty or not set,
1365 Whether to deny pushing to the repository. If empty or not set,
1366 push is not denied. If the special value ``*``, all remote users are
1366 push is not denied. If the special value ``*``, all remote users are
1367 denied push. Otherwise, unauthenticated users are all denied, and
1367 denied push. Otherwise, unauthenticated users are all denied, and
1368 any authenticated user name present in this list is also denied. The
1368 any authenticated user name present in this list is also denied. The
1369 contents of the deny_push list are examined before the allow_push list.
1369 contents of the deny_push list are examined before the allow_push list.
1370
1370
1371 ``deny_read``
1371 ``deny_read``
1372 Whether to deny reading/viewing of the repository. If this list is
1372 Whether to deny reading/viewing of the repository. If this list is
1373 not empty, unauthenticated users are all denied, and any
1373 not empty, unauthenticated users are all denied, and any
1374 authenticated user name present in this list is also denied access to
1374 authenticated user name present in this list is also denied access to
1375 the repository. If set to the special value ``*``, all remote users
1375 the repository. If set to the special value ``*``, all remote users
1376 are denied access (rarely needed ;). If deny_read is empty or not set,
1376 are denied access (rarely needed ;). If deny_read is empty or not set,
1377 the determination of repository access depends on the presence and
1377 the determination of repository access depends on the presence and
1378 content of the allow_read list (see description). If both
1378 content of the allow_read list (see description). If both
1379 deny_read and allow_read are empty or not set, then access is
1379 deny_read and allow_read are empty or not set, then access is
1380 permitted to all users by default. If the repository is being
1380 permitted to all users by default. If the repository is being
1381 served via hgwebdir, denied users will not be able to see it in
1381 served via hgwebdir, denied users will not be able to see it in
1382 the list of repositories. The contents of the deny_read list have
1382 the list of repositories. The contents of the deny_read list have
1383 priority over (are examined before) the contents of the allow_read
1383 priority over (are examined before) the contents of the allow_read
1384 list.
1384 list.
1385
1385
1386 ``descend``
1386 ``descend``
1387 hgwebdir indexes will not descend into subdirectories. Only repositories
1387 hgwebdir indexes will not descend into subdirectories. Only repositories
1388 directly in the current path will be shown (other repositories are still
1388 directly in the current path will be shown (other repositories are still
1389 available from the index corresponding to their containing path).
1389 available from the index corresponding to their containing path).
1390
1390
1391 ``description``
1391 ``description``
1392 Textual description of the repository's purpose or contents.
1392 Textual description of the repository's purpose or contents.
1393 Default is "unknown".
1393 Default is "unknown".
1394
1394
1395 ``encoding``
1395 ``encoding``
1396 Character encoding name. Default is the current locale charset.
1396 Character encoding name. Default is the current locale charset.
1397 Example: "UTF-8"
1397 Example: "UTF-8"
1398
1398
1399 ``errorlog``
1399 ``errorlog``
1400 Where to output the error log. Default is stderr.
1400 Where to output the error log. Default is stderr.
1401
1401
1402 ``guessmime``
1402 ``guessmime``
1403 Control MIME types for raw download of file content.
1403 Control MIME types for raw download of file content.
1404 Set to True to let hgweb guess the content type from the file
1404 Set to True to let hgweb guess the content type from the file
1405 extension. This will serve HTML files as ``text/html`` and might
1405 extension. This will serve HTML files as ``text/html`` and might
1406 allow cross-site scripting attacks when serving untrusted
1406 allow cross-site scripting attacks when serving untrusted
1407 repositories. Default is False.
1407 repositories. Default is False.
1408
1408
1409 ``hidden``
1409 ``hidden``
1410 Whether to hide the repository in the hgwebdir index.
1410 Whether to hide the repository in the hgwebdir index.
1411 Default is False.
1411 Default is False.
1412
1412
1413 ``ipv6``
1413 ``ipv6``
1414 Whether to use IPv6. Default is False.
1414 Whether to use IPv6. Default is False.
1415
1415
1416 ``logoimg``
1416 ``logoimg``
1417 File name of the logo image that some templates display on each page.
1417 File name of the logo image that some templates display on each page.
1418 The file name is relative to ``staticurl``. That is, the full path to
1418 The file name is relative to ``staticurl``. That is, the full path to
1419 the logo image is "staticurl/logoimg".
1419 the logo image is "staticurl/logoimg".
1420 If unset, ``hglogo.png`` will be used.
1420 If unset, ``hglogo.png`` will be used.
1421
1421
1422 ``logourl``
1422 ``logourl``
1423 Base URL to use for logos. If unset, ``http://mercurial.selenic.com/``
1423 Base URL to use for logos. If unset, ``http://mercurial.selenic.com/``
1424 will be used.
1424 will be used.
1425
1425
1426 ``maxchanges``
1426 ``maxchanges``
1427 Maximum number of changes to list on the changelog. Default is 10.
1427 Maximum number of changes to list on the changelog. Default is 10.
1428
1428
1429 ``maxfiles``
1429 ``maxfiles``
1430 Maximum number of files to list per changeset. Default is 10.
1430 Maximum number of files to list per changeset. Default is 10.
1431
1431
1432 ``maxshortchanges``
1432 ``maxshortchanges``
1433 Maximum number of changes to list on the shortlog, graph or filelog
1433 Maximum number of changes to list on the shortlog, graph or filelog
1434 pages. Default is 60.
1434 pages. Default is 60.
1435
1435
1436 ``name``
1436 ``name``
1437 Repository name to use in the web interface. Default is current
1437 Repository name to use in the web interface. Default is current
1438 working directory.
1438 working directory.
1439
1439
1440 ``port``
1440 ``port``
1441 Port to listen on. Default is 8000.
1441 Port to listen on. Default is 8000.
1442
1442
1443 ``prefix``
1443 ``prefix``
1444 Prefix path to serve from. Default is '' (server root).
1444 Prefix path to serve from. Default is '' (server root).
1445
1445
1446 ``push_ssl``
1446 ``push_ssl``
1447 Whether to require that inbound pushes be transported over SSL to
1447 Whether to require that inbound pushes be transported over SSL to
1448 prevent password sniffing. Default is True.
1448 prevent password sniffing. Default is True.
1449
1449
1450 ``staticurl``
1450 ``staticurl``
1451 Base URL to use for static files. If unset, static files (e.g. the
1451 Base URL to use for static files. If unset, static files (e.g. the
1452 hgicon.png favicon) will be served by the CGI script itself. Use
1452 hgicon.png favicon) will be served by the CGI script itself. Use
1453 this setting to serve them directly with the HTTP server.
1453 this setting to serve them directly with the HTTP server.
1454 Example: ``http://hgserver/static/``.
1454 Example: ``http://hgserver/static/``.
1455
1455
1456 ``stripes``
1456 ``stripes``
1457 How many lines a "zebra stripe" should span in multi-line output.
1457 How many lines a "zebra stripe" should span in multi-line output.
1458 Default is 1; set to 0 to disable.
1458 Default is 1; set to 0 to disable.
1459
1459
1460 ``style``
1460 ``style``
1461 Which template map style to use.
1461 Which template map style to use.
1462
1462
1463 ``templates``
1463 ``templates``
1464 Where to find the HTML templates. Default is install path.
1464 Where to find the HTML templates. Default is install path.
1465
1465
1466 ``websub``
1467 ----------
1468
1469 Web substitution filter definition. You can use this section to
1470 define a set of regular expression substitution patterns which
1471 let you automatically modify the hgweb server output.
1472
1473 The default hgweb templates only apply these substitution patterns
1474 on the revision description fields. You can apply them anywhere
1475 you want when you create your own templates by adding calls to the
1476 "websub" filter (usually after calling the "escape" filter).
1477
1478 This can be used, for example, to convert issue references to links
1479 to your issue tracker, or to convert "markdown-like" syntax into
1480 HTML (see the examples below).
1481
1482 Each entry in this section names a substitution filter.
1483 The value of each entry defines the substitution expression itself.
1484 The websub expressions follow the old interhg extension syntax,
1485 which in turn imitates the Unix sed replacement syntax::
1486
1487 pattername = s/SEARCH_REGEX/REPLACE_EXPRESSION/[i]
1488
1489 You can use any separator other than "/". The final "i" is optional
1490 and indicates that the search must be case insensitive.
1491
1492 Examples::
1493
1494 [websub]
1495 issues = s|issue(\d+)|<a href="http://bts.example.org/issue\1">issue\1</a>|i
1496 italic = s/\b_(\S+)_\b/<i>\1<\/i>/
1497 bold = s/\*\b(\S+)\b\*/<b>\1<\/b>/
1498
1466 ``worker``
1499 ``worker``
1467 ----------
1500 ----------
1468
1501
1469 Parallel master/worker configuration. We currently perform working
1502 Parallel master/worker configuration. We currently perform working
1470 directory updates in parallel on Unix-like systems, which greatly
1503 directory updates in parallel on Unix-like systems, which greatly
1471 helps performance.
1504 helps performance.
1472
1505
1473 ``numcpus``
1506 ``numcpus``
1474 Number of CPUs to use for parallel operations. Default is 4 or the
1507 Number of CPUs to use for parallel operations. Default is 4 or the
1475 number of CPUs on the system, whichever is larger. A zero or
1508 number of CPUs on the system, whichever is larger. A zero or
1476 negative value is treated as ``use the default``.
1509 negative value is treated as ``use the default``.
@@ -1,345 +1,393 b''
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import os
9 import os
10 from mercurial import ui, hg, hook, error, encoding, templater, util, repoview
10 from mercurial import ui, hg, hook, error, encoding, templater, util, repoview
11 from mercurial.templatefilters import websub
12 from mercurial.i18n import _
11 from common import get_stat, ErrorResponse, permhooks, caching
13 from common import get_stat, ErrorResponse, permhooks, caching
12 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
14 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
13 from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
15 from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
14 from request import wsgirequest
16 from request import wsgirequest
15 import webcommands, protocol, webutil
17 import webcommands, protocol, webutil, re
16
18
17 perms = {
19 perms = {
18 'changegroup': 'pull',
20 'changegroup': 'pull',
19 'changegroupsubset': 'pull',
21 'changegroupsubset': 'pull',
20 'getbundle': 'pull',
22 'getbundle': 'pull',
21 'stream_out': 'pull',
23 'stream_out': 'pull',
22 'listkeys': 'pull',
24 'listkeys': 'pull',
23 'unbundle': 'push',
25 'unbundle': 'push',
24 'pushkey': 'push',
26 'pushkey': 'push',
25 }
27 }
26
28
27 def makebreadcrumb(url, prefix=''):
29 def makebreadcrumb(url, prefix=''):
28 '''Return a 'URL breadcrumb' list
30 '''Return a 'URL breadcrumb' list
29
31
30 A 'URL breadcrumb' is a list of URL-name pairs,
32 A 'URL breadcrumb' is a list of URL-name pairs,
31 corresponding to each of the path items on a URL.
33 corresponding to each of the path items on a URL.
32 This can be used to create path navigation entries.
34 This can be used to create path navigation entries.
33 '''
35 '''
34 if url.endswith('/'):
36 if url.endswith('/'):
35 url = url[:-1]
37 url = url[:-1]
36 if prefix:
38 if prefix:
37 url = '/' + prefix + url
39 url = '/' + prefix + url
38 relpath = url
40 relpath = url
39 if relpath.startswith('/'):
41 if relpath.startswith('/'):
40 relpath = relpath[1:]
42 relpath = relpath[1:]
41
43
42 breadcrumb = []
44 breadcrumb = []
43 urlel = url
45 urlel = url
44 pathitems = [''] + relpath.split('/')
46 pathitems = [''] + relpath.split('/')
45 for pathel in reversed(pathitems):
47 for pathel in reversed(pathitems):
46 if not pathel or not urlel:
48 if not pathel or not urlel:
47 break
49 break
48 breadcrumb.append({'url': urlel, 'name': pathel})
50 breadcrumb.append({'url': urlel, 'name': pathel})
49 urlel = os.path.dirname(urlel)
51 urlel = os.path.dirname(urlel)
50 return reversed(breadcrumb)
52 return reversed(breadcrumb)
51
53
52
54
53 class hgweb(object):
55 class hgweb(object):
54 def __init__(self, repo, name=None, baseui=None):
56 def __init__(self, repo, name=None, baseui=None):
55 if isinstance(repo, str):
57 if isinstance(repo, str):
56 if baseui:
58 if baseui:
57 u = baseui.copy()
59 u = baseui.copy()
58 else:
60 else:
59 u = ui.ui()
61 u = ui.ui()
60 self.repo = hg.repository(u, repo)
62 self.repo = hg.repository(u, repo)
61 else:
63 else:
62 self.repo = repo
64 self.repo = repo
63
65
64 self.repo = self._getview(self.repo)
66 self.repo = self._getview(self.repo)
65 self.repo.ui.setconfig('ui', 'report_untrusted', 'off')
67 self.repo.ui.setconfig('ui', 'report_untrusted', 'off')
66 self.repo.ui.setconfig('ui', 'nontty', 'true')
68 self.repo.ui.setconfig('ui', 'nontty', 'true')
67 hook.redirect(True)
69 hook.redirect(True)
68 self.mtime = -1
70 self.mtime = -1
69 self.size = -1
71 self.size = -1
70 self.reponame = name
72 self.reponame = name
71 self.archives = 'zip', 'gz', 'bz2'
73 self.archives = 'zip', 'gz', 'bz2'
72 self.stripecount = 1
74 self.stripecount = 1
73 # a repo owner may set web.templates in .hg/hgrc to get any file
75 # a repo owner may set web.templates in .hg/hgrc to get any file
74 # readable by the user running the CGI script
76 # readable by the user running the CGI script
75 self.templatepath = self.config('web', 'templates')
77 self.templatepath = self.config('web', 'templates')
78 self.websubtable = self.loadwebsub()
76
79
77 # The CGI scripts are often run by a user different from the repo owner.
80 # The CGI scripts are often run by a user different from the repo owner.
78 # Trust the settings from the .hg/hgrc files by default.
81 # Trust the settings from the .hg/hgrc files by default.
79 def config(self, section, name, default=None, untrusted=True):
82 def config(self, section, name, default=None, untrusted=True):
80 return self.repo.ui.config(section, name, default,
83 return self.repo.ui.config(section, name, default,
81 untrusted=untrusted)
84 untrusted=untrusted)
82
85
83 def configbool(self, section, name, default=False, untrusted=True):
86 def configbool(self, section, name, default=False, untrusted=True):
84 return self.repo.ui.configbool(section, name, default,
87 return self.repo.ui.configbool(section, name, default,
85 untrusted=untrusted)
88 untrusted=untrusted)
86
89
87 def configlist(self, section, name, default=None, untrusted=True):
90 def configlist(self, section, name, default=None, untrusted=True):
88 return self.repo.ui.configlist(section, name, default,
91 return self.repo.ui.configlist(section, name, default,
89 untrusted=untrusted)
92 untrusted=untrusted)
90
93
91 def _getview(self, repo):
94 def _getview(self, repo):
92 viewconfig = self.config('web', 'view', 'served')
95 viewconfig = self.config('web', 'view', 'served')
93 if viewconfig == 'all':
96 if viewconfig == 'all':
94 return repo.unfiltered()
97 return repo.unfiltered()
95 elif viewconfig in repoview.filtertable:
98 elif viewconfig in repoview.filtertable:
96 return repo.filtered(viewconfig)
99 return repo.filtered(viewconfig)
97 else:
100 else:
98 return repo.filtered('served')
101 return repo.filtered('served')
99
102
100 def refresh(self, request=None):
103 def refresh(self, request=None):
101 if request:
104 if request:
102 self.repo.ui.environ = request.env
105 self.repo.ui.environ = request.env
103 st = get_stat(self.repo.spath)
106 st = get_stat(self.repo.spath)
104 # compare changelog size in addition to mtime to catch
107 # compare changelog size in addition to mtime to catch
105 # rollbacks made less than a second ago
108 # rollbacks made less than a second ago
106 if st.st_mtime != self.mtime or st.st_size != self.size:
109 if st.st_mtime != self.mtime or st.st_size != self.size:
107 self.mtime = st.st_mtime
110 self.mtime = st.st_mtime
108 self.size = st.st_size
111 self.size = st.st_size
109 self.repo = hg.repository(self.repo.ui, self.repo.root)
112 self.repo = hg.repository(self.repo.ui, self.repo.root)
110 self.repo = self._getview(self.repo)
113 self.repo = self._getview(self.repo)
111 self.maxchanges = int(self.config("web", "maxchanges", 10))
114 self.maxchanges = int(self.config("web", "maxchanges", 10))
112 self.stripecount = int(self.config("web", "stripes", 1))
115 self.stripecount = int(self.config("web", "stripes", 1))
113 self.maxshortchanges = int(self.config("web", "maxshortchanges",
116 self.maxshortchanges = int(self.config("web", "maxshortchanges",
114 60))
117 60))
115 self.maxfiles = int(self.config("web", "maxfiles", 10))
118 self.maxfiles = int(self.config("web", "maxfiles", 10))
116 self.allowpull = self.configbool("web", "allowpull", True)
119 self.allowpull = self.configbool("web", "allowpull", True)
117 encoding.encoding = self.config("web", "encoding",
120 encoding.encoding = self.config("web", "encoding",
118 encoding.encoding)
121 encoding.encoding)
119
122
120 def run(self):
123 def run(self):
121 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
124 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
122 raise RuntimeError("This function is only intended to be "
125 raise RuntimeError("This function is only intended to be "
123 "called while running as a CGI script.")
126 "called while running as a CGI script.")
124 import mercurial.hgweb.wsgicgi as wsgicgi
127 import mercurial.hgweb.wsgicgi as wsgicgi
125 wsgicgi.launch(self)
128 wsgicgi.launch(self)
126
129
127 def __call__(self, env, respond):
130 def __call__(self, env, respond):
128 req = wsgirequest(env, respond)
131 req = wsgirequest(env, respond)
129 return self.run_wsgi(req)
132 return self.run_wsgi(req)
130
133
131 def run_wsgi(self, req):
134 def run_wsgi(self, req):
132
135
133 self.refresh(req)
136 self.refresh(req)
134
137
135 # work with CGI variables to create coherent structure
138 # work with CGI variables to create coherent structure
136 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
139 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
137
140
138 req.url = req.env['SCRIPT_NAME']
141 req.url = req.env['SCRIPT_NAME']
139 if not req.url.endswith('/'):
142 if not req.url.endswith('/'):
140 req.url += '/'
143 req.url += '/'
141 if 'REPO_NAME' in req.env:
144 if 'REPO_NAME' in req.env:
142 req.url += req.env['REPO_NAME'] + '/'
145 req.url += req.env['REPO_NAME'] + '/'
143
146
144 if 'PATH_INFO' in req.env:
147 if 'PATH_INFO' in req.env:
145 parts = req.env['PATH_INFO'].strip('/').split('/')
148 parts = req.env['PATH_INFO'].strip('/').split('/')
146 repo_parts = req.env.get('REPO_NAME', '').split('/')
149 repo_parts = req.env.get('REPO_NAME', '').split('/')
147 if parts[:len(repo_parts)] == repo_parts:
150 if parts[:len(repo_parts)] == repo_parts:
148 parts = parts[len(repo_parts):]
151 parts = parts[len(repo_parts):]
149 query = '/'.join(parts)
152 query = '/'.join(parts)
150 else:
153 else:
151 query = req.env['QUERY_STRING'].split('&', 1)[0]
154 query = req.env['QUERY_STRING'].split('&', 1)[0]
152 query = query.split(';', 1)[0]
155 query = query.split(';', 1)[0]
153
156
154 # process this if it's a protocol request
157 # process this if it's a protocol request
155 # protocol bits don't need to create any URLs
158 # protocol bits don't need to create any URLs
156 # and the clients always use the old URL structure
159 # and the clients always use the old URL structure
157
160
158 cmd = req.form.get('cmd', [''])[0]
161 cmd = req.form.get('cmd', [''])[0]
159 if protocol.iscmd(cmd):
162 if protocol.iscmd(cmd):
160 try:
163 try:
161 if query:
164 if query:
162 raise ErrorResponse(HTTP_NOT_FOUND)
165 raise ErrorResponse(HTTP_NOT_FOUND)
163 if cmd in perms:
166 if cmd in perms:
164 self.check_perm(req, perms[cmd])
167 self.check_perm(req, perms[cmd])
165 return protocol.call(self.repo, req, cmd)
168 return protocol.call(self.repo, req, cmd)
166 except ErrorResponse, inst:
169 except ErrorResponse, inst:
167 # A client that sends unbundle without 100-continue will
170 # A client that sends unbundle without 100-continue will
168 # break if we respond early.
171 # break if we respond early.
169 if (cmd == 'unbundle' and
172 if (cmd == 'unbundle' and
170 (req.env.get('HTTP_EXPECT',
173 (req.env.get('HTTP_EXPECT',
171 '').lower() != '100-continue') or
174 '').lower() != '100-continue') or
172 req.env.get('X-HgHttp2', '')):
175 req.env.get('X-HgHttp2', '')):
173 req.drain()
176 req.drain()
174 req.respond(inst, protocol.HGTYPE,
177 req.respond(inst, protocol.HGTYPE,
175 body='0\n%s\n' % inst.message)
178 body='0\n%s\n' % inst.message)
176 return ''
179 return ''
177
180
178 # translate user-visible url structure to internal structure
181 # translate user-visible url structure to internal structure
179
182
180 args = query.split('/', 2)
183 args = query.split('/', 2)
181 if 'cmd' not in req.form and args and args[0]:
184 if 'cmd' not in req.form and args and args[0]:
182
185
183 cmd = args.pop(0)
186 cmd = args.pop(0)
184 style = cmd.rfind('-')
187 style = cmd.rfind('-')
185 if style != -1:
188 if style != -1:
186 req.form['style'] = [cmd[:style]]
189 req.form['style'] = [cmd[:style]]
187 cmd = cmd[style + 1:]
190 cmd = cmd[style + 1:]
188
191
189 # avoid accepting e.g. style parameter as command
192 # avoid accepting e.g. style parameter as command
190 if util.safehasattr(webcommands, cmd):
193 if util.safehasattr(webcommands, cmd):
191 req.form['cmd'] = [cmd]
194 req.form['cmd'] = [cmd]
192 else:
195 else:
193 cmd = ''
196 cmd = ''
194
197
195 if cmd == 'static':
198 if cmd == 'static':
196 req.form['file'] = ['/'.join(args)]
199 req.form['file'] = ['/'.join(args)]
197 else:
200 else:
198 if args and args[0]:
201 if args and args[0]:
199 node = args.pop(0)
202 node = args.pop(0)
200 req.form['node'] = [node]
203 req.form['node'] = [node]
201 if args:
204 if args:
202 req.form['file'] = args
205 req.form['file'] = args
203
206
204 ua = req.env.get('HTTP_USER_AGENT', '')
207 ua = req.env.get('HTTP_USER_AGENT', '')
205 if cmd == 'rev' and 'mercurial' in ua:
208 if cmd == 'rev' and 'mercurial' in ua:
206 req.form['style'] = ['raw']
209 req.form['style'] = ['raw']
207
210
208 if cmd == 'archive':
211 if cmd == 'archive':
209 fn = req.form['node'][0]
212 fn = req.form['node'][0]
210 for type_, spec in self.archive_specs.iteritems():
213 for type_, spec in self.archive_specs.iteritems():
211 ext = spec[2]
214 ext = spec[2]
212 if fn.endswith(ext):
215 if fn.endswith(ext):
213 req.form['node'] = [fn[:-len(ext)]]
216 req.form['node'] = [fn[:-len(ext)]]
214 req.form['type'] = [type_]
217 req.form['type'] = [type_]
215
218
216 # process the web interface request
219 # process the web interface request
217
220
218 try:
221 try:
219 tmpl = self.templater(req)
222 tmpl = self.templater(req)
220 ctype = tmpl('mimetype', encoding=encoding.encoding)
223 ctype = tmpl('mimetype', encoding=encoding.encoding)
221 ctype = templater.stringify(ctype)
224 ctype = templater.stringify(ctype)
222
225
223 # check read permissions non-static content
226 # check read permissions non-static content
224 if cmd != 'static':
227 if cmd != 'static':
225 self.check_perm(req, None)
228 self.check_perm(req, None)
226
229
227 if cmd == '':
230 if cmd == '':
228 req.form['cmd'] = [tmpl.cache['default']]
231 req.form['cmd'] = [tmpl.cache['default']]
229 cmd = req.form['cmd'][0]
232 cmd = req.form['cmd'][0]
230
233
231 if self.configbool('web', 'cache', True):
234 if self.configbool('web', 'cache', True):
232 caching(self, req) # sets ETag header or raises NOT_MODIFIED
235 caching(self, req) # sets ETag header or raises NOT_MODIFIED
233 if cmd not in webcommands.__all__:
236 if cmd not in webcommands.__all__:
234 msg = 'no such method: %s' % cmd
237 msg = 'no such method: %s' % cmd
235 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
238 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
236 elif cmd == 'file' and 'raw' in req.form.get('style', []):
239 elif cmd == 'file' and 'raw' in req.form.get('style', []):
237 self.ctype = ctype
240 self.ctype = ctype
238 content = webcommands.rawfile(self, req, tmpl)
241 content = webcommands.rawfile(self, req, tmpl)
239 else:
242 else:
240 content = getattr(webcommands, cmd)(self, req, tmpl)
243 content = getattr(webcommands, cmd)(self, req, tmpl)
241 req.respond(HTTP_OK, ctype)
244 req.respond(HTTP_OK, ctype)
242
245
243 return content
246 return content
244
247
245 except (error.LookupError, error.RepoLookupError), err:
248 except (error.LookupError, error.RepoLookupError), err:
246 req.respond(HTTP_NOT_FOUND, ctype)
249 req.respond(HTTP_NOT_FOUND, ctype)
247 msg = str(err)
250 msg = str(err)
248 if util.safehasattr(err, 'name') and 'manifest' not in msg:
251 if util.safehasattr(err, 'name') and 'manifest' not in msg:
249 msg = 'revision not found: %s' % err.name
252 msg = 'revision not found: %s' % err.name
250 return tmpl('error', error=msg)
253 return tmpl('error', error=msg)
251 except (error.RepoError, error.RevlogError), inst:
254 except (error.RepoError, error.RevlogError), inst:
252 req.respond(HTTP_SERVER_ERROR, ctype)
255 req.respond(HTTP_SERVER_ERROR, ctype)
253 return tmpl('error', error=str(inst))
256 return tmpl('error', error=str(inst))
254 except ErrorResponse, inst:
257 except ErrorResponse, inst:
255 req.respond(inst, ctype)
258 req.respond(inst, ctype)
256 if inst.code == HTTP_NOT_MODIFIED:
259 if inst.code == HTTP_NOT_MODIFIED:
257 # Not allowed to return a body on a 304
260 # Not allowed to return a body on a 304
258 return ['']
261 return ['']
259 return tmpl('error', error=inst.message)
262 return tmpl('error', error=inst.message)
260
263
264 def loadwebsub(self):
265 websubtable = []
266 websubdefs = self.repo.ui.configitems('websub')
267 # we must maintain interhg backwards compatibility
268 websubdefs += self.repo.ui.configitems('interhg')
269 for key, pattern in websubdefs:
270 # grab the delimiter from the character after the "s"
271 unesc = pattern[1]
272 delim = re.escape(unesc)
273
274 # identify portions of the pattern, taking care to avoid escaped
275 # delimiters. the replace format and flags are optional, but
276 # delimiters are required.
277 match = re.match(
278 r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
279 % (delim, delim, delim), pattern)
280 if not match:
281 self.repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
282 % (key, pattern))
283 continue
284
285 # we need to unescape the delimiter for regexp and format
286 delim_re = re.compile(r'(?<!\\)\\%s' % delim)
287 regexp = delim_re.sub(unesc, match.group(1))
288 format = delim_re.sub(unesc, match.group(2))
289
290 # the pattern allows for 6 regexp flags, so set them if necessary
291 flagin = match.group(3)
292 flags = 0
293 if flagin:
294 for flag in flagin.upper():
295 flags |= re.__dict__[flag]
296
297 try:
298 regexp = re.compile(regexp, flags)
299 websubtable.append((regexp, format))
300 except re.error:
301 self.repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
302 % (key, regexp))
303 return websubtable
304
261 def templater(self, req):
305 def templater(self, req):
262
306
263 # determine scheme, port and server name
307 # determine scheme, port and server name
264 # this is needed to create absolute urls
308 # this is needed to create absolute urls
265
309
266 proto = req.env.get('wsgi.url_scheme')
310 proto = req.env.get('wsgi.url_scheme')
267 if proto == 'https':
311 if proto == 'https':
268 proto = 'https'
312 proto = 'https'
269 default_port = "443"
313 default_port = "443"
270 else:
314 else:
271 proto = 'http'
315 proto = 'http'
272 default_port = "80"
316 default_port = "80"
273
317
274 port = req.env["SERVER_PORT"]
318 port = req.env["SERVER_PORT"]
275 port = port != default_port and (":" + port) or ""
319 port = port != default_port and (":" + port) or ""
276 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
320 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
277 logourl = self.config("web", "logourl", "http://mercurial.selenic.com/")
321 logourl = self.config("web", "logourl", "http://mercurial.selenic.com/")
278 logoimg = self.config("web", "logoimg", "hglogo.png")
322 logoimg = self.config("web", "logoimg", "hglogo.png")
279 staticurl = self.config("web", "staticurl") or req.url + 'static/'
323 staticurl = self.config("web", "staticurl") or req.url + 'static/'
280 if not staticurl.endswith('/'):
324 if not staticurl.endswith('/'):
281 staticurl += '/'
325 staticurl += '/'
282
326
283 # some functions for the templater
327 # some functions for the templater
284
328
285 def header(**map):
329 def header(**map):
286 yield tmpl('header', encoding=encoding.encoding, **map)
330 yield tmpl('header', encoding=encoding.encoding, **map)
287
331
288 def footer(**map):
332 def footer(**map):
289 yield tmpl("footer", **map)
333 yield tmpl("footer", **map)
290
334
291 def motd(**map):
335 def motd(**map):
292 yield self.config("web", "motd", "")
336 yield self.config("web", "motd", "")
293
337
294 # figure out which style to use
338 # figure out which style to use
295
339
296 vars = {}
340 vars = {}
297 styles = (
341 styles = (
298 req.form.get('style', [None])[0],
342 req.form.get('style', [None])[0],
299 self.config('web', 'style'),
343 self.config('web', 'style'),
300 'paper',
344 'paper',
301 )
345 )
302 style, mapfile = templater.stylemap(styles, self.templatepath)
346 style, mapfile = templater.stylemap(styles, self.templatepath)
303 if style == styles[0]:
347 if style == styles[0]:
304 vars['style'] = style
348 vars['style'] = style
305
349
306 start = req.url[-1] == '?' and '&' or '?'
350 start = req.url[-1] == '?' and '&' or '?'
307 sessionvars = webutil.sessionvars(vars, start)
351 sessionvars = webutil.sessionvars(vars, start)
308
352
309 if not self.reponame:
353 if not self.reponame:
310 self.reponame = (self.config("web", "name")
354 self.reponame = (self.config("web", "name")
311 or req.env.get('REPO_NAME')
355 or req.env.get('REPO_NAME')
312 or req.url.strip('/') or self.repo.root)
356 or req.url.strip('/') or self.repo.root)
313
357
358 def websubfilter(text):
359 return websub(text, self.websubtable)
360
314 # create the templater
361 # create the templater
315
362
316 tmpl = templater.templater(mapfile,
363 tmpl = templater.templater(mapfile,
364 filters={"websub": websubfilter},
317 defaults={"url": req.url,
365 defaults={"url": req.url,
318 "logourl": logourl,
366 "logourl": logourl,
319 "logoimg": logoimg,
367 "logoimg": logoimg,
320 "staticurl": staticurl,
368 "staticurl": staticurl,
321 "urlbase": urlbase,
369 "urlbase": urlbase,
322 "repo": self.reponame,
370 "repo": self.reponame,
323 "header": header,
371 "header": header,
324 "footer": footer,
372 "footer": footer,
325 "motd": motd,
373 "motd": motd,
326 "sessionvars": sessionvars,
374 "sessionvars": sessionvars,
327 "pathdef": makebreadcrumb(req.url),
375 "pathdef": makebreadcrumb(req.url),
328 })
376 })
329 return tmpl
377 return tmpl
330
378
331 def archivelist(self, nodeid):
379 def archivelist(self, nodeid):
332 allowed = self.configlist("web", "allow_archive")
380 allowed = self.configlist("web", "allow_archive")
333 for i, spec in self.archive_specs.iteritems():
381 for i, spec in self.archive_specs.iteritems():
334 if i in allowed or self.configbool("web", "allow" + i):
382 if i in allowed or self.configbool("web", "allow" + i):
335 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
383 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
336
384
337 archive_specs = {
385 archive_specs = {
338 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None),
386 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None),
339 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None),
387 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None),
340 'zip': ('application/zip', 'zip', '.zip', None),
388 'zip': ('application/zip', 'zip', '.zip', None),
341 }
389 }
342
390
343 def check_perm(self, req, op):
391 def check_perm(self, req, op):
344 for hook in permhooks:
392 for hook in permhooks:
345 hook(self, req, op)
393 hook(self, req, op)
@@ -1,1003 +1,1003 b''
1 # scmutil.py - Mercurial core utility functions
1 # scmutil.py - Mercurial core utility functions
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 from mercurial.node import nullrev
9 from mercurial.node import nullrev
10 import util, error, osutil, revset, similar, encoding, phases
10 import util, error, osutil, revset, similar, encoding, phases
11 import match as matchmod
11 import match as matchmod
12 import os, errno, re, stat, sys, glob
12 import os, errno, re, stat, sys, glob
13
13
14 def nochangesfound(ui, repo, excluded=None):
14 def nochangesfound(ui, repo, excluded=None):
15 '''Report no changes for push/pull, excluded is None or a list of
15 '''Report no changes for push/pull, excluded is None or a list of
16 nodes excluded from the push/pull.
16 nodes excluded from the push/pull.
17 '''
17 '''
18 secretlist = []
18 secretlist = []
19 if excluded:
19 if excluded:
20 for n in excluded:
20 for n in excluded:
21 if n not in repo:
21 if n not in repo:
22 # discovery should not have included the filtered revision,
22 # discovery should not have included the filtered revision,
23 # we have to explicitly exclude it until discovery is cleanup.
23 # we have to explicitly exclude it until discovery is cleanup.
24 continue
24 continue
25 ctx = repo[n]
25 ctx = repo[n]
26 if ctx.phase() >= phases.secret and not ctx.extinct():
26 if ctx.phase() >= phases.secret and not ctx.extinct():
27 secretlist.append(n)
27 secretlist.append(n)
28
28
29 if secretlist:
29 if secretlist:
30 ui.status(_("no changes found (ignored %d secret changesets)\n")
30 ui.status(_("no changes found (ignored %d secret changesets)\n")
31 % len(secretlist))
31 % len(secretlist))
32 else:
32 else:
33 ui.status(_("no changes found\n"))
33 ui.status(_("no changes found\n"))
34
34
35 def checknewlabel(repo, lbl, kind):
35 def checknewlabel(repo, lbl, kind):
36 if lbl in ['tip', '.', 'null']:
36 if lbl in ['tip', '.', 'null']:
37 raise util.Abort(_("the name '%s' is reserved") % lbl)
37 raise util.Abort(_("the name '%s' is reserved") % lbl)
38 for c in (':', '\0', '\n', '\r'):
38 for c in (':', '\0', '\n', '\r'):
39 if c in lbl:
39 if c in lbl:
40 raise util.Abort(_("%r cannot be used in a name") % c)
40 raise util.Abort(_("%r cannot be used in a name") % c)
41 try:
41 try:
42 int(lbl)
42 int(lbl)
43 raise util.Abort(_("a %s cannot have an integer as its name") % kind)
43 raise util.Abort(_("a %s cannot have an integer as its name") % kind)
44 except ValueError:
44 except ValueError:
45 pass
45 pass
46
46
47 def checkfilename(f):
47 def checkfilename(f):
48 '''Check that the filename f is an acceptable filename for a tracked file'''
48 '''Check that the filename f is an acceptable filename for a tracked file'''
49 if '\r' in f or '\n' in f:
49 if '\r' in f or '\n' in f:
50 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
50 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
51
51
52 def checkportable(ui, f):
52 def checkportable(ui, f):
53 '''Check if filename f is portable and warn or abort depending on config'''
53 '''Check if filename f is portable and warn or abort depending on config'''
54 checkfilename(f)
54 checkfilename(f)
55 abort, warn = checkportabilityalert(ui)
55 abort, warn = checkportabilityalert(ui)
56 if abort or warn:
56 if abort or warn:
57 msg = util.checkwinfilename(f)
57 msg = util.checkwinfilename(f)
58 if msg:
58 if msg:
59 msg = "%s: %r" % (msg, f)
59 msg = "%s: %r" % (msg, f)
60 if abort:
60 if abort:
61 raise util.Abort(msg)
61 raise util.Abort(msg)
62 ui.warn(_("warning: %s\n") % msg)
62 ui.warn(_("warning: %s\n") % msg)
63
63
64 def checkportabilityalert(ui):
64 def checkportabilityalert(ui):
65 '''check if the user's config requests nothing, a warning, or abort for
65 '''check if the user's config requests nothing, a warning, or abort for
66 non-portable filenames'''
66 non-portable filenames'''
67 val = ui.config('ui', 'portablefilenames', 'warn')
67 val = ui.config('ui', 'portablefilenames', 'warn')
68 lval = val.lower()
68 lval = val.lower()
69 bval = util.parsebool(val)
69 bval = util.parsebool(val)
70 abort = os.name == 'nt' or lval == 'abort'
70 abort = os.name == 'nt' or lval == 'abort'
71 warn = bval or lval == 'warn'
71 warn = bval or lval == 'warn'
72 if bval is None and not (warn or abort or lval == 'ignore'):
72 if bval is None and not (warn or abort or lval == 'ignore'):
73 raise error.ConfigError(
73 raise error.ConfigError(
74 _("ui.portablefilenames value is invalid ('%s')") % val)
74 _("ui.portablefilenames value is invalid ('%s')") % val)
75 return abort, warn
75 return abort, warn
76
76
77 class casecollisionauditor(object):
77 class casecollisionauditor(object):
78 def __init__(self, ui, abort, dirstate):
78 def __init__(self, ui, abort, dirstate):
79 self._ui = ui
79 self._ui = ui
80 self._abort = abort
80 self._abort = abort
81 allfiles = '\0'.join(dirstate._map)
81 allfiles = '\0'.join(dirstate._map)
82 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
82 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
83 self._dirstate = dirstate
83 self._dirstate = dirstate
84 # The purpose of _newfiles is so that we don't complain about
84 # The purpose of _newfiles is so that we don't complain about
85 # case collisions if someone were to call this object with the
85 # case collisions if someone were to call this object with the
86 # same filename twice.
86 # same filename twice.
87 self._newfiles = set()
87 self._newfiles = set()
88
88
89 def __call__(self, f):
89 def __call__(self, f):
90 fl = encoding.lower(f)
90 fl = encoding.lower(f)
91 if (fl in self._loweredfiles and f not in self._dirstate and
91 if (fl in self._loweredfiles and f not in self._dirstate and
92 f not in self._newfiles):
92 f not in self._newfiles):
93 msg = _('possible case-folding collision for %s') % f
93 msg = _('possible case-folding collision for %s') % f
94 if self._abort:
94 if self._abort:
95 raise util.Abort(msg)
95 raise util.Abort(msg)
96 self._ui.warn(_("warning: %s\n") % msg)
96 self._ui.warn(_("warning: %s\n") % msg)
97 self._loweredfiles.add(fl)
97 self._loweredfiles.add(fl)
98 self._newfiles.add(f)
98 self._newfiles.add(f)
99
99
100 class pathauditor(object):
100 class pathauditor(object):
101 '''ensure that a filesystem path contains no banned components.
101 '''ensure that a filesystem path contains no banned components.
102 the following properties of a path are checked:
102 the following properties of a path are checked:
103
103
104 - ends with a directory separator
104 - ends with a directory separator
105 - under top-level .hg
105 - under top-level .hg
106 - starts at the root of a windows drive
106 - starts at the root of a windows drive
107 - contains ".."
107 - contains ".."
108 - traverses a symlink (e.g. a/symlink_here/b)
108 - traverses a symlink (e.g. a/symlink_here/b)
109 - inside a nested repository (a callback can be used to approve
109 - inside a nested repository (a callback can be used to approve
110 some nested repositories, e.g., subrepositories)
110 some nested repositories, e.g., subrepositories)
111 '''
111 '''
112
112
113 def __init__(self, root, callback=None):
113 def __init__(self, root, callback=None):
114 self.audited = set()
114 self.audited = set()
115 self.auditeddir = set()
115 self.auditeddir = set()
116 self.root = root
116 self.root = root
117 self.callback = callback
117 self.callback = callback
118 if os.path.lexists(root) and not util.checkcase(root):
118 if os.path.lexists(root) and not util.checkcase(root):
119 self.normcase = util.normcase
119 self.normcase = util.normcase
120 else:
120 else:
121 self.normcase = lambda x: x
121 self.normcase = lambda x: x
122
122
123 def __call__(self, path):
123 def __call__(self, path):
124 '''Check the relative path.
124 '''Check the relative path.
125 path may contain a pattern (e.g. foodir/**.txt)'''
125 path may contain a pattern (e.g. foodir/**.txt)'''
126
126
127 path = util.localpath(path)
127 path = util.localpath(path)
128 normpath = self.normcase(path)
128 normpath = self.normcase(path)
129 if normpath in self.audited:
129 if normpath in self.audited:
130 return
130 return
131 # AIX ignores "/" at end of path, others raise EISDIR.
131 # AIX ignores "/" at end of path, others raise EISDIR.
132 if util.endswithsep(path):
132 if util.endswithsep(path):
133 raise util.Abort(_("path ends in directory separator: %s") % path)
133 raise util.Abort(_("path ends in directory separator: %s") % path)
134 parts = util.splitpath(path)
134 parts = util.splitpath(path)
135 if (os.path.splitdrive(path)[0]
135 if (os.path.splitdrive(path)[0]
136 or parts[0].lower() in ('.hg', '.hg.', '')
136 or parts[0].lower() in ('.hg', '.hg.', '')
137 or os.pardir in parts):
137 or os.pardir in parts):
138 raise util.Abort(_("path contains illegal component: %s") % path)
138 raise util.Abort(_("path contains illegal component: %s") % path)
139 if '.hg' in path.lower():
139 if '.hg' in path.lower():
140 lparts = [p.lower() for p in parts]
140 lparts = [p.lower() for p in parts]
141 for p in '.hg', '.hg.':
141 for p in '.hg', '.hg.':
142 if p in lparts[1:]:
142 if p in lparts[1:]:
143 pos = lparts.index(p)
143 pos = lparts.index(p)
144 base = os.path.join(*parts[:pos])
144 base = os.path.join(*parts[:pos])
145 raise util.Abort(_("path '%s' is inside nested repo %r")
145 raise util.Abort(_("path '%s' is inside nested repo %r")
146 % (path, base))
146 % (path, base))
147
147
148 normparts = util.splitpath(normpath)
148 normparts = util.splitpath(normpath)
149 assert len(parts) == len(normparts)
149 assert len(parts) == len(normparts)
150
150
151 parts.pop()
151 parts.pop()
152 normparts.pop()
152 normparts.pop()
153 prefixes = []
153 prefixes = []
154 while parts:
154 while parts:
155 prefix = os.sep.join(parts)
155 prefix = os.sep.join(parts)
156 normprefix = os.sep.join(normparts)
156 normprefix = os.sep.join(normparts)
157 if normprefix in self.auditeddir:
157 if normprefix in self.auditeddir:
158 break
158 break
159 curpath = os.path.join(self.root, prefix)
159 curpath = os.path.join(self.root, prefix)
160 try:
160 try:
161 st = os.lstat(curpath)
161 st = os.lstat(curpath)
162 except OSError, err:
162 except OSError, err:
163 # EINVAL can be raised as invalid path syntax under win32.
163 # EINVAL can be raised as invalid path syntax under win32.
164 # They must be ignored for patterns can be checked too.
164 # They must be ignored for patterns can be checked too.
165 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
165 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
166 raise
166 raise
167 else:
167 else:
168 if stat.S_ISLNK(st.st_mode):
168 if stat.S_ISLNK(st.st_mode):
169 raise util.Abort(
169 raise util.Abort(
170 _('path %r traverses symbolic link %r')
170 _('path %r traverses symbolic link %r')
171 % (path, prefix))
171 % (path, prefix))
172 elif (stat.S_ISDIR(st.st_mode) and
172 elif (stat.S_ISDIR(st.st_mode) and
173 os.path.isdir(os.path.join(curpath, '.hg'))):
173 os.path.isdir(os.path.join(curpath, '.hg'))):
174 if not self.callback or not self.callback(curpath):
174 if not self.callback or not self.callback(curpath):
175 raise util.Abort(_("path '%s' is inside nested "
175 raise util.Abort(_("path '%s' is inside nested "
176 "repo %r")
176 "repo %r")
177 % (path, prefix))
177 % (path, prefix))
178 prefixes.append(normprefix)
178 prefixes.append(normprefix)
179 parts.pop()
179 parts.pop()
180 normparts.pop()
180 normparts.pop()
181
181
182 self.audited.add(normpath)
182 self.audited.add(normpath)
183 # only add prefixes to the cache after checking everything: we don't
183 # only add prefixes to the cache after checking everything: we don't
184 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
184 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
185 self.auditeddir.update(prefixes)
185 self.auditeddir.update(prefixes)
186
186
187 def check(self, path):
188 try:
189 self(path)
190 return True
191 except (OSError, util.Abort):
192 return False
193
187 class abstractvfs(object):
194 class abstractvfs(object):
188 """Abstract base class; cannot be instantiated"""
195 """Abstract base class; cannot be instantiated"""
189
196
190 def __init__(self, *args, **kwargs):
197 def __init__(self, *args, **kwargs):
191 '''Prevent instantiation; don't call this from subclasses.'''
198 '''Prevent instantiation; don't call this from subclasses.'''
192 raise NotImplementedError('attempted instantiating ' + str(type(self)))
199 raise NotImplementedError('attempted instantiating ' + str(type(self)))
193
200
194 def tryread(self, path):
201 def tryread(self, path):
195 '''gracefully return an empty string for missing files'''
202 '''gracefully return an empty string for missing files'''
196 try:
203 try:
197 return self.read(path)
204 return self.read(path)
198 except IOError, inst:
205 except IOError, inst:
199 if inst.errno != errno.ENOENT:
206 if inst.errno != errno.ENOENT:
200 raise
207 raise
201 return ""
208 return ""
202
209
203 def read(self, path):
210 def read(self, path):
204 fp = self(path, 'rb')
211 fp = self(path, 'rb')
205 try:
212 try:
206 return fp.read()
213 return fp.read()
207 finally:
214 finally:
208 fp.close()
215 fp.close()
209
216
210 def write(self, path, data):
217 def write(self, path, data):
211 fp = self(path, 'wb')
218 fp = self(path, 'wb')
212 try:
219 try:
213 return fp.write(data)
220 return fp.write(data)
214 finally:
221 finally:
215 fp.close()
222 fp.close()
216
223
217 def append(self, path, data):
224 def append(self, path, data):
218 fp = self(path, 'ab')
225 fp = self(path, 'ab')
219 try:
226 try:
220 return fp.write(data)
227 return fp.write(data)
221 finally:
228 finally:
222 fp.close()
229 fp.close()
223
230
224 def exists(self, path=None):
231 def exists(self, path=None):
225 return os.path.exists(self.join(path))
232 return os.path.exists(self.join(path))
226
233
227 def isdir(self, path=None):
234 def isdir(self, path=None):
228 return os.path.isdir(self.join(path))
235 return os.path.isdir(self.join(path))
229
236
230 def makedir(self, path=None, notindexed=True):
237 def makedir(self, path=None, notindexed=True):
231 return util.makedir(self.join(path), notindexed)
238 return util.makedir(self.join(path), notindexed)
232
239
233 def makedirs(self, path=None, mode=None):
240 def makedirs(self, path=None, mode=None):
234 return util.makedirs(self.join(path), mode)
241 return util.makedirs(self.join(path), mode)
235
242
236 def mkdir(self, path=None):
243 def mkdir(self, path=None):
237 return os.mkdir(self.join(path))
244 return os.mkdir(self.join(path))
238
245
239 def readdir(self, path=None, stat=None, skip=None):
246 def readdir(self, path=None, stat=None, skip=None):
240 return osutil.listdir(self.join(path), stat, skip)
247 return osutil.listdir(self.join(path), stat, skip)
241
248
242 def stat(self, path=None):
249 def stat(self, path=None):
243 return os.stat(self.join(path))
250 return os.stat(self.join(path))
244
251
245 class vfs(abstractvfs):
252 class vfs(abstractvfs):
246 '''Operate files relative to a base directory
253 '''Operate files relative to a base directory
247
254
248 This class is used to hide the details of COW semantics and
255 This class is used to hide the details of COW semantics and
249 remote file access from higher level code.
256 remote file access from higher level code.
250 '''
257 '''
251 def __init__(self, base, audit=True, expand=False):
258 def __init__(self, base, audit=True, expand=False):
252 if expand:
259 if expand:
253 base = os.path.realpath(util.expandpath(base))
260 base = os.path.realpath(util.expandpath(base))
254 self.base = base
261 self.base = base
255 self._setmustaudit(audit)
262 self._setmustaudit(audit)
256 self.createmode = None
263 self.createmode = None
257 self._trustnlink = None
264 self._trustnlink = None
258
265
259 def _getmustaudit(self):
266 def _getmustaudit(self):
260 return self._audit
267 return self._audit
261
268
262 def _setmustaudit(self, onoff):
269 def _setmustaudit(self, onoff):
263 self._audit = onoff
270 self._audit = onoff
264 if onoff:
271 if onoff:
265 self.audit = pathauditor(self.base)
272 self.audit = pathauditor(self.base)
266 else:
273 else:
267 self.audit = util.always
274 self.audit = util.always
268
275
269 mustaudit = property(_getmustaudit, _setmustaudit)
276 mustaudit = property(_getmustaudit, _setmustaudit)
270
277
271 @util.propertycache
278 @util.propertycache
272 def _cansymlink(self):
279 def _cansymlink(self):
273 return util.checklink(self.base)
280 return util.checklink(self.base)
274
281
275 @util.propertycache
282 @util.propertycache
276 def _chmod(self):
283 def _chmod(self):
277 return util.checkexec(self.base)
284 return util.checkexec(self.base)
278
285
279 def _fixfilemode(self, name):
286 def _fixfilemode(self, name):
280 if self.createmode is None or not self._chmod:
287 if self.createmode is None or not self._chmod:
281 return
288 return
282 os.chmod(name, self.createmode & 0666)
289 os.chmod(name, self.createmode & 0666)
283
290
284 def __call__(self, path, mode="r", text=False, atomictemp=False):
291 def __call__(self, path, mode="r", text=False, atomictemp=False):
285 if self._audit:
292 if self._audit:
286 r = util.checkosfilename(path)
293 r = util.checkosfilename(path)
287 if r:
294 if r:
288 raise util.Abort("%s: %r" % (r, path))
295 raise util.Abort("%s: %r" % (r, path))
289 self.audit(path)
296 self.audit(path)
290 f = self.join(path)
297 f = self.join(path)
291
298
292 if not text and "b" not in mode:
299 if not text and "b" not in mode:
293 mode += "b" # for that other OS
300 mode += "b" # for that other OS
294
301
295 nlink = -1
302 nlink = -1
296 if mode not in ('r', 'rb'):
303 if mode not in ('r', 'rb'):
297 dirname, basename = util.split(f)
304 dirname, basename = util.split(f)
298 # If basename is empty, then the path is malformed because it points
305 # If basename is empty, then the path is malformed because it points
299 # to a directory. Let the posixfile() call below raise IOError.
306 # to a directory. Let the posixfile() call below raise IOError.
300 if basename:
307 if basename:
301 if atomictemp:
308 if atomictemp:
302 if not os.path.isdir(dirname):
309 if not os.path.isdir(dirname):
303 util.makedirs(dirname, self.createmode)
310 util.makedirs(dirname, self.createmode)
304 return util.atomictempfile(f, mode, self.createmode)
311 return util.atomictempfile(f, mode, self.createmode)
305 try:
312 try:
306 if 'w' in mode:
313 if 'w' in mode:
307 util.unlink(f)
314 util.unlink(f)
308 nlink = 0
315 nlink = 0
309 else:
316 else:
310 # nlinks() may behave differently for files on Windows
317 # nlinks() may behave differently for files on Windows
311 # shares if the file is open.
318 # shares if the file is open.
312 fd = util.posixfile(f)
319 fd = util.posixfile(f)
313 nlink = util.nlinks(f)
320 nlink = util.nlinks(f)
314 if nlink < 1:
321 if nlink < 1:
315 nlink = 2 # force mktempcopy (issue1922)
322 nlink = 2 # force mktempcopy (issue1922)
316 fd.close()
323 fd.close()
317 except (OSError, IOError), e:
324 except (OSError, IOError), e:
318 if e.errno != errno.ENOENT:
325 if e.errno != errno.ENOENT:
319 raise
326 raise
320 nlink = 0
327 nlink = 0
321 if not os.path.isdir(dirname):
328 if not os.path.isdir(dirname):
322 util.makedirs(dirname, self.createmode)
329 util.makedirs(dirname, self.createmode)
323 if nlink > 0:
330 if nlink > 0:
324 if self._trustnlink is None:
331 if self._trustnlink is None:
325 self._trustnlink = nlink > 1 or util.checknlink(f)
332 self._trustnlink = nlink > 1 or util.checknlink(f)
326 if nlink > 1 or not self._trustnlink:
333 if nlink > 1 or not self._trustnlink:
327 util.rename(util.mktempcopy(f), f)
334 util.rename(util.mktempcopy(f), f)
328 fp = util.posixfile(f, mode)
335 fp = util.posixfile(f, mode)
329 if nlink == 0:
336 if nlink == 0:
330 self._fixfilemode(f)
337 self._fixfilemode(f)
331 return fp
338 return fp
332
339
333 def symlink(self, src, dst):
340 def symlink(self, src, dst):
334 self.audit(dst)
341 self.audit(dst)
335 linkname = self.join(dst)
342 linkname = self.join(dst)
336 try:
343 try:
337 os.unlink(linkname)
344 os.unlink(linkname)
338 except OSError:
345 except OSError:
339 pass
346 pass
340
347
341 dirname = os.path.dirname(linkname)
348 dirname = os.path.dirname(linkname)
342 if not os.path.exists(dirname):
349 if not os.path.exists(dirname):
343 util.makedirs(dirname, self.createmode)
350 util.makedirs(dirname, self.createmode)
344
351
345 if self._cansymlink:
352 if self._cansymlink:
346 try:
353 try:
347 os.symlink(src, linkname)
354 os.symlink(src, linkname)
348 except OSError, err:
355 except OSError, err:
349 raise OSError(err.errno, _('could not symlink to %r: %s') %
356 raise OSError(err.errno, _('could not symlink to %r: %s') %
350 (src, err.strerror), linkname)
357 (src, err.strerror), linkname)
351 else:
358 else:
352 self.write(dst, src)
359 self.write(dst, src)
353
360
354 def join(self, path):
361 def join(self, path):
355 if path:
362 if path:
356 return os.path.join(self.base, path)
363 return os.path.join(self.base, path)
357 else:
364 else:
358 return self.base
365 return self.base
359
366
360 opener = vfs
367 opener = vfs
361
368
362 class auditvfs(object):
369 class auditvfs(object):
363 def __init__(self, vfs):
370 def __init__(self, vfs):
364 self.vfs = vfs
371 self.vfs = vfs
365
372
366 def _getmustaudit(self):
373 def _getmustaudit(self):
367 return self.vfs.mustaudit
374 return self.vfs.mustaudit
368
375
369 def _setmustaudit(self, onoff):
376 def _setmustaudit(self, onoff):
370 self.vfs.mustaudit = onoff
377 self.vfs.mustaudit = onoff
371
378
372 mustaudit = property(_getmustaudit, _setmustaudit)
379 mustaudit = property(_getmustaudit, _setmustaudit)
373
380
374 class filtervfs(abstractvfs, auditvfs):
381 class filtervfs(abstractvfs, auditvfs):
375 '''Wrapper vfs for filtering filenames with a function.'''
382 '''Wrapper vfs for filtering filenames with a function.'''
376
383
377 def __init__(self, vfs, filter):
384 def __init__(self, vfs, filter):
378 auditvfs.__init__(self, vfs)
385 auditvfs.__init__(self, vfs)
379 self._filter = filter
386 self._filter = filter
380
387
381 def __call__(self, path, *args, **kwargs):
388 def __call__(self, path, *args, **kwargs):
382 return self.vfs(self._filter(path), *args, **kwargs)
389 return self.vfs(self._filter(path), *args, **kwargs)
383
390
384 def join(self, path):
391 def join(self, path):
385 if path:
392 if path:
386 return self.vfs.join(self._filter(path))
393 return self.vfs.join(self._filter(path))
387 else:
394 else:
388 return self.vfs.join(path)
395 return self.vfs.join(path)
389
396
390 filteropener = filtervfs
397 filteropener = filtervfs
391
398
392 class readonlyvfs(abstractvfs, auditvfs):
399 class readonlyvfs(abstractvfs, auditvfs):
393 '''Wrapper vfs preventing any writing.'''
400 '''Wrapper vfs preventing any writing.'''
394
401
395 def __init__(self, vfs):
402 def __init__(self, vfs):
396 auditvfs.__init__(self, vfs)
403 auditvfs.__init__(self, vfs)
397
404
398 def __call__(self, path, mode='r', *args, **kw):
405 def __call__(self, path, mode='r', *args, **kw):
399 if mode not in ('r', 'rb'):
406 if mode not in ('r', 'rb'):
400 raise util.Abort('this vfs is read only')
407 raise util.Abort('this vfs is read only')
401 return self.vfs(path, mode, *args, **kw)
408 return self.vfs(path, mode, *args, **kw)
402
409
403
410
404 def canonpath(root, cwd, myname, auditor=None):
411 def canonpath(root, cwd, myname, auditor=None):
405 '''return the canonical path of myname, given cwd and root'''
412 '''return the canonical path of myname, given cwd and root'''
406 if util.endswithsep(root):
413 if util.endswithsep(root):
407 rootsep = root
414 rootsep = root
408 else:
415 else:
409 rootsep = root + os.sep
416 rootsep = root + os.sep
410 name = myname
417 name = myname
411 if not os.path.isabs(name):
418 if not os.path.isabs(name):
412 name = os.path.join(root, cwd, name)
419 name = os.path.join(root, cwd, name)
413 name = os.path.normpath(name)
420 name = os.path.normpath(name)
414 if auditor is None:
421 if auditor is None:
415 auditor = pathauditor(root)
422 auditor = pathauditor(root)
416 if name != rootsep and name.startswith(rootsep):
423 if name != rootsep and name.startswith(rootsep):
417 name = name[len(rootsep):]
424 name = name[len(rootsep):]
418 auditor(name)
425 auditor(name)
419 return util.pconvert(name)
426 return util.pconvert(name)
420 elif name == root:
427 elif name == root:
421 return ''
428 return ''
422 else:
429 else:
423 # Determine whether `name' is in the hierarchy at or beneath `root',
430 # Determine whether `name' is in the hierarchy at or beneath `root',
424 # by iterating name=dirname(name) until that causes no change (can't
431 # by iterating name=dirname(name) until that causes no change (can't
425 # check name == '/', because that doesn't work on windows). The list
432 # check name == '/', because that doesn't work on windows). The list
426 # `rel' holds the reversed list of components making up the relative
433 # `rel' holds the reversed list of components making up the relative
427 # file name we want.
434 # file name we want.
428 rel = []
435 rel = []
429 while True:
436 while True:
430 try:
437 try:
431 s = util.samefile(name, root)
438 s = util.samefile(name, root)
432 except OSError:
439 except OSError:
433 s = False
440 s = False
434 if s:
441 if s:
435 if not rel:
442 if not rel:
436 # name was actually the same as root (maybe a symlink)
443 # name was actually the same as root (maybe a symlink)
437 return ''
444 return ''
438 rel.reverse()
445 rel.reverse()
439 name = os.path.join(*rel)
446 name = os.path.join(*rel)
440 auditor(name)
447 auditor(name)
441 return util.pconvert(name)
448 return util.pconvert(name)
442 dirname, basename = util.split(name)
449 dirname, basename = util.split(name)
443 rel.append(basename)
450 rel.append(basename)
444 if dirname == name:
451 if dirname == name:
445 break
452 break
446 name = dirname
453 name = dirname
447
454
448 raise util.Abort(_("%s not under root '%s'") % (myname, root))
455 raise util.Abort(_("%s not under root '%s'") % (myname, root))
449
456
450 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
457 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
451 '''yield every hg repository under path, always recursively.
458 '''yield every hg repository under path, always recursively.
452 The recurse flag will only control recursion into repo working dirs'''
459 The recurse flag will only control recursion into repo working dirs'''
453 def errhandler(err):
460 def errhandler(err):
454 if err.filename == path:
461 if err.filename == path:
455 raise err
462 raise err
456 samestat = getattr(os.path, 'samestat', None)
463 samestat = getattr(os.path, 'samestat', None)
457 if followsym and samestat is not None:
464 if followsym and samestat is not None:
458 def adddir(dirlst, dirname):
465 def adddir(dirlst, dirname):
459 match = False
466 match = False
460 dirstat = os.stat(dirname)
467 dirstat = os.stat(dirname)
461 for lstdirstat in dirlst:
468 for lstdirstat in dirlst:
462 if samestat(dirstat, lstdirstat):
469 if samestat(dirstat, lstdirstat):
463 match = True
470 match = True
464 break
471 break
465 if not match:
472 if not match:
466 dirlst.append(dirstat)
473 dirlst.append(dirstat)
467 return not match
474 return not match
468 else:
475 else:
469 followsym = False
476 followsym = False
470
477
471 if (seen_dirs is None) and followsym:
478 if (seen_dirs is None) and followsym:
472 seen_dirs = []
479 seen_dirs = []
473 adddir(seen_dirs, path)
480 adddir(seen_dirs, path)
474 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
481 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
475 dirs.sort()
482 dirs.sort()
476 if '.hg' in dirs:
483 if '.hg' in dirs:
477 yield root # found a repository
484 yield root # found a repository
478 qroot = os.path.join(root, '.hg', 'patches')
485 qroot = os.path.join(root, '.hg', 'patches')
479 if os.path.isdir(os.path.join(qroot, '.hg')):
486 if os.path.isdir(os.path.join(qroot, '.hg')):
480 yield qroot # we have a patch queue repo here
487 yield qroot # we have a patch queue repo here
481 if recurse:
488 if recurse:
482 # avoid recursing inside the .hg directory
489 # avoid recursing inside the .hg directory
483 dirs.remove('.hg')
490 dirs.remove('.hg')
484 else:
491 else:
485 dirs[:] = [] # don't descend further
492 dirs[:] = [] # don't descend further
486 elif followsym:
493 elif followsym:
487 newdirs = []
494 newdirs = []
488 for d in dirs:
495 for d in dirs:
489 fname = os.path.join(root, d)
496 fname = os.path.join(root, d)
490 if adddir(seen_dirs, fname):
497 if adddir(seen_dirs, fname):
491 if os.path.islink(fname):
498 if os.path.islink(fname):
492 for hgname in walkrepos(fname, True, seen_dirs):
499 for hgname in walkrepos(fname, True, seen_dirs):
493 yield hgname
500 yield hgname
494 else:
501 else:
495 newdirs.append(d)
502 newdirs.append(d)
496 dirs[:] = newdirs
503 dirs[:] = newdirs
497
504
498 def osrcpath():
505 def osrcpath():
499 '''return default os-specific hgrc search path'''
506 '''return default os-specific hgrc search path'''
500 path = systemrcpath()
507 path = systemrcpath()
501 path.extend(userrcpath())
508 path.extend(userrcpath())
502 path = [os.path.normpath(f) for f in path]
509 path = [os.path.normpath(f) for f in path]
503 return path
510 return path
504
511
505 _rcpath = None
512 _rcpath = None
506
513
507 def rcpath():
514 def rcpath():
508 '''return hgrc search path. if env var HGRCPATH is set, use it.
515 '''return hgrc search path. if env var HGRCPATH is set, use it.
509 for each item in path, if directory, use files ending in .rc,
516 for each item in path, if directory, use files ending in .rc,
510 else use item.
517 else use item.
511 make HGRCPATH empty to only look in .hg/hgrc of current repo.
518 make HGRCPATH empty to only look in .hg/hgrc of current repo.
512 if no HGRCPATH, use default os-specific path.'''
519 if no HGRCPATH, use default os-specific path.'''
513 global _rcpath
520 global _rcpath
514 if _rcpath is None:
521 if _rcpath is None:
515 if 'HGRCPATH' in os.environ:
522 if 'HGRCPATH' in os.environ:
516 _rcpath = []
523 _rcpath = []
517 for p in os.environ['HGRCPATH'].split(os.pathsep):
524 for p in os.environ['HGRCPATH'].split(os.pathsep):
518 if not p:
525 if not p:
519 continue
526 continue
520 p = util.expandpath(p)
527 p = util.expandpath(p)
521 if os.path.isdir(p):
528 if os.path.isdir(p):
522 for f, kind in osutil.listdir(p):
529 for f, kind in osutil.listdir(p):
523 if f.endswith('.rc'):
530 if f.endswith('.rc'):
524 _rcpath.append(os.path.join(p, f))
531 _rcpath.append(os.path.join(p, f))
525 else:
532 else:
526 _rcpath.append(p)
533 _rcpath.append(p)
527 else:
534 else:
528 _rcpath = osrcpath()
535 _rcpath = osrcpath()
529 return _rcpath
536 return _rcpath
530
537
531 if os.name != 'nt':
538 if os.name != 'nt':
532
539
533 def rcfiles(path):
540 def rcfiles(path):
534 rcs = [os.path.join(path, 'hgrc')]
541 rcs = [os.path.join(path, 'hgrc')]
535 rcdir = os.path.join(path, 'hgrc.d')
542 rcdir = os.path.join(path, 'hgrc.d')
536 try:
543 try:
537 rcs.extend([os.path.join(rcdir, f)
544 rcs.extend([os.path.join(rcdir, f)
538 for f, kind in osutil.listdir(rcdir)
545 for f, kind in osutil.listdir(rcdir)
539 if f.endswith(".rc")])
546 if f.endswith(".rc")])
540 except OSError:
547 except OSError:
541 pass
548 pass
542 return rcs
549 return rcs
543
550
544 def systemrcpath():
551 def systemrcpath():
545 path = []
552 path = []
546 if sys.platform == 'plan9':
553 if sys.platform == 'plan9':
547 root = 'lib/mercurial'
554 root = 'lib/mercurial'
548 else:
555 else:
549 root = 'etc/mercurial'
556 root = 'etc/mercurial'
550 # old mod_python does not set sys.argv
557 # old mod_python does not set sys.argv
551 if len(getattr(sys, 'argv', [])) > 0:
558 if len(getattr(sys, 'argv', [])) > 0:
552 p = os.path.dirname(os.path.dirname(sys.argv[0]))
559 p = os.path.dirname(os.path.dirname(sys.argv[0]))
553 path.extend(rcfiles(os.path.join(p, root)))
560 path.extend(rcfiles(os.path.join(p, root)))
554 path.extend(rcfiles('/' + root))
561 path.extend(rcfiles('/' + root))
555 return path
562 return path
556
563
557 def userrcpath():
564 def userrcpath():
558 if sys.platform == 'plan9':
565 if sys.platform == 'plan9':
559 return [os.environ['home'] + '/lib/hgrc']
566 return [os.environ['home'] + '/lib/hgrc']
560 else:
567 else:
561 return [os.path.expanduser('~/.hgrc')]
568 return [os.path.expanduser('~/.hgrc')]
562
569
563 else:
570 else:
564
571
565 import _winreg
572 import _winreg
566
573
567 def systemrcpath():
574 def systemrcpath():
568 '''return default os-specific hgrc search path'''
575 '''return default os-specific hgrc search path'''
569 rcpath = []
576 rcpath = []
570 filename = util.executablepath()
577 filename = util.executablepath()
571 # Use mercurial.ini found in directory with hg.exe
578 # Use mercurial.ini found in directory with hg.exe
572 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
579 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
573 if os.path.isfile(progrc):
580 if os.path.isfile(progrc):
574 rcpath.append(progrc)
581 rcpath.append(progrc)
575 return rcpath
582 return rcpath
576 # Use hgrc.d found in directory with hg.exe
583 # Use hgrc.d found in directory with hg.exe
577 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
584 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
578 if os.path.isdir(progrcd):
585 if os.path.isdir(progrcd):
579 for f, kind in osutil.listdir(progrcd):
586 for f, kind in osutil.listdir(progrcd):
580 if f.endswith('.rc'):
587 if f.endswith('.rc'):
581 rcpath.append(os.path.join(progrcd, f))
588 rcpath.append(os.path.join(progrcd, f))
582 return rcpath
589 return rcpath
583 # else look for a system rcpath in the registry
590 # else look for a system rcpath in the registry
584 value = util.lookupreg('SOFTWARE\\Mercurial', None,
591 value = util.lookupreg('SOFTWARE\\Mercurial', None,
585 _winreg.HKEY_LOCAL_MACHINE)
592 _winreg.HKEY_LOCAL_MACHINE)
586 if not isinstance(value, str) or not value:
593 if not isinstance(value, str) or not value:
587 return rcpath
594 return rcpath
588 value = util.localpath(value)
595 value = util.localpath(value)
589 for p in value.split(os.pathsep):
596 for p in value.split(os.pathsep):
590 if p.lower().endswith('mercurial.ini'):
597 if p.lower().endswith('mercurial.ini'):
591 rcpath.append(p)
598 rcpath.append(p)
592 elif os.path.isdir(p):
599 elif os.path.isdir(p):
593 for f, kind in osutil.listdir(p):
600 for f, kind in osutil.listdir(p):
594 if f.endswith('.rc'):
601 if f.endswith('.rc'):
595 rcpath.append(os.path.join(p, f))
602 rcpath.append(os.path.join(p, f))
596 return rcpath
603 return rcpath
597
604
598 def userrcpath():
605 def userrcpath():
599 '''return os-specific hgrc search path to the user dir'''
606 '''return os-specific hgrc search path to the user dir'''
600 home = os.path.expanduser('~')
607 home = os.path.expanduser('~')
601 path = [os.path.join(home, 'mercurial.ini'),
608 path = [os.path.join(home, 'mercurial.ini'),
602 os.path.join(home, '.hgrc')]
609 os.path.join(home, '.hgrc')]
603 userprofile = os.environ.get('USERPROFILE')
610 userprofile = os.environ.get('USERPROFILE')
604 if userprofile:
611 if userprofile:
605 path.append(os.path.join(userprofile, 'mercurial.ini'))
612 path.append(os.path.join(userprofile, 'mercurial.ini'))
606 path.append(os.path.join(userprofile, '.hgrc'))
613 path.append(os.path.join(userprofile, '.hgrc'))
607 return path
614 return path
608
615
609 def revsingle(repo, revspec, default='.'):
616 def revsingle(repo, revspec, default='.'):
610 if not revspec:
617 if not revspec:
611 return repo[default]
618 return repo[default]
612
619
613 l = revrange(repo, [revspec])
620 l = revrange(repo, [revspec])
614 if len(l) < 1:
621 if len(l) < 1:
615 raise util.Abort(_('empty revision set'))
622 raise util.Abort(_('empty revision set'))
616 return repo[l[-1]]
623 return repo[l[-1]]
617
624
618 def revpair(repo, revs):
625 def revpair(repo, revs):
619 if not revs:
626 if not revs:
620 return repo.dirstate.p1(), None
627 return repo.dirstate.p1(), None
621
628
622 l = revrange(repo, revs)
629 l = revrange(repo, revs)
623
630
624 if len(l) == 0:
631 if len(l) == 0:
625 if revs:
632 if revs:
626 raise util.Abort(_('empty revision range'))
633 raise util.Abort(_('empty revision range'))
627 return repo.dirstate.p1(), None
634 return repo.dirstate.p1(), None
628
635
629 if len(l) == 1 and len(revs) == 1 and _revrangesep not in revs[0]:
636 if len(l) == 1 and len(revs) == 1 and _revrangesep not in revs[0]:
630 return repo.lookup(l[0]), None
637 return repo.lookup(l[0]), None
631
638
632 return repo.lookup(l[0]), repo.lookup(l[-1])
639 return repo.lookup(l[0]), repo.lookup(l[-1])
633
640
634 _revrangesep = ':'
641 _revrangesep = ':'
635
642
636 def revrange(repo, revs):
643 def revrange(repo, revs):
637 """Yield revision as strings from a list of revision specifications."""
644 """Yield revision as strings from a list of revision specifications."""
638
645
639 def revfix(repo, val, defval):
646 def revfix(repo, val, defval):
640 if not val and val != 0 and defval is not None:
647 if not val and val != 0 and defval is not None:
641 return defval
648 return defval
642 return repo[val].rev()
649 return repo[val].rev()
643
650
644 seen, l = set(), []
651 seen, l = set(), []
645 for spec in revs:
652 for spec in revs:
646 if l and not seen:
653 if l and not seen:
647 seen = set(l)
654 seen = set(l)
648 # attempt to parse old-style ranges first to deal with
655 # attempt to parse old-style ranges first to deal with
649 # things like old-tag which contain query metacharacters
656 # things like old-tag which contain query metacharacters
650 try:
657 try:
651 if isinstance(spec, int):
658 if isinstance(spec, int):
652 seen.add(spec)
659 seen.add(spec)
653 l.append(spec)
660 l.append(spec)
654 continue
661 continue
655
662
656 if _revrangesep in spec:
663 if _revrangesep in spec:
657 start, end = spec.split(_revrangesep, 1)
664 start, end = spec.split(_revrangesep, 1)
658 start = revfix(repo, start, 0)
665 start = revfix(repo, start, 0)
659 end = revfix(repo, end, len(repo) - 1)
666 end = revfix(repo, end, len(repo) - 1)
660 if end == nullrev and start <= 0:
667 if end == nullrev and start <= 0:
661 start = nullrev
668 start = nullrev
662 rangeiter = repo.changelog.revs(start, end)
669 rangeiter = repo.changelog.revs(start, end)
663 if not seen and not l:
670 if not seen and not l:
664 # by far the most common case: revs = ["-1:0"]
671 # by far the most common case: revs = ["-1:0"]
665 l = list(rangeiter)
672 l = list(rangeiter)
666 # defer syncing seen until next iteration
673 # defer syncing seen until next iteration
667 continue
674 continue
668 newrevs = set(rangeiter)
675 newrevs = set(rangeiter)
669 if seen:
676 if seen:
670 newrevs.difference_update(seen)
677 newrevs.difference_update(seen)
671 seen.update(newrevs)
678 seen.update(newrevs)
672 else:
679 else:
673 seen = newrevs
680 seen = newrevs
674 l.extend(sorted(newrevs, reverse=start > end))
681 l.extend(sorted(newrevs, reverse=start > end))
675 continue
682 continue
676 elif spec and spec in repo: # single unquoted rev
683 elif spec and spec in repo: # single unquoted rev
677 rev = revfix(repo, spec, None)
684 rev = revfix(repo, spec, None)
678 if rev in seen:
685 if rev in seen:
679 continue
686 continue
680 seen.add(rev)
687 seen.add(rev)
681 l.append(rev)
688 l.append(rev)
682 continue
689 continue
683 except error.RepoLookupError:
690 except error.RepoLookupError:
684 pass
691 pass
685
692
686 # fall through to new-style queries if old-style fails
693 # fall through to new-style queries if old-style fails
687 m = revset.match(repo.ui, spec)
694 m = revset.match(repo.ui, spec)
688 dl = [r for r in m(repo, list(repo)) if r not in seen]
695 dl = [r for r in m(repo, list(repo)) if r not in seen]
689 l.extend(dl)
696 l.extend(dl)
690 seen.update(dl)
697 seen.update(dl)
691
698
692 return l
699 return l
693
700
694 def expandpats(pats):
701 def expandpats(pats):
695 if not util.expandglobs:
702 if not util.expandglobs:
696 return list(pats)
703 return list(pats)
697 ret = []
704 ret = []
698 for p in pats:
705 for p in pats:
699 kind, name = matchmod._patsplit(p, None)
706 kind, name = matchmod._patsplit(p, None)
700 if kind is None:
707 if kind is None:
701 try:
708 try:
702 globbed = glob.glob(name)
709 globbed = glob.glob(name)
703 except re.error:
710 except re.error:
704 globbed = [name]
711 globbed = [name]
705 if globbed:
712 if globbed:
706 ret.extend(globbed)
713 ret.extend(globbed)
707 continue
714 continue
708 ret.append(p)
715 ret.append(p)
709 return ret
716 return ret
710
717
711 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath'):
718 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath'):
712 if pats == ("",):
719 if pats == ("",):
713 pats = []
720 pats = []
714 if not globbed and default == 'relpath':
721 if not globbed and default == 'relpath':
715 pats = expandpats(pats or [])
722 pats = expandpats(pats or [])
716
723
717 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
724 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
718 default)
725 default)
719 def badfn(f, msg):
726 def badfn(f, msg):
720 ctx._repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
727 ctx._repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
721 m.bad = badfn
728 m.bad = badfn
722 return m, pats
729 return m, pats
723
730
724 def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
731 def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
725 return matchandpats(ctx, pats, opts, globbed, default)[0]
732 return matchandpats(ctx, pats, opts, globbed, default)[0]
726
733
727 def matchall(repo):
734 def matchall(repo):
728 return matchmod.always(repo.root, repo.getcwd())
735 return matchmod.always(repo.root, repo.getcwd())
729
736
730 def matchfiles(repo, files):
737 def matchfiles(repo, files):
731 return matchmod.exact(repo.root, repo.getcwd(), files)
738 return matchmod.exact(repo.root, repo.getcwd(), files)
732
739
733 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
740 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
734 if dry_run is None:
741 if dry_run is None:
735 dry_run = opts.get('dry_run')
742 dry_run = opts.get('dry_run')
736 if similarity is None:
743 if similarity is None:
737 similarity = float(opts.get('similarity') or 0)
744 similarity = float(opts.get('similarity') or 0)
738 # we'd use status here, except handling of symlinks and ignore is tricky
745 # we'd use status here, except handling of symlinks and ignore is tricky
739 added, unknown, deleted, removed = [], [], [], []
746 added, unknown, deleted, removed = [], [], [], []
740 audit_path = pathauditor(repo.root)
747 audit_path = pathauditor(repo.root)
741 m = match(repo[None], pats, opts)
748 m = match(repo[None], pats, opts)
742 rejected = []
749 rejected = []
743 m.bad = lambda x, y: rejected.append(x)
750 m.bad = lambda x, y: rejected.append(x)
744
751
745 ctx = repo[None]
752 ctx = repo[None]
746 walkresults = repo.dirstate.walk(m, sorted(ctx.substate), True, False)
753 walkresults = repo.dirstate.walk(m, sorted(ctx.substate), True, False)
747 for abs in sorted(walkresults):
754 for abs in sorted(walkresults):
748 good = True
749 try:
750 audit_path(abs)
751 except (OSError, util.Abort):
752 good = False
753
754 st = walkresults[abs]
755 st = walkresults[abs]
755 dstate = repo.dirstate[abs]
756 dstate = repo.dirstate[abs]
756 if good and dstate == '?':
757 if dstate == '?' and audit_path.check(abs):
757 unknown.append(abs)
758 unknown.append(abs)
758 if repo.ui.verbose or not m.exact(abs):
759 if repo.ui.verbose or not m.exact(abs):
759 rel = m.rel(abs)
760 rel = m.rel(abs)
760 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
761 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
761 elif (dstate != 'r' and
762 elif (dstate != 'r' and (not st or
762 (not good or not st or
763 (stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode)))):
763 (stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode)))):
764 deleted.append(abs)
764 deleted.append(abs)
765 if repo.ui.verbose or not m.exact(abs):
765 if repo.ui.verbose or not m.exact(abs):
766 rel = m.rel(abs)
766 rel = m.rel(abs)
767 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
767 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
768 # for finding renames
768 # for finding renames
769 elif dstate == 'r':
769 elif dstate == 'r':
770 removed.append(abs)
770 removed.append(abs)
771 elif dstate == 'a':
771 elif dstate == 'a':
772 added.append(abs)
772 added.append(abs)
773 copies = {}
773 copies = {}
774 if similarity > 0:
774 if similarity > 0:
775 for old, new, score in similar.findrenames(repo,
775 for old, new, score in similar.findrenames(repo,
776 added + unknown, removed + deleted, similarity):
776 added + unknown, removed + deleted, similarity):
777 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
777 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
778 repo.ui.status(_('recording removal of %s as rename to %s '
778 repo.ui.status(_('recording removal of %s as rename to %s '
779 '(%d%% similar)\n') %
779 '(%d%% similar)\n') %
780 (m.rel(old), m.rel(new), score * 100))
780 (m.rel(old), m.rel(new), score * 100))
781 copies[new] = old
781 copies[new] = old
782
782
783 if not dry_run:
783 if not dry_run:
784 wctx = repo[None]
784 wctx = repo[None]
785 wlock = repo.wlock()
785 wlock = repo.wlock()
786 try:
786 try:
787 wctx.forget(deleted)
787 wctx.forget(deleted)
788 wctx.add(unknown)
788 wctx.add(unknown)
789 for new, old in copies.iteritems():
789 for new, old in copies.iteritems():
790 wctx.copy(old, new)
790 wctx.copy(old, new)
791 finally:
791 finally:
792 wlock.release()
792 wlock.release()
793
793
794 for f in rejected:
794 for f in rejected:
795 if f in m.files():
795 if f in m.files():
796 return 1
796 return 1
797 return 0
797 return 0
798
798
799 def updatedir(ui, repo, patches, similarity=0):
799 def updatedir(ui, repo, patches, similarity=0):
800 '''Update dirstate after patch application according to metadata'''
800 '''Update dirstate after patch application according to metadata'''
801 if not patches:
801 if not patches:
802 return []
802 return []
803 copies = []
803 copies = []
804 removes = set()
804 removes = set()
805 cfiles = patches.keys()
805 cfiles = patches.keys()
806 cwd = repo.getcwd()
806 cwd = repo.getcwd()
807 if cwd:
807 if cwd:
808 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
808 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
809 for f in patches:
809 for f in patches:
810 gp = patches[f]
810 gp = patches[f]
811 if not gp:
811 if not gp:
812 continue
812 continue
813 if gp.op == 'RENAME':
813 if gp.op == 'RENAME':
814 copies.append((gp.oldpath, gp.path))
814 copies.append((gp.oldpath, gp.path))
815 removes.add(gp.oldpath)
815 removes.add(gp.oldpath)
816 elif gp.op == 'COPY':
816 elif gp.op == 'COPY':
817 copies.append((gp.oldpath, gp.path))
817 copies.append((gp.oldpath, gp.path))
818 elif gp.op == 'DELETE':
818 elif gp.op == 'DELETE':
819 removes.add(gp.path)
819 removes.add(gp.path)
820
820
821 wctx = repo[None]
821 wctx = repo[None]
822 for src, dst in copies:
822 for src, dst in copies:
823 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
823 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
824 if (not similarity) and removes:
824 if (not similarity) and removes:
825 wctx.remove(sorted(removes), True)
825 wctx.remove(sorted(removes), True)
826
826
827 for f in patches:
827 for f in patches:
828 gp = patches[f]
828 gp = patches[f]
829 if gp and gp.mode:
829 if gp and gp.mode:
830 islink, isexec = gp.mode
830 islink, isexec = gp.mode
831 dst = repo.wjoin(gp.path)
831 dst = repo.wjoin(gp.path)
832 # patch won't create empty files
832 # patch won't create empty files
833 if gp.op == 'ADD' and not os.path.lexists(dst):
833 if gp.op == 'ADD' and not os.path.lexists(dst):
834 flags = (isexec and 'x' or '') + (islink and 'l' or '')
834 flags = (isexec and 'x' or '') + (islink and 'l' or '')
835 repo.wwrite(gp.path, '', flags)
835 repo.wwrite(gp.path, '', flags)
836 util.setflags(dst, islink, isexec)
836 util.setflags(dst, islink, isexec)
837 addremove(repo, cfiles, similarity=similarity)
837 addremove(repo, cfiles, similarity=similarity)
838 files = patches.keys()
838 files = patches.keys()
839 files.extend([r for r in removes if r not in files])
839 files.extend([r for r in removes if r not in files])
840 return sorted(files)
840 return sorted(files)
841
841
842 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
842 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
843 """Update the dirstate to reflect the intent of copying src to dst. For
843 """Update the dirstate to reflect the intent of copying src to dst. For
844 different reasons it might not end with dst being marked as copied from src.
844 different reasons it might not end with dst being marked as copied from src.
845 """
845 """
846 origsrc = repo.dirstate.copied(src) or src
846 origsrc = repo.dirstate.copied(src) or src
847 if dst == origsrc: # copying back a copy?
847 if dst == origsrc: # copying back a copy?
848 if repo.dirstate[dst] not in 'mn' and not dryrun:
848 if repo.dirstate[dst] not in 'mn' and not dryrun:
849 repo.dirstate.normallookup(dst)
849 repo.dirstate.normallookup(dst)
850 else:
850 else:
851 if repo.dirstate[origsrc] == 'a' and origsrc == src:
851 if repo.dirstate[origsrc] == 'a' and origsrc == src:
852 if not ui.quiet:
852 if not ui.quiet:
853 ui.warn(_("%s has not been committed yet, so no copy "
853 ui.warn(_("%s has not been committed yet, so no copy "
854 "data will be stored for %s.\n")
854 "data will be stored for %s.\n")
855 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
855 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
856 if repo.dirstate[dst] in '?r' and not dryrun:
856 if repo.dirstate[dst] in '?r' and not dryrun:
857 wctx.add([dst])
857 wctx.add([dst])
858 elif not dryrun:
858 elif not dryrun:
859 wctx.copy(origsrc, dst)
859 wctx.copy(origsrc, dst)
860
860
861 def readrequires(opener, supported):
861 def readrequires(opener, supported):
862 '''Reads and parses .hg/requires and checks if all entries found
862 '''Reads and parses .hg/requires and checks if all entries found
863 are in the list of supported features.'''
863 are in the list of supported features.'''
864 requirements = set(opener.read("requires").splitlines())
864 requirements = set(opener.read("requires").splitlines())
865 missings = []
865 missings = []
866 for r in requirements:
866 for r in requirements:
867 if r not in supported:
867 if r not in supported:
868 if not r or not r[0].isalnum():
868 if not r or not r[0].isalnum():
869 raise error.RequirementError(_(".hg/requires file is corrupt"))
869 raise error.RequirementError(_(".hg/requires file is corrupt"))
870 missings.append(r)
870 missings.append(r)
871 missings.sort()
871 missings.sort()
872 if missings:
872 if missings:
873 raise error.RequirementError(
873 raise error.RequirementError(
874 _("unknown repository format: requires features '%s' (upgrade "
874 _("unknown repository format: requires features '%s' (upgrade "
875 "Mercurial)") % "', '".join(missings))
875 "Mercurial)") % "', '".join(missings))
876 return requirements
876 return requirements
877
877
878 class filecacheentry(object):
878 class filecacheentry(object):
879 def __init__(self, path, stat=True):
879 def __init__(self, path, stat=True):
880 self.path = path
880 self.path = path
881 self.cachestat = None
881 self.cachestat = None
882 self._cacheable = None
882 self._cacheable = None
883
883
884 if stat:
884 if stat:
885 self.cachestat = filecacheentry.stat(self.path)
885 self.cachestat = filecacheentry.stat(self.path)
886
886
887 if self.cachestat:
887 if self.cachestat:
888 self._cacheable = self.cachestat.cacheable()
888 self._cacheable = self.cachestat.cacheable()
889 else:
889 else:
890 # None means we don't know yet
890 # None means we don't know yet
891 self._cacheable = None
891 self._cacheable = None
892
892
893 def refresh(self):
893 def refresh(self):
894 if self.cacheable():
894 if self.cacheable():
895 self.cachestat = filecacheentry.stat(self.path)
895 self.cachestat = filecacheentry.stat(self.path)
896
896
897 def cacheable(self):
897 def cacheable(self):
898 if self._cacheable is not None:
898 if self._cacheable is not None:
899 return self._cacheable
899 return self._cacheable
900
900
901 # we don't know yet, assume it is for now
901 # we don't know yet, assume it is for now
902 return True
902 return True
903
903
904 def changed(self):
904 def changed(self):
905 # no point in going further if we can't cache it
905 # no point in going further if we can't cache it
906 if not self.cacheable():
906 if not self.cacheable():
907 return True
907 return True
908
908
909 newstat = filecacheentry.stat(self.path)
909 newstat = filecacheentry.stat(self.path)
910
910
911 # we may not know if it's cacheable yet, check again now
911 # we may not know if it's cacheable yet, check again now
912 if newstat and self._cacheable is None:
912 if newstat and self._cacheable is None:
913 self._cacheable = newstat.cacheable()
913 self._cacheable = newstat.cacheable()
914
914
915 # check again
915 # check again
916 if not self._cacheable:
916 if not self._cacheable:
917 return True
917 return True
918
918
919 if self.cachestat != newstat:
919 if self.cachestat != newstat:
920 self.cachestat = newstat
920 self.cachestat = newstat
921 return True
921 return True
922 else:
922 else:
923 return False
923 return False
924
924
925 @staticmethod
925 @staticmethod
926 def stat(path):
926 def stat(path):
927 try:
927 try:
928 return util.cachestat(path)
928 return util.cachestat(path)
929 except OSError, e:
929 except OSError, e:
930 if e.errno != errno.ENOENT:
930 if e.errno != errno.ENOENT:
931 raise
931 raise
932
932
933 class filecache(object):
933 class filecache(object):
934 '''A property like decorator that tracks a file under .hg/ for updates.
934 '''A property like decorator that tracks a file under .hg/ for updates.
935
935
936 Records stat info when called in _filecache.
936 Records stat info when called in _filecache.
937
937
938 On subsequent calls, compares old stat info with new info, and recreates
938 On subsequent calls, compares old stat info with new info, and recreates
939 the object when needed, updating the new stat info in _filecache.
939 the object when needed, updating the new stat info in _filecache.
940
940
941 Mercurial either atomic renames or appends for files under .hg,
941 Mercurial either atomic renames or appends for files under .hg,
942 so to ensure the cache is reliable we need the filesystem to be able
942 so to ensure the cache is reliable we need the filesystem to be able
943 to tell us if a file has been replaced. If it can't, we fallback to
943 to tell us if a file has been replaced. If it can't, we fallback to
944 recreating the object on every call (essentially the same behaviour as
944 recreating the object on every call (essentially the same behaviour as
945 propertycache).'''
945 propertycache).'''
946 def __init__(self, path):
946 def __init__(self, path):
947 self.path = path
947 self.path = path
948
948
949 def join(self, obj, fname):
949 def join(self, obj, fname):
950 """Used to compute the runtime path of the cached file.
950 """Used to compute the runtime path of the cached file.
951
951
952 Users should subclass filecache and provide their own version of this
952 Users should subclass filecache and provide their own version of this
953 function to call the appropriate join function on 'obj' (an instance
953 function to call the appropriate join function on 'obj' (an instance
954 of the class that its member function was decorated).
954 of the class that its member function was decorated).
955 """
955 """
956 return obj.join(fname)
956 return obj.join(fname)
957
957
958 def __call__(self, func):
958 def __call__(self, func):
959 self.func = func
959 self.func = func
960 self.name = func.__name__
960 self.name = func.__name__
961 return self
961 return self
962
962
963 def __get__(self, obj, type=None):
963 def __get__(self, obj, type=None):
964 # do we need to check if the file changed?
964 # do we need to check if the file changed?
965 if self.name in obj.__dict__:
965 if self.name in obj.__dict__:
966 assert self.name in obj._filecache, self.name
966 assert self.name in obj._filecache, self.name
967 return obj.__dict__[self.name]
967 return obj.__dict__[self.name]
968
968
969 entry = obj._filecache.get(self.name)
969 entry = obj._filecache.get(self.name)
970
970
971 if entry:
971 if entry:
972 if entry.changed():
972 if entry.changed():
973 entry.obj = self.func(obj)
973 entry.obj = self.func(obj)
974 else:
974 else:
975 path = self.join(obj, self.path)
975 path = self.join(obj, self.path)
976
976
977 # We stat -before- creating the object so our cache doesn't lie if
977 # We stat -before- creating the object so our cache doesn't lie if
978 # a writer modified between the time we read and stat
978 # a writer modified between the time we read and stat
979 entry = filecacheentry(path)
979 entry = filecacheentry(path)
980 entry.obj = self.func(obj)
980 entry.obj = self.func(obj)
981
981
982 obj._filecache[self.name] = entry
982 obj._filecache[self.name] = entry
983
983
984 obj.__dict__[self.name] = entry.obj
984 obj.__dict__[self.name] = entry.obj
985 return entry.obj
985 return entry.obj
986
986
987 def __set__(self, obj, value):
987 def __set__(self, obj, value):
988 if self.name not in obj._filecache:
988 if self.name not in obj._filecache:
989 # we add an entry for the missing value because X in __dict__
989 # we add an entry for the missing value because X in __dict__
990 # implies X in _filecache
990 # implies X in _filecache
991 ce = filecacheentry(self.join(obj, self.path), False)
991 ce = filecacheentry(self.join(obj, self.path), False)
992 obj._filecache[self.name] = ce
992 obj._filecache[self.name] = ce
993 else:
993 else:
994 ce = obj._filecache[self.name]
994 ce = obj._filecache[self.name]
995
995
996 ce.obj = value # update cached copy
996 ce.obj = value # update cached copy
997 obj.__dict__[self.name] = value # update copy returned by obj.x
997 obj.__dict__[self.name] = value # update copy returned by obj.x
998
998
999 def __delete__(self, obj):
999 def __delete__(self, obj):
1000 try:
1000 try:
1001 del obj.__dict__[self.name]
1001 del obj.__dict__[self.name]
1002 except KeyError:
1002 except KeyError:
1003 raise AttributeError(self.name)
1003 raise AttributeError(self.name)
@@ -1,424 +1,433 b''
1 # template-filters.py - common template expansion filters
1 # template-filters.py - common template expansion filters
2 #
2 #
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import cgi, re, os, time, urllib
8 import cgi, re, os, time, urllib
9 import encoding, node, util, error
9 import encoding, node, util, error
10 import hbisect
10 import hbisect
11
11
12 def addbreaks(text):
12 def addbreaks(text):
13 """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
13 """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
14 every line except the last.
14 every line except the last.
15 """
15 """
16 return text.replace('\n', '<br/>\n')
16 return text.replace('\n', '<br/>\n')
17
17
18 agescales = [("year", 3600 * 24 * 365),
18 agescales = [("year", 3600 * 24 * 365),
19 ("month", 3600 * 24 * 30),
19 ("month", 3600 * 24 * 30),
20 ("week", 3600 * 24 * 7),
20 ("week", 3600 * 24 * 7),
21 ("day", 3600 * 24),
21 ("day", 3600 * 24),
22 ("hour", 3600),
22 ("hour", 3600),
23 ("minute", 60),
23 ("minute", 60),
24 ("second", 1)]
24 ("second", 1)]
25
25
26 def age(date):
26 def age(date):
27 """:age: Date. Returns a human-readable date/time difference between the
27 """:age: Date. Returns a human-readable date/time difference between the
28 given date/time and the current date/time.
28 given date/time and the current date/time.
29 """
29 """
30
30
31 def plural(t, c):
31 def plural(t, c):
32 if c == 1:
32 if c == 1:
33 return t
33 return t
34 return t + "s"
34 return t + "s"
35 def fmt(t, c):
35 def fmt(t, c):
36 return "%d %s" % (c, plural(t, c))
36 return "%d %s" % (c, plural(t, c))
37
37
38 now = time.time()
38 now = time.time()
39 then = date[0]
39 then = date[0]
40 future = False
40 future = False
41 if then > now:
41 if then > now:
42 future = True
42 future = True
43 delta = max(1, int(then - now))
43 delta = max(1, int(then - now))
44 if delta > agescales[0][1] * 30:
44 if delta > agescales[0][1] * 30:
45 return 'in the distant future'
45 return 'in the distant future'
46 else:
46 else:
47 delta = max(1, int(now - then))
47 delta = max(1, int(now - then))
48 if delta > agescales[0][1] * 2:
48 if delta > agescales[0][1] * 2:
49 return util.shortdate(date)
49 return util.shortdate(date)
50
50
51 for t, s in agescales:
51 for t, s in agescales:
52 n = delta // s
52 n = delta // s
53 if n >= 2 or s == 1:
53 if n >= 2 or s == 1:
54 if future:
54 if future:
55 return '%s from now' % fmt(t, n)
55 return '%s from now' % fmt(t, n)
56 return '%s ago' % fmt(t, n)
56 return '%s ago' % fmt(t, n)
57
57
58 def basename(path):
58 def basename(path):
59 """:basename: Any text. Treats the text as a path, and returns the last
59 """:basename: Any text. Treats the text as a path, and returns the last
60 component of the path after splitting by the path separator
60 component of the path after splitting by the path separator
61 (ignoring trailing separators). For example, "foo/bar/baz" becomes
61 (ignoring trailing separators). For example, "foo/bar/baz" becomes
62 "baz" and "foo/bar//" becomes "bar".
62 "baz" and "foo/bar//" becomes "bar".
63 """
63 """
64 return os.path.basename(path)
64 return os.path.basename(path)
65
65
66 def datefilter(text):
66 def datefilter(text):
67 """:date: Date. Returns a date in a Unix date format, including the
67 """:date: Date. Returns a date in a Unix date format, including the
68 timezone: "Mon Sep 04 15:13:13 2006 0700".
68 timezone: "Mon Sep 04 15:13:13 2006 0700".
69 """
69 """
70 return util.datestr(text)
70 return util.datestr(text)
71
71
72 def domain(author):
72 def domain(author):
73 """:domain: Any text. Finds the first string that looks like an email
73 """:domain: Any text. Finds the first string that looks like an email
74 address, and extracts just the domain component. Example: ``User
74 address, and extracts just the domain component. Example: ``User
75 <user@example.com>`` becomes ``example.com``.
75 <user@example.com>`` becomes ``example.com``.
76 """
76 """
77 f = author.find('@')
77 f = author.find('@')
78 if f == -1:
78 if f == -1:
79 return ''
79 return ''
80 author = author[f + 1:]
80 author = author[f + 1:]
81 f = author.find('>')
81 f = author.find('>')
82 if f >= 0:
82 if f >= 0:
83 author = author[:f]
83 author = author[:f]
84 return author
84 return author
85
85
86 def email(text):
86 def email(text):
87 """:email: Any text. Extracts the first string that looks like an email
87 """:email: Any text. Extracts the first string that looks like an email
88 address. Example: ``User <user@example.com>`` becomes
88 address. Example: ``User <user@example.com>`` becomes
89 ``user@example.com``.
89 ``user@example.com``.
90 """
90 """
91 return util.email(text)
91 return util.email(text)
92
92
93 def escape(text):
93 def escape(text):
94 """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
94 """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
95 and ">" with XML entities, and filters out NUL characters.
95 and ">" with XML entities, and filters out NUL characters.
96 """
96 """
97 return cgi.escape(text.replace('\0', ''), True)
97 return cgi.escape(text.replace('\0', ''), True)
98
98
99 para_re = None
99 para_re = None
100 space_re = None
100 space_re = None
101
101
102 def fill(text, width):
102 def fill(text, width):
103 '''fill many paragraphs.'''
103 '''fill many paragraphs.'''
104 global para_re, space_re
104 global para_re, space_re
105 if para_re is None:
105 if para_re is None:
106 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
106 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
107 space_re = re.compile(r' +')
107 space_re = re.compile(r' +')
108
108
109 def findparas():
109 def findparas():
110 start = 0
110 start = 0
111 while True:
111 while True:
112 m = para_re.search(text, start)
112 m = para_re.search(text, start)
113 if not m:
113 if not m:
114 uctext = unicode(text[start:], encoding.encoding)
114 uctext = unicode(text[start:], encoding.encoding)
115 w = len(uctext)
115 w = len(uctext)
116 while 0 < w and uctext[w - 1].isspace():
116 while 0 < w and uctext[w - 1].isspace():
117 w -= 1
117 w -= 1
118 yield (uctext[:w].encode(encoding.encoding),
118 yield (uctext[:w].encode(encoding.encoding),
119 uctext[w:].encode(encoding.encoding))
119 uctext[w:].encode(encoding.encoding))
120 break
120 break
121 yield text[start:m.start(0)], m.group(1)
121 yield text[start:m.start(0)], m.group(1)
122 start = m.end(1)
122 start = m.end(1)
123
123
124 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
124 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
125 for para, rest in findparas()])
125 for para, rest in findparas()])
126
126
127 def fill68(text):
127 def fill68(text):
128 """:fill68: Any text. Wraps the text to fit in 68 columns."""
128 """:fill68: Any text. Wraps the text to fit in 68 columns."""
129 return fill(text, 68)
129 return fill(text, 68)
130
130
131 def fill76(text):
131 def fill76(text):
132 """:fill76: Any text. Wraps the text to fit in 76 columns."""
132 """:fill76: Any text. Wraps the text to fit in 76 columns."""
133 return fill(text, 76)
133 return fill(text, 76)
134
134
135 def firstline(text):
135 def firstline(text):
136 """:firstline: Any text. Returns the first line of text."""
136 """:firstline: Any text. Returns the first line of text."""
137 try:
137 try:
138 return text.splitlines(True)[0].rstrip('\r\n')
138 return text.splitlines(True)[0].rstrip('\r\n')
139 except IndexError:
139 except IndexError:
140 return ''
140 return ''
141
141
142 def hexfilter(text):
142 def hexfilter(text):
143 """:hex: Any text. Convert a binary Mercurial node identifier into
143 """:hex: Any text. Convert a binary Mercurial node identifier into
144 its long hexadecimal representation.
144 its long hexadecimal representation.
145 """
145 """
146 return node.hex(text)
146 return node.hex(text)
147
147
148 def hgdate(text):
148 def hgdate(text):
149 """:hgdate: Date. Returns the date as a pair of numbers: "1157407993
149 """:hgdate: Date. Returns the date as a pair of numbers: "1157407993
150 25200" (Unix timestamp, timezone offset).
150 25200" (Unix timestamp, timezone offset).
151 """
151 """
152 return "%d %d" % text
152 return "%d %d" % text
153
153
154 def isodate(text):
154 def isodate(text):
155 """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
155 """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
156 +0200".
156 +0200".
157 """
157 """
158 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
158 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
159
159
160 def isodatesec(text):
160 def isodatesec(text):
161 """:isodatesec: Date. Returns the date in ISO 8601 format, including
161 """:isodatesec: Date. Returns the date in ISO 8601 format, including
162 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
162 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
163 filter.
163 filter.
164 """
164 """
165 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
165 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
166
166
167 def indent(text, prefix):
167 def indent(text, prefix):
168 '''indent each non-empty line of text after first with prefix.'''
168 '''indent each non-empty line of text after first with prefix.'''
169 lines = text.splitlines()
169 lines = text.splitlines()
170 num_lines = len(lines)
170 num_lines = len(lines)
171 endswithnewline = text[-1:] == '\n'
171 endswithnewline = text[-1:] == '\n'
172 def indenter():
172 def indenter():
173 for i in xrange(num_lines):
173 for i in xrange(num_lines):
174 l = lines[i]
174 l = lines[i]
175 if i and l.strip():
175 if i and l.strip():
176 yield prefix
176 yield prefix
177 yield l
177 yield l
178 if i < num_lines - 1 or endswithnewline:
178 if i < num_lines - 1 or endswithnewline:
179 yield '\n'
179 yield '\n'
180 return "".join(indenter())
180 return "".join(indenter())
181
181
182 def json(obj):
182 def json(obj):
183 if obj is None or obj is False or obj is True:
183 if obj is None or obj is False or obj is True:
184 return {None: 'null', False: 'false', True: 'true'}[obj]
184 return {None: 'null', False: 'false', True: 'true'}[obj]
185 elif isinstance(obj, int) or isinstance(obj, float):
185 elif isinstance(obj, int) or isinstance(obj, float):
186 return str(obj)
186 return str(obj)
187 elif isinstance(obj, str):
187 elif isinstance(obj, str):
188 u = unicode(obj, encoding.encoding, 'replace')
188 u = unicode(obj, encoding.encoding, 'replace')
189 return '"%s"' % jsonescape(u)
189 return '"%s"' % jsonescape(u)
190 elif isinstance(obj, unicode):
190 elif isinstance(obj, unicode):
191 return '"%s"' % jsonescape(obj)
191 return '"%s"' % jsonescape(obj)
192 elif util.safehasattr(obj, 'keys'):
192 elif util.safehasattr(obj, 'keys'):
193 out = []
193 out = []
194 for k, v in obj.iteritems():
194 for k, v in obj.iteritems():
195 s = '%s: %s' % (json(k), json(v))
195 s = '%s: %s' % (json(k), json(v))
196 out.append(s)
196 out.append(s)
197 return '{' + ', '.join(out) + '}'
197 return '{' + ', '.join(out) + '}'
198 elif util.safehasattr(obj, '__iter__'):
198 elif util.safehasattr(obj, '__iter__'):
199 out = []
199 out = []
200 for i in obj:
200 for i in obj:
201 out.append(json(i))
201 out.append(json(i))
202 return '[' + ', '.join(out) + ']'
202 return '[' + ', '.join(out) + ']'
203 else:
203 else:
204 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
204 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
205
205
206 def _uescape(c):
206 def _uescape(c):
207 if ord(c) < 0x80:
207 if ord(c) < 0x80:
208 return c
208 return c
209 else:
209 else:
210 return '\\u%04x' % ord(c)
210 return '\\u%04x' % ord(c)
211
211
212 _escapes = [
212 _escapes = [
213 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
213 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
214 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
214 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
215 ]
215 ]
216
216
217 def jsonescape(s):
217 def jsonescape(s):
218 for k, v in _escapes:
218 for k, v in _escapes:
219 s = s.replace(k, v)
219 s = s.replace(k, v)
220 return ''.join(_uescape(c) for c in s)
220 return ''.join(_uescape(c) for c in s)
221
221
222 def localdate(text):
222 def localdate(text):
223 """:localdate: Date. Converts a date to local date."""
223 """:localdate: Date. Converts a date to local date."""
224 return (util.parsedate(text)[0], util.makedate()[1])
224 return (util.parsedate(text)[0], util.makedate()[1])
225
225
226 def nonempty(str):
226 def nonempty(str):
227 """:nonempty: Any text. Returns '(none)' if the string is empty."""
227 """:nonempty: Any text. Returns '(none)' if the string is empty."""
228 return str or "(none)"
228 return str or "(none)"
229
229
230 def obfuscate(text):
230 def obfuscate(text):
231 """:obfuscate: Any text. Returns the input text rendered as a sequence of
231 """:obfuscate: Any text. Returns the input text rendered as a sequence of
232 XML entities.
232 XML entities.
233 """
233 """
234 text = unicode(text, encoding.encoding, 'replace')
234 text = unicode(text, encoding.encoding, 'replace')
235 return ''.join(['&#%d;' % ord(c) for c in text])
235 return ''.join(['&#%d;' % ord(c) for c in text])
236
236
237 def permissions(flags):
237 def permissions(flags):
238 if "l" in flags:
238 if "l" in flags:
239 return "lrwxrwxrwx"
239 return "lrwxrwxrwx"
240 if "x" in flags:
240 if "x" in flags:
241 return "-rwxr-xr-x"
241 return "-rwxr-xr-x"
242 return "-rw-r--r--"
242 return "-rw-r--r--"
243
243
244 def person(author):
244 def person(author):
245 """:person: Any text. Returns the name before an email address,
245 """:person: Any text. Returns the name before an email address,
246 interpreting it as per RFC 5322.
246 interpreting it as per RFC 5322.
247
247
248 >>> person('foo@bar')
248 >>> person('foo@bar')
249 'foo'
249 'foo'
250 >>> person('Foo Bar <foo@bar>')
250 >>> person('Foo Bar <foo@bar>')
251 'Foo Bar'
251 'Foo Bar'
252 >>> person('"Foo Bar" <foo@bar>')
252 >>> person('"Foo Bar" <foo@bar>')
253 'Foo Bar'
253 'Foo Bar'
254 >>> person('"Foo \"buz\" Bar" <foo@bar>')
254 >>> person('"Foo \"buz\" Bar" <foo@bar>')
255 'Foo "buz" Bar'
255 'Foo "buz" Bar'
256 >>> # The following are invalid, but do exist in real-life
256 >>> # The following are invalid, but do exist in real-life
257 ...
257 ...
258 >>> person('Foo "buz" Bar <foo@bar>')
258 >>> person('Foo "buz" Bar <foo@bar>')
259 'Foo "buz" Bar'
259 'Foo "buz" Bar'
260 >>> person('"Foo Bar <foo@bar>')
260 >>> person('"Foo Bar <foo@bar>')
261 'Foo Bar'
261 'Foo Bar'
262 """
262 """
263 if '@' not in author:
263 if '@' not in author:
264 return author
264 return author
265 f = author.find('<')
265 f = author.find('<')
266 if f != -1:
266 if f != -1:
267 return author[:f].strip(' "').replace('\\"', '"')
267 return author[:f].strip(' "').replace('\\"', '"')
268 f = author.find('@')
268 f = author.find('@')
269 return author[:f].replace('.', ' ')
269 return author[:f].replace('.', ' ')
270
270
271 def rfc3339date(text):
271 def rfc3339date(text):
272 """:rfc3339date: Date. Returns a date using the Internet date format
272 """:rfc3339date: Date. Returns a date using the Internet date format
273 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
273 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
274 """
274 """
275 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
275 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
276
276
277 def rfc822date(text):
277 def rfc822date(text):
278 """:rfc822date: Date. Returns a date using the same format used in email
278 """:rfc822date: Date. Returns a date using the same format used in email
279 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
279 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
280 """
280 """
281 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
281 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
282
282
283 def short(text):
283 def short(text):
284 """:short: Changeset hash. Returns the short form of a changeset hash,
284 """:short: Changeset hash. Returns the short form of a changeset hash,
285 i.e. a 12 hexadecimal digit string.
285 i.e. a 12 hexadecimal digit string.
286 """
286 """
287 return text[:12]
287 return text[:12]
288
288
289 def shortbisect(text):
289 def shortbisect(text):
290 """:shortbisect: Any text. Treats `text` as a bisection status, and
290 """:shortbisect: Any text. Treats `text` as a bisection status, and
291 returns a single-character representing the status (G: good, B: bad,
291 returns a single-character representing the status (G: good, B: bad,
292 S: skipped, U: untested, I: ignored). Returns single space if `text`
292 S: skipped, U: untested, I: ignored). Returns single space if `text`
293 is not a valid bisection status.
293 is not a valid bisection status.
294 """
294 """
295 return hbisect.shortlabel(text) or ' '
295 return hbisect.shortlabel(text) or ' '
296
296
297 def shortdate(text):
297 def shortdate(text):
298 """:shortdate: Date. Returns a date like "2006-09-18"."""
298 """:shortdate: Date. Returns a date like "2006-09-18"."""
299 return util.shortdate(text)
299 return util.shortdate(text)
300
300
301 def stringescape(text):
301 def stringescape(text):
302 return text.encode('string_escape')
302 return text.encode('string_escape')
303
303
304 def stringify(thing):
304 def stringify(thing):
305 """:stringify: Any type. Turns the value into text by converting values into
305 """:stringify: Any type. Turns the value into text by converting values into
306 text and concatenating them.
306 text and concatenating them.
307 """
307 """
308 if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
308 if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
309 return "".join([stringify(t) for t in thing if t is not None])
309 return "".join([stringify(t) for t in thing if t is not None])
310 return str(thing)
310 return str(thing)
311
311
312 def strip(text):
312 def strip(text):
313 """:strip: Any text. Strips all leading and trailing whitespace."""
313 """:strip: Any text. Strips all leading and trailing whitespace."""
314 return text.strip()
314 return text.strip()
315
315
316 def stripdir(text):
316 def stripdir(text):
317 """:stripdir: Treat the text as path and strip a directory level, if
317 """:stripdir: Treat the text as path and strip a directory level, if
318 possible. For example, "foo" and "foo/bar" becomes "foo".
318 possible. For example, "foo" and "foo/bar" becomes "foo".
319 """
319 """
320 dir = os.path.dirname(text)
320 dir = os.path.dirname(text)
321 if dir == "":
321 if dir == "":
322 return os.path.basename(text)
322 return os.path.basename(text)
323 else:
323 else:
324 return dir
324 return dir
325
325
326 def tabindent(text):
326 def tabindent(text):
327 """:tabindent: Any text. Returns the text, with every line except the
327 """:tabindent: Any text. Returns the text, with every line except the
328 first starting with a tab character.
328 first starting with a tab character.
329 """
329 """
330 return indent(text, '\t')
330 return indent(text, '\t')
331
331
332 def urlescape(text):
332 def urlescape(text):
333 """:urlescape: Any text. Escapes all "special" characters. For example,
333 """:urlescape: Any text. Escapes all "special" characters. For example,
334 "foo bar" becomes "foo%20bar".
334 "foo bar" becomes "foo%20bar".
335 """
335 """
336 return urllib.quote(text)
336 return urllib.quote(text)
337
337
338 def userfilter(text):
338 def userfilter(text):
339 """:user: Any text. Returns a short representation of a user name or email
339 """:user: Any text. Returns a short representation of a user name or email
340 address."""
340 address."""
341 return util.shortuser(text)
341 return util.shortuser(text)
342
342
343 def emailuser(text):
343 def emailuser(text):
344 """:emailuser: Any text. Returns the user portion of an email address."""
344 """:emailuser: Any text. Returns the user portion of an email address."""
345 return util.emailuser(text)
345 return util.emailuser(text)
346
346
347 def xmlescape(text):
347 def xmlescape(text):
348 text = (text
348 text = (text
349 .replace('&', '&amp;')
349 .replace('&', '&amp;')
350 .replace('<', '&lt;')
350 .replace('<', '&lt;')
351 .replace('>', '&gt;')
351 .replace('>', '&gt;')
352 .replace('"', '&quot;')
352 .replace('"', '&quot;')
353 .replace("'", '&#39;')) # &apos; invalid in HTML
353 .replace("'", '&#39;')) # &apos; invalid in HTML
354 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
354 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
355
355
356 filters = {
356 filters = {
357 "addbreaks": addbreaks,
357 "addbreaks": addbreaks,
358 "age": age,
358 "age": age,
359 "basename": basename,
359 "basename": basename,
360 "date": datefilter,
360 "date": datefilter,
361 "domain": domain,
361 "domain": domain,
362 "email": email,
362 "email": email,
363 "escape": escape,
363 "escape": escape,
364 "fill68": fill68,
364 "fill68": fill68,
365 "fill76": fill76,
365 "fill76": fill76,
366 "firstline": firstline,
366 "firstline": firstline,
367 "hex": hexfilter,
367 "hex": hexfilter,
368 "hgdate": hgdate,
368 "hgdate": hgdate,
369 "isodate": isodate,
369 "isodate": isodate,
370 "isodatesec": isodatesec,
370 "isodatesec": isodatesec,
371 "json": json,
371 "json": json,
372 "jsonescape": jsonescape,
372 "jsonescape": jsonescape,
373 "localdate": localdate,
373 "localdate": localdate,
374 "nonempty": nonempty,
374 "nonempty": nonempty,
375 "obfuscate": obfuscate,
375 "obfuscate": obfuscate,
376 "permissions": permissions,
376 "permissions": permissions,
377 "person": person,
377 "person": person,
378 "rfc3339date": rfc3339date,
378 "rfc3339date": rfc3339date,
379 "rfc822date": rfc822date,
379 "rfc822date": rfc822date,
380 "short": short,
380 "short": short,
381 "shortbisect": shortbisect,
381 "shortbisect": shortbisect,
382 "shortdate": shortdate,
382 "shortdate": shortdate,
383 "stringescape": stringescape,
383 "stringescape": stringescape,
384 "stringify": stringify,
384 "stringify": stringify,
385 "strip": strip,
385 "strip": strip,
386 "stripdir": stripdir,
386 "stripdir": stripdir,
387 "tabindent": tabindent,
387 "tabindent": tabindent,
388 "urlescape": urlescape,
388 "urlescape": urlescape,
389 "user": userfilter,
389 "user": userfilter,
390 "emailuser": emailuser,
390 "emailuser": emailuser,
391 "xmlescape": xmlescape,
391 "xmlescape": xmlescape,
392 }
392 }
393
393
394 def websub(text, websubtable):
395 """:websub: Any text. Only applies to hgweb. Applies the regular
396 expression replacements defined in the websub section.
397 """
398 if websubtable:
399 for regexp, format in websubtable:
400 text = regexp.sub(format, text)
401 return text
402
394 def fillfunc(context, mapping, args):
403 def fillfunc(context, mapping, args):
395 if not (1 <= len(args) <= 2):
404 if not (1 <= len(args) <= 2):
396 raise error.ParseError(_("fill expects one or two arguments"))
405 raise error.ParseError(_("fill expects one or two arguments"))
397
406
398 text = stringify(args[0][0](context, mapping, args[0][1]))
407 text = stringify(args[0][0](context, mapping, args[0][1]))
399 width = 76
408 width = 76
400 if len(args) == 2:
409 if len(args) == 2:
401 try:
410 try:
402 width = int(stringify(args[1][0](context, mapping, args[1][1])))
411 width = int(stringify(args[1][0](context, mapping, args[1][1])))
403 except ValueError:
412 except ValueError:
404 raise error.ParseError(_("fill expects an integer width"))
413 raise error.ParseError(_("fill expects an integer width"))
405
414
406 return fill(text, width)
415 return fill(text, width)
407
416
408 def datefunc(context, mapping, args):
417 def datefunc(context, mapping, args):
409 if not (1 <= len(args) <= 2):
418 if not (1 <= len(args) <= 2):
410 raise error.ParseError(_("date expects one or two arguments"))
419 raise error.ParseError(_("date expects one or two arguments"))
411
420
412 date = args[0][0](context, mapping, args[0][1])
421 date = args[0][0](context, mapping, args[0][1])
413 if len(args) == 2:
422 if len(args) == 2:
414 fmt = stringify(args[1][0](context, mapping, args[1][1]))
423 fmt = stringify(args[1][0](context, mapping, args[1][1]))
415 return util.datestr(date, fmt)
424 return util.datestr(date, fmt)
416 return util.datestr(date)
425 return util.datestr(date)
417
426
418 funcs = {
427 funcs = {
419 "fill": fillfunc,
428 "fill": fillfunc,
420 "date": datefunc,
429 "date": datefunc,
421 }
430 }
422
431
423 # tell hggettext to extract docstrings from these functions:
432 # tell hggettext to extract docstrings from these functions:
424 i18nfunctions = filters.values()
433 i18nfunctions = filters.values()
@@ -1,14 +1,14 b''
1 <div>
1 <div>
2 <a class="title" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}"><span class="age">{date|rfc822date}</span>{desc|strip|firstline|escape|nonempty}<span class="logtags"> {inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span></a>
2 <a class="title" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}"><span class="age">{date|rfc822date}</span>{desc|strip|firstline|escape|nonempty}<span class="logtags"> {inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span></a>
3 </div>
3 </div>
4 <div class="title_text">
4 <div class="title_text">
5 <div class="log_link">
5 <div class="log_link">
6 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a><br/>
6 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a><br/>
7 </div>
7 </div>
8 <i>{author|obfuscate} [{date|rfc822date}] rev {rev}</i><br/>
8 <i>{author|obfuscate} [{date|rfc822date}] rev {rev}</i><br/>
9 </div>
9 </div>
10 <div class="log_body">
10 <div class="log_body">
11 {desc|strip|escape|addbreaks|nonempty}
11 {desc|strip|escape|websub|addbreaks|nonempty}
12 <br/>
12 <br/>
13 <br/>
13 <br/>
14 </div>
14 </div>
@@ -1,54 +1,54 b''
1 {header}
1 {header}
2 <title>{repo|escape}: changeset {rev}:{node|short}</title>
2 <title>{repo|escape}: changeset {rev}:{node|short}</title>
3 <link rel="alternate" type="application/atom+xml"
3 <link rel="alternate" type="application/atom+xml"
4 href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
4 href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
5 <link rel="alternate" type="application/rss+xml"
5 <link rel="alternate" type="application/rss+xml"
6 href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
6 href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
7 </head>
7 </head>
8 <body>
8 <body>
9
9
10 <div class="page_header">
10 <div class="page_header">
11 <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
11 <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
12 <a href="/">Mercurial</a> {pathdef%breadcrumb} / changeset
12 <a href="/">Mercurial</a> {pathdef%breadcrumb} / changeset
13 </div>
13 </div>
14
14
15 <div class="page_nav">
15 <div class="page_nav">
16 <a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a> |
16 <a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a> |
17 <a href="{url|urlescape}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a> |
17 <a href="{url|urlescape}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a> |
18 <a href="{url|urlescape}log/{rev}{sessionvars%urlparameter}">changelog</a> |
18 <a href="{url|urlescape}log/{rev}{sessionvars%urlparameter}">changelog</a> |
19 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a> |
19 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a> |
20 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> |
20 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> |
21 <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
21 <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
22 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> |
22 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> |
23 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a> |
23 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a> |
24 changeset |
24 changeset |
25 <a href="{url|urlescape}raw-rev/{node|short}">raw</a> {archives%archiveentry} |
25 <a href="{url|urlescape}raw-rev/{node|short}">raw</a> {archives%archiveentry} |
26 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
26 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
27 <br/>
27 <br/>
28 </div>
28 </div>
29
29
30 <div>
30 <div>
31 <a class="title" href="{url|urlescape}raw-rev/{node|short}">{desc|strip|escape|firstline|nonempty} <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span></a>
31 <a class="title" href="{url|urlescape}raw-rev/{node|short}">{desc|strip|escape|firstline|nonempty} <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span></a>
32 </div>
32 </div>
33 <div class="title_text">
33 <div class="title_text">
34 <table cellspacing="0">
34 <table cellspacing="0">
35 <tr><td>author</td><td>{author|obfuscate}</td></tr>
35 <tr><td>author</td><td>{author|obfuscate}</td></tr>
36 <tr><td></td><td class="date age">{date|rfc822date}</td></tr>
36 <tr><td></td><td class="date age">{date|rfc822date}</td></tr>
37 {branch%changesetbranch}
37 {branch%changesetbranch}
38 <tr><td>changeset {rev}</td><td style="font-family:monospace">{node|short}</td></tr>
38 <tr><td>changeset {rev}</td><td style="font-family:monospace">{node|short}</td></tr>
39 {parent%changesetparent}
39 {parent%changesetparent}
40 {child%changesetchild}
40 {child%changesetchild}
41 </table></div>
41 </table></div>
42
42
43 <div class="page_body">
43 <div class="page_body">
44 {desc|strip|escape|addbreaks|nonempty}
44 {desc|strip|escape|websub|addbreaks|nonempty}
45 </div>
45 </div>
46 <div class="list_head"></div>
46 <div class="list_head"></div>
47 <div class="title_text">
47 <div class="title_text">
48 <table cellspacing="0">
48 <table cellspacing="0">
49 {files}
49 {files}
50 </table></div>
50 </table></div>
51
51
52 <div class="page_body">{diff}</div>
52 <div class="page_body">{diff}</div>
53
53
54 {footer}
54 {footer}
@@ -1,67 +1,67 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape}@{node|short} (annotated)</title>
2 <title>{repo|escape}: {file|escape}@{node|short} (annotated)</title>
3 <link rel="alternate" type="application/atom+xml"
3 <link rel="alternate" type="application/atom+xml"
4 href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
4 href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
5 <link rel="alternate" type="application/rss+xml"
5 <link rel="alternate" type="application/rss+xml"
6 href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
6 href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
7 </head>
7 </head>
8 <body>
8 <body>
9
9
10 <div class="page_header">
10 <div class="page_header">
11 <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
11 <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
12 <a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate
12 <a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate
13 </div>
13 </div>
14
14
15 <div class="page_nav">
15 <div class="page_nav">
16 <a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a> |
16 <a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a> |
17 <a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a> |
17 <a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a> |
18 <a href="{url|urlescape}log{sessionvars%urlparameter}">changelog</a> |
18 <a href="{url|urlescape}log{sessionvars%urlparameter}">changelog</a> |
19 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a> |
19 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a> |
20 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> |
20 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> |
21 <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
21 <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
22 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> |
22 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> |
23 <a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
23 <a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
24 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
24 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
25 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
25 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
26 <a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
26 <a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
27 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
27 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
28 annotate |
28 annotate |
29 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
29 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
30 <a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
30 <a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
31 <a href="{url|urlescape}raw-annotate/{node|short}/{file|urlescape}">raw</a> |
31 <a href="{url|urlescape}raw-annotate/{node|short}/{file|urlescape}">raw</a> |
32 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
32 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
33 <br/>
33 <br/>
34 </div>
34 </div>
35
35
36 <div class="title">{file|escape}</div>
36 <div class="title">{file|escape}</div>
37
37
38 <div class="title_text">
38 <div class="title_text">
39 <table cellspacing="0">
39 <table cellspacing="0">
40 <tr>
40 <tr>
41 <td>author</td>
41 <td>author</td>
42 <td>{author|obfuscate}</td></tr>
42 <td>{author|obfuscate}</td></tr>
43 <tr>
43 <tr>
44 <td></td>
44 <td></td>
45 <td class="date age">{date|rfc822date}</td></tr>
45 <td class="date age">{date|rfc822date}</td></tr>
46 {branch%filerevbranch}
46 {branch%filerevbranch}
47 <tr>
47 <tr>
48 <td>changeset {rev}</td>
48 <td>changeset {rev}</td>
49 <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
49 <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
50 {parent%fileannotateparent}
50 {parent%fileannotateparent}
51 {child%fileannotatechild}
51 {child%fileannotatechild}
52 <tr>
52 <tr>
53 <td>permissions</td>
53 <td>permissions</td>
54 <td style="font-family:monospace">{permissions|permissions}</td></tr>
54 <td style="font-family:monospace">{permissions|permissions}</td></tr>
55 </table>
55 </table>
56 </div>
56 </div>
57
57
58 <div class="page_path">
58 <div class="page_path">
59 {desc|strip|escape|addbreaks|nonempty}
59 {desc|strip|escape|websub|addbreaks|nonempty}
60 </div>
60 </div>
61 <div class="page_body">
61 <div class="page_body">
62 <table>
62 <table>
63 {annotate%annotateline}
63 {annotate%annotateline}
64 </table>
64 </table>
65 </div>
65 </div>
66
66
67 {footer}
67 {footer}
@@ -1,66 +1,66 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape}@{node|short}</title>
2 <title>{repo|escape}: {file|escape}@{node|short}</title>
3 <link rel="alternate" type="application/atom+xml"
3 <link rel="alternate" type="application/atom+xml"
4 href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
4 href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
5 <link rel="alternate" type="application/rss+xml"
5 <link rel="alternate" type="application/rss+xml"
6 href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
6 href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
7 </head>
7 </head>
8 <body>
8 <body>
9
9
10 <div class="page_header">
10 <div class="page_header">
11 <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
11 <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
12 <a href="/">Mercurial</a> {pathdef%breadcrumb} / file revision
12 <a href="/">Mercurial</a> {pathdef%breadcrumb} / file revision
13 </div>
13 </div>
14
14
15 <div class="page_nav">
15 <div class="page_nav">
16 <a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a> |
16 <a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a> |
17 <a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a> |
17 <a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a> |
18 <a href="{url|urlescape}log{sessionvars%urlparameter}">changelog</a> |
18 <a href="{url|urlescape}log{sessionvars%urlparameter}">changelog</a> |
19 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a> |
19 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a> |
20 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> |
20 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> |
21 <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
21 <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
22 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> |
22 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> |
23 <a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
23 <a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
24 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
24 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
25 file |
25 file |
26 <a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
26 <a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
27 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
27 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
28 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
28 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
29 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
29 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
30 <a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
30 <a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
31 <a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a> |
31 <a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a> |
32 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
32 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
33 <br/>
33 <br/>
34 </div>
34 </div>
35
35
36 <div class="title">{file|escape}</div>
36 <div class="title">{file|escape}</div>
37
37
38 <div class="title_text">
38 <div class="title_text">
39 <table cellspacing="0">
39 <table cellspacing="0">
40 <tr>
40 <tr>
41 <td>author</td>
41 <td>author</td>
42 <td>{author|obfuscate}</td></tr>
42 <td>{author|obfuscate}</td></tr>
43 <tr>
43 <tr>
44 <td></td>
44 <td></td>
45 <td class="date age">{date|rfc822date}</td></tr>
45 <td class="date age">{date|rfc822date}</td></tr>
46 {branch%filerevbranch}
46 {branch%filerevbranch}
47 <tr>
47 <tr>
48 <td>changeset {rev}</td>
48 <td>changeset {rev}</td>
49 <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
49 <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
50 {parent%filerevparent}
50 {parent%filerevparent}
51 {child%filerevchild}
51 {child%filerevchild}
52 <tr>
52 <tr>
53 <td>permissions</td>
53 <td>permissions</td>
54 <td style="font-family:monospace">{permissions|permissions}</td></tr>
54 <td style="font-family:monospace">{permissions|permissions}</td></tr>
55 </table>
55 </table>
56 </div>
56 </div>
57
57
58 <div class="page_path">
58 <div class="page_path">
59 {desc|strip|escape|addbreaks|nonempty}
59 {desc|strip|escape|websub|addbreaks|nonempty}
60 </div>
60 </div>
61
61
62 <div class="page_body">
62 <div class="page_body">
63 {text%fileline}
63 {text%fileline}
64 </div>
64 </div>
65
65
66 {footer}
66 {footer}
@@ -1,6 +1,6 b''
1 <h3 class="changelog"><a class="title" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}<span class="logtags"> {inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span></a></h3>
1 <h3 class="changelog"><a class="title" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}<span class="logtags"> {inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span></a></h3>
2 <ul class="changelog-entry">
2 <ul class="changelog-entry">
3 <li class="age">{date|rfc822date}</li>
3 <li class="age">{date|rfc822date}</li>
4 <li>by <span class="name">{author|obfuscate}</span> <span class="revdate">[{date|rfc822date}] rev {rev}</span></li>
4 <li>by <span class="name">{author|obfuscate}</span> <span class="revdate">[{date|rfc822date}] rev {rev}</span></li>
5 <li class="description">{desc|strip|escape|addbreaks|nonempty}</li>
5 <li class="description">{desc|strip|escape|websub|addbreaks|nonempty}</li>
6 </ul>
6 </ul>
@@ -1,65 +1,65 b''
1 {header}
1 {header}
2 <title>{repo|escape}: changeset {rev}:{node|short}</title>
2 <title>{repo|escape}: changeset {rev}:{node|short}</title>
3 <link rel="alternate" type="application/atom+xml" href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
3 <link rel="alternate" type="application/atom+xml" href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
4 <link rel="alternate" type="application/rss+xml" href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
4 <link rel="alternate" type="application/rss+xml" href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
5 </head>
5 </head>
6
6
7 <body>
7 <body>
8 <div id="container">
8 <div id="container">
9 <div class="page-header">
9 <div class="page-header">
10 <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / changeset</h1>
10 <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / changeset</h1>
11
11
12 <form action="{url|urlescape}log">
12 <form action="{url|urlescape}log">
13 {sessionvars%hiddenformentry}
13 {sessionvars%hiddenformentry}
14 <dl class="search">
14 <dl class="search">
15 <dt><label>Search: </label></dt>
15 <dt><label>Search: </label></dt>
16 <dd><input type="text" name="rev" /></dd>
16 <dd><input type="text" name="rev" /></dd>
17 </dl>
17 </dl>
18 </form>
18 </form>
19
19
20 <ul class="page-nav">
20 <ul class="page-nav">
21 <li><a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a></li>
21 <li><a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a></li>
22 <li><a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a></li>
22 <li><a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a></li>
23 <li><a href="{url|urlescape}changelog{sessionvars%urlparameter}">changelog</a></li>
23 <li><a href="{url|urlescape}changelog{sessionvars%urlparameter}">changelog</a></li>
24 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
24 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
25 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
25 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
26 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
26 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
27 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
27 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
28 <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
28 <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
29 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
29 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
30 </ul>
30 </ul>
31 </div>
31 </div>
32
32
33 <ul class="submenu">
33 <ul class="submenu">
34 <li class="current">changeset</li>
34 <li class="current">changeset</li>
35 <li><a href="{url|urlescape}raw-rev/{node|short}">raw</a> {archives%archiveentry}</li>
35 <li><a href="{url|urlescape}raw-rev/{node|short}">raw</a> {archives%archiveentry}</li>
36 </ul>
36 </ul>
37
37
38 <h2 class="no-link no-border">changeset</h2>
38 <h2 class="no-link no-border">changeset</h2>
39
39
40 <h3 class="changeset"><a href="{url|urlescape}raw-rev/{node|short}">{desc|strip|escape|firstline|nonempty} <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span></a></h3>
40 <h3 class="changeset"><a href="{url|urlescape}raw-rev/{node|short}">{desc|strip|escape|firstline|nonempty} <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span></a></h3>
41 <p class="changeset-age age">{date|rfc822date}</p>
41 <p class="changeset-age age">{date|rfc822date}</p>
42
42
43 <dl class="overview">
43 <dl class="overview">
44 <dt>author</dt>
44 <dt>author</dt>
45 <dd>{author|obfuscate}</dd>
45 <dd>{author|obfuscate}</dd>
46 <dt>date</dt>
46 <dt>date</dt>
47 <dd>{date|rfc822date}</dd>
47 <dd>{date|rfc822date}</dd>
48 {branch%changesetbranch}
48 {branch%changesetbranch}
49 <dt>changeset {rev}</dt>
49 <dt>changeset {rev}</dt>
50 <dd>{node|short}</dd>
50 <dd>{node|short}</dd>
51 {parent%changesetparent}
51 {parent%changesetparent}
52 {child%changesetchild}
52 {child%changesetchild}
53 </dl>
53 </dl>
54
54
55 <p class="description">{desc|strip|escape|addbreaks|nonempty}</p>
55 <p class="description">{desc|strip|escape|websub|addbreaks|nonempty}</p>
56
56
57 <table>
57 <table>
58 {files}
58 {files}
59 </table>
59 </table>
60
60
61 <div class="diff">
61 <div class="diff">
62 {diff}
62 {diff}
63 </div>
63 </div>
64
64
65 {footer}
65 {footer}
@@ -1,66 +1,66 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape}@{node|short} (annotated)</title>
2 <title>{repo|escape}: {file|escape}@{node|short} (annotated)</title>
3 <link rel="alternate" type="application/atom+xml" href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
3 <link rel="alternate" type="application/atom+xml" href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
4 <link rel="alternate" type="application/rss+xml" href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
4 <link rel="alternate" type="application/rss+xml" href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
5 </head>
5 </head>
6
6
7 <body>
7 <body>
8 <div id="container">
8 <div id="container">
9 <div class="page-header">
9 <div class="page-header">
10 <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate</h1>
10 <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate</h1>
11
11
12 <form action="{url|urlescape}log">
12 <form action="{url|urlescape}log">
13 {sessionvars%hiddenformentry}
13 {sessionvars%hiddenformentry}
14 <dl class="search">
14 <dl class="search">
15 <dt><label>Search: </label></dt>
15 <dt><label>Search: </label></dt>
16 <dd><input type="text" name="rev" /></dd>
16 <dd><input type="text" name="rev" /></dd>
17 </dl>
17 </dl>
18 </form>
18 </form>
19
19
20 <ul class="page-nav">
20 <ul class="page-nav">
21 <li><a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a></li>
21 <li><a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a></li>
22 <li><a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a></li>
22 <li><a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a></li>
23 <li><a href="{url|urlescape}log{sessionvars%urlparameter}">changelog</a></li>
23 <li><a href="{url|urlescape}log{sessionvars%urlparameter}">changelog</a></li>
24 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
24 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
25 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
25 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
26 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
26 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
27 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
27 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
28 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></li>
28 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></li>
29 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
29 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
30 </ul>
30 </ul>
31 </div>
31 </div>
32
32
33 <ul class="submenu">
33 <ul class="submenu">
34 <li><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
34 <li><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
35 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
35 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
36 <li class="current">annotate</li>
36 <li class="current">annotate</li>
37 <li><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
37 <li><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
38 <li><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
38 <li><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
39 <li><a href="{url|urlescape}raw-annotate/{node|short}/{file|urlescape}">raw</a></li>
39 <li><a href="{url|urlescape}raw-annotate/{node|short}/{file|urlescape}">raw</a></li>
40 </ul>
40 </ul>
41
41
42 <h2 class="no-link no-border">{file|escape}@{node|short} (annotated)</h2>
42 <h2 class="no-link no-border">{file|escape}@{node|short} (annotated)</h2>
43 <h3 class="changeset">{file|escape}</h3>
43 <h3 class="changeset">{file|escape}</h3>
44 <p class="changeset-age age">{date|rfc822date}</p>
44 <p class="changeset-age age">{date|rfc822date}</p>
45
45
46 <dl class="overview">
46 <dl class="overview">
47 <dt>author</dt>
47 <dt>author</dt>
48 <dd>{author|obfuscate}</dd>
48 <dd>{author|obfuscate}</dd>
49 <dt>date</dt>
49 <dt>date</dt>
50 <dd>{date|rfc822date}</dd>
50 <dd>{date|rfc822date}</dd>
51 {branch%filerevbranch}
51 {branch%filerevbranch}
52 <dt>changeset {rev}</dt>
52 <dt>changeset {rev}</dt>
53 <dd><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>
53 <dd><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>
54 {parent%fileannotateparent}
54 {parent%fileannotateparent}
55 {child%fileannotatechild}
55 {child%fileannotatechild}
56 <dt>permissions</dt>
56 <dt>permissions</dt>
57 <dd>{permissions|permissions}</dd>
57 <dd>{permissions|permissions}</dd>
58 </dl>
58 </dl>
59
59
60 <p class="description">{desc|strip|escape|addbreaks|nonempty}</p>
60 <p class="description">{desc|strip|escape|websub|addbreaks|nonempty}</p>
61
61
62 <table class="annotated">
62 <table class="annotated">
63 {annotate%annotateline}
63 {annotate%annotateline}
64 </table>
64 </table>
65
65
66 {footer}
66 {footer}
@@ -1,66 +1,66 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape}@{node|short}</title>
2 <title>{repo|escape}: {file|escape}@{node|short}</title>
3 <link rel="alternate" type="application/atom+xml" href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
3 <link rel="alternate" type="application/atom+xml" href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
4 <link rel="alternate" type="application/rss+xml" href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
4 <link rel="alternate" type="application/rss+xml" href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
5 </head>
5 </head>
6
6
7 <body>
7 <body>
8 <div id="container">
8 <div id="container">
9 <div class="page-header">
9 <div class="page-header">
10 <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / file revision</h1>
10 <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / file revision</h1>
11
11
12 <form action="{url|urlescape}log">
12 <form action="{url|urlescape}log">
13 {sessionvars%hiddenformentry}
13 {sessionvars%hiddenformentry}
14 <dl class="search">
14 <dl class="search">
15 <dt><label>Search: </label></dt>
15 <dt><label>Search: </label></dt>
16 <dd><input type="text" name="rev" /></dd>
16 <dd><input type="text" name="rev" /></dd>
17 </dl>
17 </dl>
18 </form>
18 </form>
19
19
20 <ul class="page-nav">
20 <ul class="page-nav">
21 <li><a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a></li>
21 <li><a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a></li>
22 <li><a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a></li>
22 <li><a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a></li>
23 <li><a href="{url|urlescape}changelog{sessionvars%urlparameter}">changelog</a></li>
23 <li><a href="{url|urlescape}changelog{sessionvars%urlparameter}">changelog</a></li>
24 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
24 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
25 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
25 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
26 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
26 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
27 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
27 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
28 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></li>
28 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></li>
29 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
29 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
30 </ul>
30 </ul>
31 </div>
31 </div>
32
32
33 <ul class="submenu">
33 <ul class="submenu">
34 <li class="current">file</li>
34 <li class="current">file</li>
35 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
35 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
36 <li><a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
36 <li><a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
37 <li><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
37 <li><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
38 <li><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
38 <li><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
39 <li><a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a></li>
39 <li><a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a></li>
40 </ul>
40 </ul>
41
41
42 <h2 class="no-link no-border">{file|escape}@{node|short}</h2>
42 <h2 class="no-link no-border">{file|escape}@{node|short}</h2>
43 <h3 class="changeset">{file|escape}</h3>
43 <h3 class="changeset">{file|escape}</h3>
44 <p class="changeset-age age">{date|rfc822date}</p>
44 <p class="changeset-age age">{date|rfc822date}</p>
45
45
46 <dl class="overview">
46 <dl class="overview">
47 <dt>author</dt>
47 <dt>author</dt>
48 <dd>{author|obfuscate}</dd>
48 <dd>{author|obfuscate}</dd>
49 <dt>date</dt>
49 <dt>date</dt>
50 <dd>{date|rfc822date}</dd>
50 <dd>{date|rfc822date}</dd>
51 {branch%filerevbranch}
51 {branch%filerevbranch}
52 <dt>changeset {rev}</dt>
52 <dt>changeset {rev}</dt>
53 <dd><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>
53 <dd><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>
54 {parent%filerevparent}
54 {parent%filerevparent}
55 {child%filerevchild}
55 {child%filerevchild}
56 <dt>permissions</dt>
56 <dt>permissions</dt>
57 <dd>{permissions|permissions}</dd>
57 <dd>{permissions|permissions}</dd>
58 </dl>
58 </dl>
59
59
60 <p class="description">{desc|strip|escape|addbreaks|nonempty}</p>
60 <p class="description">{desc|strip|escape|websub|addbreaks|nonempty}</p>
61
61
62 <div class="source">
62 <div class="source">
63 {text%fileline}
63 {text%fileline}
64 </div>
64 </div>
65
65
66 {footer}
66 {footer}
@@ -1,87 +1,87 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {node|short}</title>
2 <title>{repo|escape}: {node|short}</title>
3 </head>
3 </head>
4 <body>
4 <body>
5 <div class="container">
5 <div class="container">
6 <div class="menu">
6 <div class="menu">
7 <div class="logo">
7 <div class="logo">
8 <a href="{logourl}">
8 <a href="{logourl}">
9 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
9 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
10 </div>
10 </div>
11 <ul>
11 <ul>
12 <li><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
12 <li><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
13 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
13 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
14 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
14 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
15 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
15 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
16 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
16 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
17 </ul>
17 </ul>
18 <ul>
18 <ul>
19 <li class="active">changeset</li>
19 <li class="active">changeset</li>
20 <li><a href="{url|urlescape}raw-rev/{node|short}{sessionvars%urlparameter}">raw</a></li>
20 <li><a href="{url|urlescape}raw-rev/{node|short}{sessionvars%urlparameter}">raw</a></li>
21 <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">browse</a></li>
21 <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">browse</a></li>
22 </ul>
22 </ul>
23 <ul>
23 <ul>
24 {archives%archiveentry}
24 {archives%archiveentry}
25 </ul>
25 </ul>
26 <ul>
26 <ul>
27 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
27 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
28 </ul>
28 </ul>
29 </div>
29 </div>
30
30
31 <div class="main">
31 <div class="main">
32
32
33 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
33 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
34 <h3>changeset {rev}:{node|short} {changesetbranch%changelogbranchname} {changesettag} {changesetbookmark}</h3>
34 <h3>changeset {rev}:{node|short} {changesetbranch%changelogbranchname} {changesettag} {changesetbookmark}</h3>
35
35
36 <form class="search" action="{url|urlescape}log">
36 <form class="search" action="{url|urlescape}log">
37 {sessionvars%hiddenformentry}
37 {sessionvars%hiddenformentry}
38 <p><input name="rev" id="search1" type="text" size="30" /></p>
38 <p><input name="rev" id="search1" type="text" size="30" /></p>
39 <div id="hint">find changesets by author, revision,
39 <div id="hint">find changesets by author, revision,
40 files, or words in the commit message</div>
40 files, or words in the commit message</div>
41 </form>
41 </form>
42
42
43 <div class="description">{desc|strip|escape|nonempty}</div>
43 <div class="description">{desc|strip|escape|websub|nonempty}</div>
44
44
45 <table id="changesetEntry">
45 <table id="changesetEntry">
46 <tr>
46 <tr>
47 <th class="author">author</th>
47 <th class="author">author</th>
48 <td class="author">{author|obfuscate}</td>
48 <td class="author">{author|obfuscate}</td>
49 </tr>
49 </tr>
50 <tr>
50 <tr>
51 <th class="date">date</th>
51 <th class="date">date</th>
52 <td class="date age">{date|rfc822date}</td></tr>
52 <td class="date age">{date|rfc822date}</td></tr>
53 <tr>
53 <tr>
54 <th class="author">parents</th>
54 <th class="author">parents</th>
55 <td class="author">{parent%changesetparent}</td>
55 <td class="author">{parent%changesetparent}</td>
56 </tr>
56 </tr>
57 <tr>
57 <tr>
58 <th class="author">children</th>
58 <th class="author">children</th>
59 <td class="author">{child%changesetchild}</td>
59 <td class="author">{child%changesetchild}</td>
60 </tr>
60 </tr>
61 <tr>
61 <tr>
62 <th class="files">files</th>
62 <th class="files">files</th>
63 <td class="files">{files}</td>
63 <td class="files">{files}</td>
64 </tr>
64 </tr>
65 <tr>
65 <tr>
66 <th class="diffstat">diffstat</th>
66 <th class="diffstat">diffstat</th>
67 <td class="diffstat">
67 <td class="diffstat">
68 {diffsummary}
68 {diffsummary}
69 <a id="diffstatexpand" href="javascript:showDiffstat()"/>[<tt>+</tt>]</a>
69 <a id="diffstatexpand" href="javascript:showDiffstat()"/>[<tt>+</tt>]</a>
70 <div id="diffstatdetails" style="display:none;">
70 <div id="diffstatdetails" style="display:none;">
71 <a href="javascript:hideDiffstat()"/>[<tt>-</tt>]</a>
71 <a href="javascript:hideDiffstat()"/>[<tt>-</tt>]</a>
72 <p>
72 <p>
73 <table>{diffstat}</table>
73 <table>{diffstat}</table>
74 </div>
74 </div>
75 </td>
75 </td>
76 </tr>
76 </tr>
77 </table>
77 </table>
78
78
79 <div class="overflow">
79 <div class="overflow">
80 <div class="sourcefirst"> line diff</div>
80 <div class="sourcefirst"> line diff</div>
81
81
82 {diff}
82 {diff}
83 </div>
83 </div>
84
84
85 </div>
85 </div>
86 </div>
86 </div>
87 {footer}
87 {footer}
@@ -1,83 +1,83 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape} annotate</title>
2 <title>{repo|escape}: {file|escape} annotate</title>
3 </head>
3 </head>
4 <body>
4 <body>
5
5
6 <div class="container">
6 <div class="container">
7 <div class="menu">
7 <div class="menu">
8 <div class="logo">
8 <div class="logo">
9 <a href="{logourl}">
9 <a href="{logourl}">
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
11 </div>
11 </div>
12 <ul>
12 <ul>
13 <li><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
13 <li><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
14 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
16 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
16 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
17 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
17 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
18 </ul>
18 </ul>
19
19
20 <ul>
20 <ul>
21 <li><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
21 <li><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
22 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
22 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
23 </ul>
23 </ul>
24 <ul>
24 <ul>
25 <li><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
25 <li><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
26 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
26 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
27 <li><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
27 <li><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
28 <li><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
28 <li><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
29 <li class="active">annotate</li>
29 <li class="active">annotate</li>
30 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
30 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
31 <li><a href="{url|urlescape}raw-annotate/{node|short}/{file|urlescape}">raw</a></li>
31 <li><a href="{url|urlescape}raw-annotate/{node|short}/{file|urlescape}">raw</a></li>
32 </ul>
32 </ul>
33 <ul>
33 <ul>
34 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
34 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
35 </ul>
35 </ul>
36 </div>
36 </div>
37
37
38 <div class="main">
38 <div class="main">
39 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
39 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
40 <h3>annotate {file|escape} @ {rev}:{node|short}</h3>
40 <h3>annotate {file|escape} @ {rev}:{node|short}</h3>
41
41
42 <form class="search" action="{url|urlescape}log">
42 <form class="search" action="{url|urlescape}log">
43 {sessionvars%hiddenformentry}
43 {sessionvars%hiddenformentry}
44 <p><input name="rev" id="search1" type="text" size="30" /></p>
44 <p><input name="rev" id="search1" type="text" size="30" /></p>
45 <div id="hint">find changesets by author, revision,
45 <div id="hint">find changesets by author, revision,
46 files, or words in the commit message</div>
46 files, or words in the commit message</div>
47 </form>
47 </form>
48
48
49 <div class="description">{desc|strip|escape|nonempty}</div>
49 <div class="description">{desc|strip|escape|websub|nonempty}</div>
50
50
51 <table id="changesetEntry">
51 <table id="changesetEntry">
52 <tr>
52 <tr>
53 <th class="author">author</th>
53 <th class="author">author</th>
54 <td class="author">{author|obfuscate}</td>
54 <td class="author">{author|obfuscate}</td>
55 </tr>
55 </tr>
56 <tr>
56 <tr>
57 <th class="date">date</th>
57 <th class="date">date</th>
58 <td class="date age">{date|rfc822date}</td>
58 <td class="date age">{date|rfc822date}</td>
59 </tr>
59 </tr>
60 <tr>
60 <tr>
61 <th class="author">parents</th>
61 <th class="author">parents</th>
62 <td class="author">{parent%filerevparent}</td>
62 <td class="author">{parent%filerevparent}</td>
63 </tr>
63 </tr>
64 <tr>
64 <tr>
65 <th class="author">children</th>
65 <th class="author">children</th>
66 <td class="author">{child%filerevchild}</td>
66 <td class="author">{child%filerevchild}</td>
67 </tr>
67 </tr>
68 {changesettag}
68 {changesettag}
69 </table>
69 </table>
70
70
71 <div class="overflow">
71 <div class="overflow">
72 <table class="bigtable">
72 <table class="bigtable">
73 <tr>
73 <tr>
74 <th class="annotate">rev</th>
74 <th class="annotate">rev</th>
75 <th class="line">&nbsp;&nbsp;line source</th>
75 <th class="line">&nbsp;&nbsp;line source</th>
76 </tr>
76 </tr>
77 {annotate%annotateline}
77 {annotate%annotateline}
78 </table>
78 </table>
79 </div>
79 </div>
80 </div>
80 </div>
81 </div>
81 </div>
82
82
83 {footer}
83 {footer}
@@ -1,93 +1,93 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape} comparison</title>
2 <title>{repo|escape}: {file|escape} comparison</title>
3 </head>
3 </head>
4 <body>
4 <body>
5
5
6 <div class="container">
6 <div class="container">
7 <div class="menu">
7 <div class="menu">
8 <div class="logo">
8 <div class="logo">
9 <a href="{logourl}">
9 <a href="{logourl}">
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
11 </div>
11 </div>
12 <ul>
12 <ul>
13 <li><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
13 <li><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
14 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
16 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
16 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
17 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
17 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
18 </ul>
18 </ul>
19 <ul>
19 <ul>
20 <li><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
20 <li><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
21 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
21 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
22 </ul>
22 </ul>
23 <ul>
23 <ul>
24 <li><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
24 <li><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
25 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
25 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
26 <li><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
26 <li><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
27 <li class="active">comparison</li>
27 <li class="active">comparison</li>
28 <li><a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
28 <li><a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
29 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
29 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
30 <li><a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a></li>
30 <li><a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a></li>
31 </ul>
31 </ul>
32 <ul>
32 <ul>
33 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
33 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
34 </ul>
34 </ul>
35 </div>
35 </div>
36
36
37 <div class="main">
37 <div class="main">
38 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
38 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
39 <h3>comparison {file|escape} @ {rev}:{node|short}</h3>
39 <h3>comparison {file|escape} @ {rev}:{node|short}</h3>
40
40
41 <form class="search" action="{url|urlescape}log">
41 <form class="search" action="{url|urlescape}log">
42 <p>{sessionvars%hiddenformentry}</p>
42 <p>{sessionvars%hiddenformentry}</p>
43 <p><input name="rev" id="search1" type="text" size="30" /></p>
43 <p><input name="rev" id="search1" type="text" size="30" /></p>
44 <div id="hint">find changesets by author, revision,
44 <div id="hint">find changesets by author, revision,
45 files, or words in the commit message</div>
45 files, or words in the commit message</div>
46 </form>
46 </form>
47
47
48 <div class="description">{desc|strip|escape|nonempty}</div>
48 <div class="description">{desc|strip|escape|websub|nonempty}</div>
49
49
50 <table id="changesetEntry">
50 <table id="changesetEntry">
51 <tr>
51 <tr>
52 <th>author</th>
52 <th>author</th>
53 <td>{author|obfuscate}</td>
53 <td>{author|obfuscate}</td>
54 </tr>
54 </tr>
55 <tr>
55 <tr>
56 <th>date</th>
56 <th>date</th>
57 <td class="date age">{date|rfc822date}</td>
57 <td class="date age">{date|rfc822date}</td>
58 </tr>
58 </tr>
59 <tr>
59 <tr>
60 <th>parents</th>
60 <th>parents</th>
61 <td>{parent%filerevparent}</td>
61 <td>{parent%filerevparent}</td>
62 </tr>
62 </tr>
63 <tr>
63 <tr>
64 <th>children</th>
64 <th>children</th>
65 <td>{child%filerevchild}</td>
65 <td>{child%filerevchild}</td>
66 </tr>
66 </tr>
67 {changesettag}
67 {changesettag}
68 </table>
68 </table>
69
69
70 <div class="overflow">
70 <div class="overflow">
71 <div class="sourcefirst"> comparison</div>
71 <div class="sourcefirst"> comparison</div>
72 <div class="legend">
72 <div class="legend">
73 <span class="legendinfo equal">equal</span>
73 <span class="legendinfo equal">equal</span>
74 <span class="legendinfo delete">deleted</span>
74 <span class="legendinfo delete">deleted</span>
75 <span class="legendinfo insert">inserted</span>
75 <span class="legendinfo insert">inserted</span>
76 <span class="legendinfo replace">replaced</span>
76 <span class="legendinfo replace">replaced</span>
77 </div>
77 </div>
78
78
79 <table class="bigtable">
79 <table class="bigtable">
80 <thead class="header">
80 <thead class="header">
81 <tr>
81 <tr>
82 <th>{leftrev}:{leftnode|short}</th>
82 <th>{leftrev}:{leftnode|short}</th>
83 <th>{rightrev}:{rightnode|short}</th>
83 <th>{rightrev}:{rightnode|short}</th>
84 </tr>
84 </tr>
85 </thead>
85 </thead>
86 {comparison}
86 {comparison}
87 </table>
87 </table>
88
88
89 </div>
89 </div>
90 </div>
90 </div>
91 </div>
91 </div>
92
92
93 {footer}
93 {footer}
@@ -1,78 +1,78 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape} diff</title>
2 <title>{repo|escape}: {file|escape} diff</title>
3 </head>
3 </head>
4 <body>
4 <body>
5
5
6 <div class="container">
6 <div class="container">
7 <div class="menu">
7 <div class="menu">
8 <div class="logo">
8 <div class="logo">
9 <a href="{logourl}">
9 <a href="{logourl}">
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
11 </div>
11 </div>
12 <ul>
12 <ul>
13 <li><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
13 <li><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
14 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
16 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
16 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
17 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
17 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
18 </ul>
18 </ul>
19 <ul>
19 <ul>
20 <li><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
20 <li><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
21 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
21 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
22 </ul>
22 </ul>
23 <ul>
23 <ul>
24 <li><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
24 <li><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
25 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
25 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
26 <li class="active">diff</li>
26 <li class="active">diff</li>
27 <li><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
27 <li><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
28 <li><a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
28 <li><a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
29 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
29 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
30 <li><a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a></li>
30 <li><a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a></li>
31 </ul>
31 </ul>
32 <ul>
32 <ul>
33 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
33 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
34 </ul>
34 </ul>
35 </div>
35 </div>
36
36
37 <div class="main">
37 <div class="main">
38 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
38 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
39 <h3>diff {file|escape} @ {rev}:{node|short}</h3>
39 <h3>diff {file|escape} @ {rev}:{node|short}</h3>
40
40
41 <form class="search" action="{url|urlescape}log">
41 <form class="search" action="{url|urlescape}log">
42 <p>{sessionvars%hiddenformentry}</p>
42 <p>{sessionvars%hiddenformentry}</p>
43 <p><input name="rev" id="search1" type="text" size="30" /></p>
43 <p><input name="rev" id="search1" type="text" size="30" /></p>
44 <div id="hint">find changesets by author, revision,
44 <div id="hint">find changesets by author, revision,
45 files, or words in the commit message</div>
45 files, or words in the commit message</div>
46 </form>
46 </form>
47
47
48 <div class="description">{desc|strip|escape|nonempty}</div>
48 <div class="description">{desc|strip|escape|websub|nonempty}</div>
49
49
50 <table id="changesetEntry">
50 <table id="changesetEntry">
51 <tr>
51 <tr>
52 <th>author</th>
52 <th>author</th>
53 <td>{author|obfuscate}</td>
53 <td>{author|obfuscate}</td>
54 </tr>
54 </tr>
55 <tr>
55 <tr>
56 <th>date</th>
56 <th>date</th>
57 <td class="date age">{date|rfc822date}</td>
57 <td class="date age">{date|rfc822date}</td>
58 </tr>
58 </tr>
59 <tr>
59 <tr>
60 <th>parents</th>
60 <th>parents</th>
61 <td>{parent%filerevparent}</td>
61 <td>{parent%filerevparent}</td>
62 </tr>
62 </tr>
63 <tr>
63 <tr>
64 <th>children</th>
64 <th>children</th>
65 <td>{child%filerevchild}</td>
65 <td>{child%filerevchild}</td>
66 </tr>
66 </tr>
67 {changesettag}
67 {changesettag}
68 </table>
68 </table>
69
69
70 <div class="overflow">
70 <div class="overflow">
71 <div class="sourcefirst"> line diff</div>
71 <div class="sourcefirst"> line diff</div>
72
72
73 {diff}
73 {diff}
74 </div>
74 </div>
75 </div>
75 </div>
76 </div>
76 </div>
77
77
78 {footer}
78 {footer}
@@ -1,77 +1,77 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {node|short} {file|escape}</title>
2 <title>{repo|escape}: {node|short} {file|escape}</title>
3 </head>
3 </head>
4 <body>
4 <body>
5
5
6 <div class="container">
6 <div class="container">
7 <div class="menu">
7 <div class="menu">
8 <div class="logo">
8 <div class="logo">
9 <a href="{logourl}">
9 <a href="{logourl}">
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
11 </div>
11 </div>
12 <ul>
12 <ul>
13 <li><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
13 <li><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
14 <li><a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
16 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
16 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
17 </ul>
17 </ul>
18 <ul>
18 <ul>
19 <li><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
19 <li><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
20 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
20 <li><a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
21 </ul>
21 </ul>
22 <ul>
22 <ul>
23 <li class="active">file</li>
23 <li class="active">file</li>
24 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
24 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
25 <li><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
25 <li><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
26 <li><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
26 <li><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
27 <li><a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
27 <li><a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
28 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
28 <li><a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
29 <li><a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a></li>
29 <li><a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a></li>
30 </ul>
30 </ul>
31 <ul>
31 <ul>
32 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
32 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
33 </ul>
33 </ul>
34 </div>
34 </div>
35
35
36 <div class="main">
36 <div class="main">
37 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
37 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
38 <h3>view {file|escape} @ {rev}:{node|short}</h3>
38 <h3>view {file|escape} @ {rev}:{node|short}</h3>
39
39
40 <form class="search" action="{url|urlescape}log">
40 <form class="search" action="{url|urlescape}log">
41 {sessionvars%hiddenformentry}
41 {sessionvars%hiddenformentry}
42 <p><input name="rev" id="search1" type="text" size="30" /></p>
42 <p><input name="rev" id="search1" type="text" size="30" /></p>
43 <div id="hint">find changesets by author, revision,
43 <div id="hint">find changesets by author, revision,
44 files, or words in the commit message</div>
44 files, or words in the commit message</div>
45 </form>
45 </form>
46
46
47 <div class="description">{desc|strip|escape|nonempty}</div>
47 <div class="description">{desc|strip|escape|websub|nonempty}</div>
48
48
49 <table id="changesetEntry">
49 <table id="changesetEntry">
50 <tr>
50 <tr>
51 <th class="author">author</th>
51 <th class="author">author</th>
52 <td class="author">{author|obfuscate}</td>
52 <td class="author">{author|obfuscate}</td>
53 </tr>
53 </tr>
54 <tr>
54 <tr>
55 <th class="date">date</th>
55 <th class="date">date</th>
56 <td class="date age">{date|rfc822date}</td>
56 <td class="date age">{date|rfc822date}</td>
57 </tr>
57 </tr>
58 <tr>
58 <tr>
59 <th class="author">parents</th>
59 <th class="author">parents</th>
60 <td class="author">{parent%filerevparent}</td>
60 <td class="author">{parent%filerevparent}</td>
61 </tr>
61 </tr>
62 <tr>
62 <tr>
63 <th class="author">children</th>
63 <th class="author">children</th>
64 <td class="author">{child%filerevchild}</td>
64 <td class="author">{child%filerevchild}</td>
65 </tr>
65 </tr>
66 {changesettag}
66 {changesettag}
67 </table>
67 </table>
68
68
69 <div class="overflow">
69 <div class="overflow">
70 <div class="sourcefirst"> line source</div>
70 <div class="sourcefirst"> line source</div>
71 {text%fileline}
71 {text%fileline}
72 <div class="sourcelast"></div>
72 <div class="sourcelast"></div>
73 </div>
73 </div>
74 </div>
74 </div>
75 </div>
75 </div>
76
76
77 {footer}
77 {footer}
@@ -1,52 +1,52 b''
1 {header}
1 {header}
2 <title>{repo|escape}: changeset {node|short}</title>
2 <title>{repo|escape}: changeset {node|short}</title>
3 </head>
3 </head>
4 <body>
4 <body>
5
5
6 <div class="buttons">
6 <div class="buttons">
7 <a href="{url|urlescape}log/{rev}{sessionvars%urlparameter}">changelog</a>
7 <a href="{url|urlescape}log/{rev}{sessionvars%urlparameter}">changelog</a>
8 <a href="{url|urlescape}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
8 <a href="{url|urlescape}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
9 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a>
9 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a>
10 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a>
10 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a>
11 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a>
11 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a>
12 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a>
12 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a>
13 <a href="{url|urlescape}raw-rev/{node|short}">raw</a>
13 <a href="{url|urlescape}raw-rev/{node|short}">raw</a>
14 {archives%archiveentry}
14 {archives%archiveentry}
15 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
15 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
16 </div>
16 </div>
17
17
18 <h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / changeset: {desc|strip|escape|firstline|nonempty}</h2>
18 <h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / changeset: {desc|strip|escape|firstline|nonempty}</h2>
19
19
20 <table id="changesetEntry">
20 <table id="changesetEntry">
21 <tr>
21 <tr>
22 <th class="changeset">changeset {rev}:</th>
22 <th class="changeset">changeset {rev}:</th>
23 <td class="changeset"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
23 <td class="changeset"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
24 </tr>
24 </tr>
25 {parent%changesetparent}
25 {parent%changesetparent}
26 {child%changesetchild}
26 {child%changesetchild}
27 {changesettag}
27 {changesettag}
28 <tr>
28 <tr>
29 <th class="author">author:</th>
29 <th class="author">author:</th>
30 <td class="author">{author|obfuscate}</td>
30 <td class="author">{author|obfuscate}</td>
31 </tr>
31 </tr>
32 <tr>
32 <tr>
33 <th class="date">date:</th>
33 <th class="date">date:</th>
34 <td class="date age">{date|rfc822date}</td>
34 <td class="date age">{date|rfc822date}</td>
35 </tr>
35 </tr>
36 <tr>
36 <tr>
37 <th class="files">files:</th>
37 <th class="files">files:</th>
38 <td class="files">{files}</td>
38 <td class="files">{files}</td>
39 </tr>
39 </tr>
40 <tr>
40 <tr>
41 <th class="description">description:</th>
41 <th class="description">description:</th>
42 <td class="description">{desc|strip|escape|addbreaks|nonempty}</td>
42 <td class="description">{desc|strip|escape|websub|addbreaks|nonempty}</td>
43 </tr>
43 </tr>
44 </table>
44 </table>
45
45
46 <div id="changesetDiff">
46 <div id="changesetDiff">
47 {diff}
47 {diff}
48 </div>
48 </div>
49
49
50 {footer}
50 {footer}
51
51
52
52
@@ -1,49 +1,49 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape} annotate</title>
2 <title>{repo|escape}: {file|escape} annotate</title>
3 </head>
3 </head>
4 <body>
4 <body>
5
5
6 <div class="buttons">
6 <div class="buttons">
7 <a href="{url|urlescape}log/{rev}{sessionvars%urlparameter}">changelog</a>
7 <a href="{url|urlescape}log/{rev}{sessionvars%urlparameter}">changelog</a>
8 <a href="{url|urlescape}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
8 <a href="{url|urlescape}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
9 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a>
9 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a>
10 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a>
10 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a>
11 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a>
11 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a>
12 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
12 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
13 <a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a>
13 <a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a>
14 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>
14 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>
15 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
15 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
16 <a href="{url|urlescape}raw-annotate/{node|short}/{file|urlescape}">raw</a>
16 <a href="{url|urlescape}raw-annotate/{node|short}/{file|urlescape}">raw</a>
17 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
17 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
18 </div>
18 </div>
19
19
20 <h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate {file|escape}</h2>
20 <h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate {file|escape}</h2>
21
21
22 <table>
22 <table>
23 <tr>
23 <tr>
24 <td class="metatag">changeset {rev}:</td>
24 <td class="metatag">changeset {rev}:</td>
25 <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
25 <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
26 {parent%fileannotateparent}
26 {parent%fileannotateparent}
27 {child%fileannotatechild}
27 {child%fileannotatechild}
28 <tr>
28 <tr>
29 <td class="metatag">author:</td>
29 <td class="metatag">author:</td>
30 <td>{author|obfuscate}</td></tr>
30 <td>{author|obfuscate}</td></tr>
31 <tr>
31 <tr>
32 <td class="metatag">date:</td>
32 <td class="metatag">date:</td>
33 <td class="date age">{date|rfc822date}</td>
33 <td class="date age">{date|rfc822date}</td>
34 </tr>
34 </tr>
35 <tr>
35 <tr>
36 <td class="metatag">permissions:</td>
36 <td class="metatag">permissions:</td>
37 <td>{permissions|permissions}</td>
37 <td>{permissions|permissions}</td>
38 </tr>
38 </tr>
39 <tr>
39 <tr>
40 <td class="metatag">description:</td>
40 <td class="metatag">description:</td>
41 <td>{desc|strip|escape|addbreaks|nonempty}</td>
41 <td>{desc|strip|escape|websub|addbreaks|nonempty}</td>
42 </tr>
42 </tr>
43 </table>
43 </table>
44
44
45 <table cellspacing="0" cellpadding="0">
45 <table cellspacing="0" cellpadding="0">
46 {annotate%annotateline}
46 {annotate%annotateline}
47 </table>
47 </table>
48
48
49 {footer}
49 {footer}
@@ -1,47 +1,47 b''
1 {header}
1 {header}
2 <title>{repo|escape}:{file|escape}</title>
2 <title>{repo|escape}:{file|escape}</title>
3 </head>
3 </head>
4 <body>
4 <body>
5
5
6 <div class="buttons">
6 <div class="buttons">
7 <a href="{url|urlescape}log/{rev}{sessionvars%urlparameter}">changelog</a>
7 <a href="{url|urlescape}log/{rev}{sessionvars%urlparameter}">changelog</a>
8 <a href="{url|urlescape}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
8 <a href="{url|urlescape}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
9 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a>
9 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a>
10 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a>
10 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a>
11 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a>
11 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a>
12 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
12 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
13 <a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a>
13 <a href="{url|urlescape}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a>
14 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
14 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
15 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
15 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
16 <a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a>
16 <a href="{url|urlescape}raw-file/{node|short}/{file|urlescape}">raw</a>
17 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
17 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
18 </div>
18 </div>
19
19
20 <h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / {file|escape}</h2>
20 <h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / {file|escape}</h2>
21
21
22 <table>
22 <table>
23 <tr>
23 <tr>
24 <td class="metatag">changeset {rev}:</td>
24 <td class="metatag">changeset {rev}:</td>
25 <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
25 <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
26 {parent%filerevparent}
26 {parent%filerevparent}
27 {child%filerevchild}
27 {child%filerevchild}
28 <tr>
28 <tr>
29 <td class="metatag">author:</td>
29 <td class="metatag">author:</td>
30 <td>{author|obfuscate}</td></tr>
30 <td>{author|obfuscate}</td></tr>
31 <tr>
31 <tr>
32 <td class="metatag">date:</td>
32 <td class="metatag">date:</td>
33 <td class="date age">{date|rfc822date}</td></tr>
33 <td class="date age">{date|rfc822date}</td></tr>
34 <tr>
34 <tr>
35 <td class="metatag">permissions:</td>
35 <td class="metatag">permissions:</td>
36 <td>{permissions|permissions}</td></tr>
36 <td>{permissions|permissions}</td></tr>
37 <tr>
37 <tr>
38 <td class="metatag">description:</td>
38 <td class="metatag">description:</td>
39 <td>{desc|strip|escape|addbreaks|nonempty}</td>
39 <td>{desc|strip|escape|websub|addbreaks|nonempty}</td>
40 </tr>
40 </tr>
41 </table>
41 </table>
42
42
43 <pre>
43 <pre>
44 {text%fileline}
44 {text%fileline}
45 </pre>
45 </pre>
46
46
47 {footer}
47 {footer}
@@ -1,256 +1,260 b''
1 $ "$TESTDIR/hghave" symlink || exit 80
1 $ "$TESTDIR/hghave" symlink || exit 80
2
2
3 == tests added in 0.7 ==
3 == tests added in 0.7 ==
4
4
5 $ hg init test-symlinks-0.7; cd test-symlinks-0.7;
5 $ hg init test-symlinks-0.7; cd test-symlinks-0.7;
6 $ touch foo; ln -s foo bar;
6 $ touch foo; ln -s foo bar;
7
7
8 import with addremove -- symlink walking should _not_ screwup.
8 import with addremove -- symlink walking should _not_ screwup.
9
9
10 $ hg addremove
10 $ hg addremove
11 adding bar
11 adding bar
12 adding foo
12 adding foo
13
13
14 commit -- the symlink should _not_ appear added to dir state
14 commit -- the symlink should _not_ appear added to dir state
15
15
16 $ hg commit -m 'initial'
16 $ hg commit -m 'initial'
17
17
18 $ touch bomb
18 $ touch bomb
19
19
20 again, symlink should _not_ show up on dir state
20 again, symlink should _not_ show up on dir state
21
21
22 $ hg addremove
22 $ hg addremove
23 adding bomb
23 adding bomb
24
24
25 Assert screamed here before, should go by without consequence
25 Assert screamed here before, should go by without consequence
26
26
27 $ hg commit -m 'is there a bug?'
27 $ hg commit -m 'is there a bug?'
28 $ cd ..
28 $ cd ..
29
29
30
30
31 == fifo & ignore ==
31 == fifo & ignore ==
32
32
33 $ hg init test; cd test;
33 $ hg init test; cd test;
34
34
35 $ mkdir dir
35 $ mkdir dir
36 $ touch a.c dir/a.o dir/b.o
36 $ touch a.c dir/a.o dir/b.o
37
37
38 test what happens if we want to trick hg
38 test what happens if we want to trick hg
39
39
40 $ hg commit -A -m 0
40 $ hg commit -A -m 0
41 adding a.c
41 adding a.c
42 adding dir/a.o
42 adding dir/a.o
43 adding dir/b.o
43 adding dir/b.o
44 $ echo "relglob:*.o" > .hgignore
44 $ echo "relglob:*.o" > .hgignore
45 $ rm a.c
45 $ rm a.c
46 $ rm dir/a.o
46 $ rm dir/a.o
47 $ rm dir/b.o
47 $ rm dir/b.o
48 $ mkdir dir/a.o
48 $ mkdir dir/a.o
49 $ ln -s nonexistent dir/b.o
49 $ ln -s nonexistent dir/b.o
50 $ mkfifo a.c
50 $ mkfifo a.c
51
51
52 it should show a.c, dir/a.o and dir/b.o deleted
52 it should show a.c, dir/a.o and dir/b.o deleted
53
53
54 $ hg status
54 $ hg status
55 M dir/b.o
55 M dir/b.o
56 ! a.c
56 ! a.c
57 ! dir/a.o
57 ! dir/a.o
58 ? .hgignore
58 ? .hgignore
59 $ hg status a.c
59 $ hg status a.c
60 a.c: unsupported file type (type is fifo)
60 a.c: unsupported file type (type is fifo)
61 ! a.c
61 ! a.c
62 $ cd ..
62 $ cd ..
63
63
64
64
65 == symlinks from outside the tree ==
65 == symlinks from outside the tree ==
66
66
67 test absolute path through symlink outside repo
67 test absolute path through symlink outside repo
68
68
69 $ p=`pwd`
69 $ p=`pwd`
70 $ hg init x
70 $ hg init x
71 $ ln -s x y
71 $ ln -s x y
72 $ cd x
72 $ cd x
73 $ touch f
73 $ touch f
74 $ hg add f
74 $ hg add f
75 $ hg status "$p"/y/f
75 $ hg status "$p"/y/f
76 A f
76 A f
77
77
78 try symlink outside repo to file inside
78 try symlink outside repo to file inside
79
79
80 $ ln -s x/f ../z
80 $ ln -s x/f ../z
81
81
82 this should fail
82 this should fail
83
83
84 $ hg status ../z && { echo hg mistakenly exited with status 0; exit 1; } || :
84 $ hg status ../z && { echo hg mistakenly exited with status 0; exit 1; } || :
85 abort: ../z not under root '$TESTTMP/x'
85 abort: ../z not under root '$TESTTMP/x'
86 $ cd ..
86 $ cd ..
87
87
88
88
89 == cloning symlinks ==
89 == cloning symlinks ==
90 $ hg init clone; cd clone;
90 $ hg init clone; cd clone;
91
91
92 try cloning symlink in a subdir
92 try cloning symlink in a subdir
93 1. commit a symlink
93 1. commit a symlink
94
94
95 $ mkdir -p a/b/c
95 $ mkdir -p a/b/c
96 $ cd a/b/c
96 $ cd a/b/c
97 $ ln -s /path/to/symlink/source demo
97 $ ln -s /path/to/symlink/source demo
98 $ cd ../../..
98 $ cd ../../..
99 $ hg stat
99 $ hg stat
100 ? a/b/c/demo
100 ? a/b/c/demo
101 $ hg commit -A -m 'add symlink in a/b/c subdir'
101 $ hg commit -A -m 'add symlink in a/b/c subdir'
102 adding a/b/c/demo
102 adding a/b/c/demo
103
103
104 2. clone it
104 2. clone it
105
105
106 $ cd ..
106 $ cd ..
107 $ hg clone clone clonedest
107 $ hg clone clone clonedest
108 updating to branch default
108 updating to branch default
109 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
110
110
111
111
112 == symlink and git diffs ==
112 == symlink and git diffs ==
113
113
114 git symlink diff
114 git symlink diff
115
115
116 $ cd clonedest
116 $ cd clonedest
117 $ hg diff --git -r null:tip
117 $ hg diff --git -r null:tip
118 diff --git a/a/b/c/demo b/a/b/c/demo
118 diff --git a/a/b/c/demo b/a/b/c/demo
119 new file mode 120000
119 new file mode 120000
120 --- /dev/null
120 --- /dev/null
121 +++ b/a/b/c/demo
121 +++ b/a/b/c/demo
122 @@ -0,0 +1,1 @@
122 @@ -0,0 +1,1 @@
123 +/path/to/symlink/source
123 +/path/to/symlink/source
124 \ No newline at end of file
124 \ No newline at end of file
125 $ hg export --git tip > ../sl.diff
125 $ hg export --git tip > ../sl.diff
126
126
127 import git symlink diff
127 import git symlink diff
128
128
129 $ hg rm a/b/c/demo
129 $ hg rm a/b/c/demo
130 $ hg commit -m'remove link'
130 $ hg commit -m'remove link'
131 $ hg import ../sl.diff
131 $ hg import ../sl.diff
132 applying ../sl.diff
132 applying ../sl.diff
133 $ hg diff --git -r 1:tip
133 $ hg diff --git -r 1:tip
134 diff --git a/a/b/c/demo b/a/b/c/demo
134 diff --git a/a/b/c/demo b/a/b/c/demo
135 new file mode 120000
135 new file mode 120000
136 --- /dev/null
136 --- /dev/null
137 +++ b/a/b/c/demo
137 +++ b/a/b/c/demo
138 @@ -0,0 +1,1 @@
138 @@ -0,0 +1,1 @@
139 +/path/to/symlink/source
139 +/path/to/symlink/source
140 \ No newline at end of file
140 \ No newline at end of file
141
141
142 == symlinks and addremove ==
142 == symlinks and addremove ==
143
143
144 directory moved and symlinked
144 directory moved and symlinked
145
145
146 $ mkdir foo
146 $ mkdir foo
147 $ touch foo/a
147 $ touch foo/a
148 $ hg ci -Ama
148 $ hg ci -Ama
149 adding foo/a
149 adding foo/a
150 $ mv foo bar
150 $ mv foo bar
151 $ ln -s bar foo
151 $ ln -s bar foo
152 $ hg status
153 ! foo/a
154 ? bar/a
155 ? foo
152
156
153 now addremove should remove old files
157 now addremove should remove old files
154
158
155 $ hg addremove
159 $ hg addremove
156 adding bar/a
160 adding bar/a
157 adding foo
161 adding foo
158 removing foo/a
162 removing foo/a
159 $ cd ..
163 $ cd ..
160
164
161 == root of repository is symlinked ==
165 == root of repository is symlinked ==
162
166
163 $ hg init root
167 $ hg init root
164 $ ln -s root link
168 $ ln -s root link
165 $ cd root
169 $ cd root
166 $ echo foo > foo
170 $ echo foo > foo
167 $ hg status
171 $ hg status
168 ? foo
172 ? foo
169 $ hg status ../link
173 $ hg status ../link
170 ? foo
174 ? foo
171 $ hg add foo
175 $ hg add foo
172 $ hg cp foo "$TESTTMP/link/bar"
176 $ hg cp foo "$TESTTMP/link/bar"
173 foo has not been committed yet, so no copy data will be stored for bar.
177 foo has not been committed yet, so no copy data will be stored for bar.
174 $ cd ..
178 $ cd ..
175
179
176
180
177 $ hg init b
181 $ hg init b
178 $ cd b
182 $ cd b
179 $ ln -s nothing dangling
183 $ ln -s nothing dangling
180 $ hg commit -m 'commit symlink without adding' dangling
184 $ hg commit -m 'commit symlink without adding' dangling
181 abort: dangling: file not tracked!
185 abort: dangling: file not tracked!
182 [255]
186 [255]
183 $ hg add dangling
187 $ hg add dangling
184 $ hg commit -m 'add symlink'
188 $ hg commit -m 'add symlink'
185
189
186 $ hg tip -v
190 $ hg tip -v
187 changeset: 0:cabd88b706fc
191 changeset: 0:cabd88b706fc
188 tag: tip
192 tag: tip
189 user: test
193 user: test
190 date: Thu Jan 01 00:00:00 1970 +0000
194 date: Thu Jan 01 00:00:00 1970 +0000
191 files: dangling
195 files: dangling
192 description:
196 description:
193 add symlink
197 add symlink
194
198
195
199
196 $ hg manifest --debug
200 $ hg manifest --debug
197 2564acbe54bbbedfbf608479340b359f04597f80 644 @ dangling
201 2564acbe54bbbedfbf608479340b359f04597f80 644 @ dangling
198 $ "$TESTDIR/readlink.py" dangling
202 $ "$TESTDIR/readlink.py" dangling
199 dangling -> nothing
203 dangling -> nothing
200
204
201 $ rm dangling
205 $ rm dangling
202 $ ln -s void dangling
206 $ ln -s void dangling
203 $ hg commit -m 'change symlink'
207 $ hg commit -m 'change symlink'
204 $ "$TESTDIR/readlink.py" dangling
208 $ "$TESTDIR/readlink.py" dangling
205 dangling -> void
209 dangling -> void
206
210
207
211
208 modifying link
212 modifying link
209
213
210 $ rm dangling
214 $ rm dangling
211 $ ln -s empty dangling
215 $ ln -s empty dangling
212 $ "$TESTDIR/readlink.py" dangling
216 $ "$TESTDIR/readlink.py" dangling
213 dangling -> empty
217 dangling -> empty
214
218
215
219
216 reverting to rev 0:
220 reverting to rev 0:
217
221
218 $ hg revert -r 0 -a
222 $ hg revert -r 0 -a
219 reverting dangling
223 reverting dangling
220 $ "$TESTDIR/readlink.py" dangling
224 $ "$TESTDIR/readlink.py" dangling
221 dangling -> nothing
225 dangling -> nothing
222
226
223
227
224 backups:
228 backups:
225
229
226 $ "$TESTDIR/readlink.py" *.orig
230 $ "$TESTDIR/readlink.py" *.orig
227 dangling.orig -> empty
231 dangling.orig -> empty
228 $ rm *.orig
232 $ rm *.orig
229 $ hg up -C
233 $ hg up -C
230 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
231
235
232 copies
236 copies
233
237
234 $ hg cp -v dangling dangling2
238 $ hg cp -v dangling dangling2
235 copying dangling to dangling2
239 copying dangling to dangling2
236 $ hg st -Cmard
240 $ hg st -Cmard
237 A dangling2
241 A dangling2
238 dangling
242 dangling
239 $ "$TESTDIR/readlink.py" dangling dangling2
243 $ "$TESTDIR/readlink.py" dangling dangling2
240 dangling -> void
244 dangling -> void
241 dangling2 -> void
245 dangling2 -> void
242
246
243
247
244 Issue995: hg copy -A incorrectly handles symbolic links
248 Issue995: hg copy -A incorrectly handles symbolic links
245
249
246 $ hg up -C
250 $ hg up -C
247 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
251 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 $ mkdir dir
252 $ mkdir dir
249 $ ln -s dir dirlink
253 $ ln -s dir dirlink
250 $ hg ci -qAm 'add dirlink'
254 $ hg ci -qAm 'add dirlink'
251 $ mkdir newdir
255 $ mkdir newdir
252 $ mv dir newdir/dir
256 $ mv dir newdir/dir
253 $ mv dirlink newdir/dirlink
257 $ mv dirlink newdir/dirlink
254 $ hg mv -A dirlink newdir/dirlink
258 $ hg mv -A dirlink newdir/dirlink
255
259
256 $ cd ..
260 $ cd ..
@@ -1,33 +1,36 b''
1 $ "$TESTDIR/hghave" serve || exit 80
1 $ "$TESTDIR/hghave" serve || exit 80
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5
5
6 $ cat > .hg/hgrc <<EOF
6 $ cat > .hg/hgrc <<EOF
7 > [extensions]
7 > [extensions]
8 > # this is only necessary to check that the mapping from
9 > # interhg to websub works
8 > interhg =
10 > interhg =
9 >
11 >
12 > [websub]
13 > issues = s|Issue(\d+)|<a href="http://bts.example.org/issue\1">Issue\1</a>|
14 >
10 > [interhg]
15 > [interhg]
11 > issues = s|Issue(\d+)|<a href="http://bts.example.org/issue\1">Issue\1</a>|
16 > # check that we maintain some interhg backwards compatibility...
12 >
13 > # yes, 'x' is a weird delimiter...
17 > # yes, 'x' is a weird delimiter...
14 > markbugs = sxbugx<i class="\x">bug</i>x
18 > markbugs = sxbugx<i class="\x">bug</i>x
15 > EOF
19 > EOF
16
20
17 $ touch foo
21 $ touch foo
18 $ hg add foo
22 $ hg add foo
19 $ hg commit -d '1 0' -m 'Issue123: fixed the bug!'
23 $ hg commit -d '1 0' -m 'Issue123: fixed the bug!'
20
24
21 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
25 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
22 $ cat hg.pid >> $DAEMON_PIDS
26 $ cat hg.pid >> $DAEMON_PIDS
23
27
24 log
28 log
25
29
26 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '' | grep bts
30 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT "rev/tip" | grep bts
27 <td class="description"><a href="/rev/1b0e7ece6bd6"><a href="http://bts.example.org/issue123">Issue123</a>: fixed the <i class="x">bug</i>!</a><span class="branchhead">default</span> <span class="tag">tip</span> </td>
31 <div class="description"><a href="http://bts.example.org/issue123">Issue123</a>: fixed the <i class="x">bug</i>!</div>
28
29 errors
32 errors
30
33
31 $ cat errors.log
34 $ cat errors.log
32
35
33 $ cd ..
36 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now