##// END OF EJS Templates
Do not use 'glob' expansion by default on OS != 'nt'
Benoit Boissinot -
r1413:1c64c628 default
parent child Browse files
Show More
@@ -1,2242 +1,2242
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 demandload(globals(), "fancyopts ui hg util lock revlog")
12 demandload(globals(), "fancyopts ui hg util lock revlog")
13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
14 demandload(globals(), "errno socket version struct atexit sets bz2")
14 demandload(globals(), "errno socket version struct atexit sets bz2")
15
15
16 class UnknownCommand(Exception):
16 class UnknownCommand(Exception):
17 """Exception raised if command is not in the command table."""
17 """Exception raised if command is not in the command table."""
18
18
19 def filterfiles(filters, files):
19 def filterfiles(filters, files):
20 l = [x for x in files if x in filters]
20 l = [x for x in files if x in filters]
21
21
22 for t in filters:
22 for t in filters:
23 if t and t[-1] != "/":
23 if t and t[-1] != "/":
24 t += "/"
24 t += "/"
25 l += [x for x in files if x.startswith(t)]
25 l += [x for x in files if x.startswith(t)]
26 return l
26 return l
27
27
28 def relpath(repo, args):
28 def relpath(repo, args):
29 cwd = repo.getcwd()
29 cwd = repo.getcwd()
30 if cwd:
30 if cwd:
31 return [util.normpath(os.path.join(cwd, x)) for x in args]
31 return [util.normpath(os.path.join(cwd, x)) for x in args]
32 return args
32 return args
33
33
34 def matchpats(repo, cwd, pats=[], opts={}, head=''):
34 def matchpats(repo, cwd, pats=[], opts={}, head=''):
35 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
35 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
36 opts.get('exclude'), head)
36 opts.get('exclude'), head)
37
37
38 def makewalk(repo, pats, opts, head=''):
38 def makewalk(repo, pats, opts, head=''):
39 cwd = repo.getcwd()
39 cwd = repo.getcwd()
40 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
40 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
41 exact = dict(zip(files, files))
41 exact = dict(zip(files, files))
42 def walk():
42 def walk():
43 for src, fn in repo.walk(files=files, match=matchfn):
43 for src, fn in repo.walk(files=files, match=matchfn):
44 yield src, fn, util.pathto(cwd, fn), fn in exact
44 yield src, fn, util.pathto(cwd, fn), fn in exact
45 return files, matchfn, walk()
45 return files, matchfn, walk()
46
46
47 def walk(repo, pats, opts, head=''):
47 def walk(repo, pats, opts, head=''):
48 files, matchfn, results = makewalk(repo, pats, opts, head)
48 files, matchfn, results = makewalk(repo, pats, opts, head)
49 for r in results:
49 for r in results:
50 yield r
50 yield r
51
51
52 def walkchangerevs(ui, repo, cwd, pats, opts):
52 def walkchangerevs(ui, repo, cwd, pats, opts):
53 '''Iterate over files and the revs they changed in.
53 '''Iterate over files and the revs they changed in.
54
54
55 Callers most commonly need to iterate backwards over the history
55 Callers most commonly need to iterate backwards over the history
56 it is interested in. Doing so has awful (quadratic-looking)
56 it is interested in. Doing so has awful (quadratic-looking)
57 performance, so we use iterators in a "windowed" way.
57 performance, so we use iterators in a "windowed" way.
58
58
59 We walk a window of revisions in the desired order. Within the
59 We walk a window of revisions in the desired order. Within the
60 window, we first walk forwards to gather data, then in the desired
60 window, we first walk forwards to gather data, then in the desired
61 order (usually backwards) to display it.
61 order (usually backwards) to display it.
62
62
63 This function returns an (iterator, getchange) pair. The
63 This function returns an (iterator, getchange) pair. The
64 getchange function returns the changelog entry for a numeric
64 getchange function returns the changelog entry for a numeric
65 revision. The iterator yields 3-tuples. They will be of one of
65 revision. The iterator yields 3-tuples. They will be of one of
66 the following forms:
66 the following forms:
67
67
68 "window", incrementing, lastrev: stepping through a window,
68 "window", incrementing, lastrev: stepping through a window,
69 positive if walking forwards through revs, last rev in the
69 positive if walking forwards through revs, last rev in the
70 sequence iterated over - use to reset state for the current window
70 sequence iterated over - use to reset state for the current window
71
71
72 "add", rev, fns: out-of-order traversal of the given file names
72 "add", rev, fns: out-of-order traversal of the given file names
73 fns, which changed during revision rev - use to gather data for
73 fns, which changed during revision rev - use to gather data for
74 possible display
74 possible display
75
75
76 "iter", rev, None: in-order traversal of the revs earlier iterated
76 "iter", rev, None: in-order traversal of the revs earlier iterated
77 over with "add" - use to display data'''
77 over with "add" - use to display data'''
78
78
79 if repo.changelog.count() == 0:
79 if repo.changelog.count() == 0:
80 return [], False
80 return [], False
81
81
82 cwd = repo.getcwd()
82 cwd = repo.getcwd()
83 if not pats and cwd:
83 if not pats and cwd:
84 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
84 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
85 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
85 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
86 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
86 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
87 pats, opts)
87 pats, opts)
88 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
88 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
89 wanted = {}
89 wanted = {}
90 slowpath = anypats
90 slowpath = anypats
91 window = 300
91 window = 300
92 fncache = {}
92 fncache = {}
93
93
94 chcache = {}
94 chcache = {}
95 def getchange(rev):
95 def getchange(rev):
96 ch = chcache.get(rev)
96 ch = chcache.get(rev)
97 if ch is None:
97 if ch is None:
98 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
98 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
99 return ch
99 return ch
100
100
101 if not slowpath and not files:
101 if not slowpath and not files:
102 # No files, no patterns. Display all revs.
102 # No files, no patterns. Display all revs.
103 wanted = dict(zip(revs, revs))
103 wanted = dict(zip(revs, revs))
104 if not slowpath:
104 if not slowpath:
105 # Only files, no patterns. Check the history of each file.
105 # Only files, no patterns. Check the history of each file.
106 def filerevgen(filelog):
106 def filerevgen(filelog):
107 for i in xrange(filelog.count() - 1, -1, -window):
107 for i in xrange(filelog.count() - 1, -1, -window):
108 revs = []
108 revs = []
109 for j in xrange(max(0, i - window), i + 1):
109 for j in xrange(max(0, i - window), i + 1):
110 revs.append(filelog.linkrev(filelog.node(j)))
110 revs.append(filelog.linkrev(filelog.node(j)))
111 revs.reverse()
111 revs.reverse()
112 for rev in revs:
112 for rev in revs:
113 yield rev
113 yield rev
114
114
115 minrev, maxrev = min(revs), max(revs)
115 minrev, maxrev = min(revs), max(revs)
116 for file in files:
116 for file in files:
117 filelog = repo.file(file)
117 filelog = repo.file(file)
118 # A zero count may be a directory or deleted file, so
118 # A zero count may be a directory or deleted file, so
119 # try to find matching entries on the slow path.
119 # try to find matching entries on the slow path.
120 if filelog.count() == 0:
120 if filelog.count() == 0:
121 slowpath = True
121 slowpath = True
122 break
122 break
123 for rev in filerevgen(filelog):
123 for rev in filerevgen(filelog):
124 if rev <= maxrev:
124 if rev <= maxrev:
125 if rev < minrev:
125 if rev < minrev:
126 break
126 break
127 fncache.setdefault(rev, [])
127 fncache.setdefault(rev, [])
128 fncache[rev].append(file)
128 fncache[rev].append(file)
129 wanted[rev] = 1
129 wanted[rev] = 1
130 if slowpath:
130 if slowpath:
131 # The slow path checks files modified in every changeset.
131 # The slow path checks files modified in every changeset.
132 def changerevgen():
132 def changerevgen():
133 for i in xrange(repo.changelog.count() - 1, -1, -window):
133 for i in xrange(repo.changelog.count() - 1, -1, -window):
134 for j in xrange(max(0, i - window), i + 1):
134 for j in xrange(max(0, i - window), i + 1):
135 yield j, getchange(j)[3]
135 yield j, getchange(j)[3]
136
136
137 for rev, changefiles in changerevgen():
137 for rev, changefiles in changerevgen():
138 matches = filter(matchfn, changefiles)
138 matches = filter(matchfn, changefiles)
139 if matches:
139 if matches:
140 fncache[rev] = matches
140 fncache[rev] = matches
141 wanted[rev] = 1
141 wanted[rev] = 1
142
142
143 def iterate():
143 def iterate():
144 for i in xrange(0, len(revs), window):
144 for i in xrange(0, len(revs), window):
145 yield 'window', revs[0] < revs[-1], revs[-1]
145 yield 'window', revs[0] < revs[-1], revs[-1]
146 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
146 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
147 if rev in wanted]
147 if rev in wanted]
148 srevs = list(nrevs)
148 srevs = list(nrevs)
149 srevs.sort()
149 srevs.sort()
150 for rev in srevs:
150 for rev in srevs:
151 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
151 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
152 yield 'add', rev, fns
152 yield 'add', rev, fns
153 for rev in nrevs:
153 for rev in nrevs:
154 yield 'iter', rev, None
154 yield 'iter', rev, None
155 return iterate(), getchange
155 return iterate(), getchange
156
156
157 revrangesep = ':'
157 revrangesep = ':'
158
158
159 def revrange(ui, repo, revs, revlog=None):
159 def revrange(ui, repo, revs, revlog=None):
160 """Yield revision as strings from a list of revision specifications."""
160 """Yield revision as strings from a list of revision specifications."""
161 if revlog is None:
161 if revlog is None:
162 revlog = repo.changelog
162 revlog = repo.changelog
163 revcount = revlog.count()
163 revcount = revlog.count()
164 def fix(val, defval):
164 def fix(val, defval):
165 if not val:
165 if not val:
166 return defval
166 return defval
167 try:
167 try:
168 num = int(val)
168 num = int(val)
169 if str(num) != val:
169 if str(num) != val:
170 raise ValueError
170 raise ValueError
171 if num < 0: num += revcount
171 if num < 0: num += revcount
172 if num < 0: num = 0
172 if num < 0: num = 0
173 elif num >= revcount:
173 elif num >= revcount:
174 raise ValueError
174 raise ValueError
175 except ValueError:
175 except ValueError:
176 try:
176 try:
177 num = repo.changelog.rev(repo.lookup(val))
177 num = repo.changelog.rev(repo.lookup(val))
178 except KeyError:
178 except KeyError:
179 try:
179 try:
180 num = revlog.rev(revlog.lookup(val))
180 num = revlog.rev(revlog.lookup(val))
181 except KeyError:
181 except KeyError:
182 raise util.Abort(_('invalid revision identifier %s'), val)
182 raise util.Abort(_('invalid revision identifier %s'), val)
183 return num
183 return num
184 seen = {}
184 seen = {}
185 for spec in revs:
185 for spec in revs:
186 if spec.find(revrangesep) >= 0:
186 if spec.find(revrangesep) >= 0:
187 start, end = spec.split(revrangesep, 1)
187 start, end = spec.split(revrangesep, 1)
188 start = fix(start, 0)
188 start = fix(start, 0)
189 end = fix(end, revcount - 1)
189 end = fix(end, revcount - 1)
190 step = start > end and -1 or 1
190 step = start > end and -1 or 1
191 for rev in xrange(start, end+step, step):
191 for rev in xrange(start, end+step, step):
192 if rev in seen: continue
192 if rev in seen: continue
193 seen[rev] = 1
193 seen[rev] = 1
194 yield str(rev)
194 yield str(rev)
195 else:
195 else:
196 rev = fix(spec, None)
196 rev = fix(spec, None)
197 if rev in seen: continue
197 if rev in seen: continue
198 seen[rev] = 1
198 seen[rev] = 1
199 yield str(rev)
199 yield str(rev)
200
200
201 def make_filename(repo, r, pat, node=None,
201 def make_filename(repo, r, pat, node=None,
202 total=None, seqno=None, revwidth=None, pathname=None):
202 total=None, seqno=None, revwidth=None, pathname=None):
203 node_expander = {
203 node_expander = {
204 'H': lambda: hex(node),
204 'H': lambda: hex(node),
205 'R': lambda: str(r.rev(node)),
205 'R': lambda: str(r.rev(node)),
206 'h': lambda: short(node),
206 'h': lambda: short(node),
207 }
207 }
208 expander = {
208 expander = {
209 '%': lambda: '%',
209 '%': lambda: '%',
210 'b': lambda: os.path.basename(repo.root),
210 'b': lambda: os.path.basename(repo.root),
211 }
211 }
212
212
213 try:
213 try:
214 if node:
214 if node:
215 expander.update(node_expander)
215 expander.update(node_expander)
216 if node and revwidth is not None:
216 if node and revwidth is not None:
217 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
217 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
218 if total is not None:
218 if total is not None:
219 expander['N'] = lambda: str(total)
219 expander['N'] = lambda: str(total)
220 if seqno is not None:
220 if seqno is not None:
221 expander['n'] = lambda: str(seqno)
221 expander['n'] = lambda: str(seqno)
222 if total is not None and seqno is not None:
222 if total is not None and seqno is not None:
223 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
223 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
224 if pathname is not None:
224 if pathname is not None:
225 expander['s'] = lambda: os.path.basename(pathname)
225 expander['s'] = lambda: os.path.basename(pathname)
226 expander['d'] = lambda: os.path.dirname(pathname) or '.'
226 expander['d'] = lambda: os.path.dirname(pathname) or '.'
227 expander['p'] = lambda: pathname
227 expander['p'] = lambda: pathname
228
228
229 newname = []
229 newname = []
230 patlen = len(pat)
230 patlen = len(pat)
231 i = 0
231 i = 0
232 while i < patlen:
232 while i < patlen:
233 c = pat[i]
233 c = pat[i]
234 if c == '%':
234 if c == '%':
235 i += 1
235 i += 1
236 c = pat[i]
236 c = pat[i]
237 c = expander[c]()
237 c = expander[c]()
238 newname.append(c)
238 newname.append(c)
239 i += 1
239 i += 1
240 return ''.join(newname)
240 return ''.join(newname)
241 except KeyError, inst:
241 except KeyError, inst:
242 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
242 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
243 inst.args[0])
243 inst.args[0])
244
244
245 def make_file(repo, r, pat, node=None,
245 def make_file(repo, r, pat, node=None,
246 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
246 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
247 if not pat or pat == '-':
247 if not pat or pat == '-':
248 return 'w' in mode and sys.stdout or sys.stdin
248 return 'w' in mode and sys.stdout or sys.stdin
249 if hasattr(pat, 'write') and 'w' in mode:
249 if hasattr(pat, 'write') and 'w' in mode:
250 return pat
250 return pat
251 if hasattr(pat, 'read') and 'r' in mode:
251 if hasattr(pat, 'read') and 'r' in mode:
252 return pat
252 return pat
253 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
253 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
254 pathname),
254 pathname),
255 mode)
255 mode)
256
256
257 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
257 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
258 changes=None, text=False):
258 changes=None, text=False):
259 if not changes:
259 if not changes:
260 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
260 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
261 else:
261 else:
262 (c, a, d, u) = changes
262 (c, a, d, u) = changes
263 if files:
263 if files:
264 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
264 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
265
265
266 if not c and not a and not d:
266 if not c and not a and not d:
267 return
267 return
268
268
269 if node2:
269 if node2:
270 change = repo.changelog.read(node2)
270 change = repo.changelog.read(node2)
271 mmap2 = repo.manifest.read(change[0])
271 mmap2 = repo.manifest.read(change[0])
272 date2 = util.datestr(change[2])
272 date2 = util.datestr(change[2])
273 def read(f):
273 def read(f):
274 return repo.file(f).read(mmap2[f])
274 return repo.file(f).read(mmap2[f])
275 else:
275 else:
276 date2 = util.datestr()
276 date2 = util.datestr()
277 if not node1:
277 if not node1:
278 node1 = repo.dirstate.parents()[0]
278 node1 = repo.dirstate.parents()[0]
279 def read(f):
279 def read(f):
280 return repo.wfile(f).read()
280 return repo.wfile(f).read()
281
281
282 if ui.quiet:
282 if ui.quiet:
283 r = None
283 r = None
284 else:
284 else:
285 hexfunc = ui.verbose and hex or short
285 hexfunc = ui.verbose and hex or short
286 r = [hexfunc(node) for node in [node1, node2] if node]
286 r = [hexfunc(node) for node in [node1, node2] if node]
287
287
288 change = repo.changelog.read(node1)
288 change = repo.changelog.read(node1)
289 mmap = repo.manifest.read(change[0])
289 mmap = repo.manifest.read(change[0])
290 date1 = util.datestr(change[2])
290 date1 = util.datestr(change[2])
291
291
292 for f in c:
292 for f in c:
293 to = None
293 to = None
294 if f in mmap:
294 if f in mmap:
295 to = repo.file(f).read(mmap[f])
295 to = repo.file(f).read(mmap[f])
296 tn = read(f)
296 tn = read(f)
297 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
297 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
298 for f in a:
298 for f in a:
299 to = None
299 to = None
300 tn = read(f)
300 tn = read(f)
301 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
301 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
302 for f in d:
302 for f in d:
303 to = repo.file(f).read(mmap[f])
303 to = repo.file(f).read(mmap[f])
304 tn = None
304 tn = None
305 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
305 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
306
306
307 def trimuser(ui, name, rev, revcache):
307 def trimuser(ui, name, rev, revcache):
308 """trim the name of the user who committed a change"""
308 """trim the name of the user who committed a change"""
309 user = revcache.get(rev)
309 user = revcache.get(rev)
310 if user is None:
310 if user is None:
311 user = revcache[rev] = ui.shortuser(name)
311 user = revcache[rev] = ui.shortuser(name)
312 return user
312 return user
313
313
314 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
314 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
315 """show a single changeset or file revision"""
315 """show a single changeset or file revision"""
316 log = repo.changelog
316 log = repo.changelog
317 if changenode is None:
317 if changenode is None:
318 changenode = log.node(rev)
318 changenode = log.node(rev)
319 elif not rev:
319 elif not rev:
320 rev = log.rev(changenode)
320 rev = log.rev(changenode)
321
321
322 if ui.quiet:
322 if ui.quiet:
323 ui.write("%d:%s\n" % (rev, short(changenode)))
323 ui.write("%d:%s\n" % (rev, short(changenode)))
324 return
324 return
325
325
326 changes = log.read(changenode)
326 changes = log.read(changenode)
327 date = util.datestr(changes[2])
327 date = util.datestr(changes[2])
328
328
329 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
329 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
330 for p in log.parents(changenode)
330 for p in log.parents(changenode)
331 if ui.debugflag or p != nullid]
331 if ui.debugflag or p != nullid]
332 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
332 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
333 parents = []
333 parents = []
334
334
335 if ui.verbose:
335 if ui.verbose:
336 ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
336 ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
337 else:
337 else:
338 ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
338 ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
339
339
340 for tag in repo.nodetags(changenode):
340 for tag in repo.nodetags(changenode):
341 ui.status(_("tag: %s\n") % tag)
341 ui.status(_("tag: %s\n") % tag)
342 for parent in parents:
342 for parent in parents:
343 ui.write(_("parent: %d:%s\n") % parent)
343 ui.write(_("parent: %d:%s\n") % parent)
344
344
345 if brinfo and changenode in brinfo:
345 if brinfo and changenode in brinfo:
346 br = brinfo[changenode]
346 br = brinfo[changenode]
347 ui.write(_("branch: %s\n") % " ".join(br))
347 ui.write(_("branch: %s\n") % " ".join(br))
348
348
349 ui.debug(_("manifest: %d:%s\n") % (repo.manifest.rev(changes[0]),
349 ui.debug(_("manifest: %d:%s\n") % (repo.manifest.rev(changes[0]),
350 hex(changes[0])))
350 hex(changes[0])))
351 ui.status(_("user: %s\n") % changes[1])
351 ui.status(_("user: %s\n") % changes[1])
352 ui.status(_("date: %s\n") % date)
352 ui.status(_("date: %s\n") % date)
353
353
354 if ui.debugflag:
354 if ui.debugflag:
355 files = repo.changes(log.parents(changenode)[0], changenode)
355 files = repo.changes(log.parents(changenode)[0], changenode)
356 for key, value in zip([_("files:"), _("files+:"), _("files-:")], files):
356 for key, value in zip([_("files:"), _("files+:"), _("files-:")], files):
357 if value:
357 if value:
358 ui.note("%-12s %s\n" % (key, " ".join(value)))
358 ui.note("%-12s %s\n" % (key, " ".join(value)))
359 else:
359 else:
360 ui.note(_("files: %s\n") % " ".join(changes[3]))
360 ui.note(_("files: %s\n") % " ".join(changes[3]))
361
361
362 description = changes[4].strip()
362 description = changes[4].strip()
363 if description:
363 if description:
364 if ui.verbose:
364 if ui.verbose:
365 ui.status(_("description:\n"))
365 ui.status(_("description:\n"))
366 ui.status(description)
366 ui.status(description)
367 ui.status("\n\n")
367 ui.status("\n\n")
368 else:
368 else:
369 ui.status(_("summary: %s\n") % description.splitlines()[0])
369 ui.status(_("summary: %s\n") % description.splitlines()[0])
370 ui.status("\n")
370 ui.status("\n")
371
371
372 def show_version(ui):
372 def show_version(ui):
373 """output version and copyright information"""
373 """output version and copyright information"""
374 ui.write(_("Mercurial Distributed SCM (version %s)\n")
374 ui.write(_("Mercurial Distributed SCM (version %s)\n")
375 % version.get_version())
375 % version.get_version())
376 ui.status(_(
376 ui.status(_(
377 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
377 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
378 "This is free software; see the source for copying conditions. "
378 "This is free software; see the source for copying conditions. "
379 "There is NO\nwarranty; "
379 "There is NO\nwarranty; "
380 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
380 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
381 ))
381 ))
382
382
383 def help_(ui, cmd=None, with_version=False):
383 def help_(ui, cmd=None, with_version=False):
384 """show help for a given command or all commands"""
384 """show help for a given command or all commands"""
385 option_lists = []
385 option_lists = []
386 if cmd and cmd != 'shortlist':
386 if cmd and cmd != 'shortlist':
387 if with_version:
387 if with_version:
388 show_version(ui)
388 show_version(ui)
389 ui.write('\n')
389 ui.write('\n')
390 key, i = find(cmd)
390 key, i = find(cmd)
391 # synopsis
391 # synopsis
392 ui.write("%s\n\n" % i[2])
392 ui.write("%s\n\n" % i[2])
393
393
394 # description
394 # description
395 doc = i[0].__doc__
395 doc = i[0].__doc__
396 if ui.quiet:
396 if ui.quiet:
397 doc = doc.splitlines(0)[0]
397 doc = doc.splitlines(0)[0]
398 ui.write("%s\n" % doc.rstrip())
398 ui.write("%s\n" % doc.rstrip())
399
399
400 if not ui.quiet:
400 if not ui.quiet:
401 # aliases
401 # aliases
402 aliases = ', '.join(key.split('|')[1:])
402 aliases = ', '.join(key.split('|')[1:])
403 if aliases:
403 if aliases:
404 ui.write(_("\naliases: %s\n") % aliases)
404 ui.write(_("\naliases: %s\n") % aliases)
405
405
406 # options
406 # options
407 if i[1]:
407 if i[1]:
408 option_lists.append(("options", i[1]))
408 option_lists.append(("options", i[1]))
409
409
410 else:
410 else:
411 # program name
411 # program name
412 if ui.verbose or with_version:
412 if ui.verbose or with_version:
413 show_version(ui)
413 show_version(ui)
414 else:
414 else:
415 ui.status(_("Mercurial Distributed SCM\n"))
415 ui.status(_("Mercurial Distributed SCM\n"))
416 ui.status('\n')
416 ui.status('\n')
417
417
418 # list of commands
418 # list of commands
419 if cmd == "shortlist":
419 if cmd == "shortlist":
420 ui.status(_('basic commands (use "hg help" '
420 ui.status(_('basic commands (use "hg help" '
421 'for the full list or option "-v" for details):\n\n'))
421 'for the full list or option "-v" for details):\n\n'))
422 elif ui.verbose:
422 elif ui.verbose:
423 ui.status(_('list of commands:\n\n'))
423 ui.status(_('list of commands:\n\n'))
424 else:
424 else:
425 ui.status(_('list of commands (use "hg help -v" '
425 ui.status(_('list of commands (use "hg help -v" '
426 'to show aliases and global options):\n\n'))
426 'to show aliases and global options):\n\n'))
427
427
428 h = {}
428 h = {}
429 cmds = {}
429 cmds = {}
430 for c, e in table.items():
430 for c, e in table.items():
431 f = c.split("|")[0]
431 f = c.split("|")[0]
432 if cmd == "shortlist" and not f.startswith("^"):
432 if cmd == "shortlist" and not f.startswith("^"):
433 continue
433 continue
434 f = f.lstrip("^")
434 f = f.lstrip("^")
435 if not ui.debugflag and f.startswith("debug"):
435 if not ui.debugflag and f.startswith("debug"):
436 continue
436 continue
437 d = ""
437 d = ""
438 if e[0].__doc__:
438 if e[0].__doc__:
439 d = e[0].__doc__.splitlines(0)[0].rstrip()
439 d = e[0].__doc__.splitlines(0)[0].rstrip()
440 h[f] = d
440 h[f] = d
441 cmds[f]=c.lstrip("^")
441 cmds[f]=c.lstrip("^")
442
442
443 fns = h.keys()
443 fns = h.keys()
444 fns.sort()
444 fns.sort()
445 m = max(map(len, fns))
445 m = max(map(len, fns))
446 for f in fns:
446 for f in fns:
447 if ui.verbose:
447 if ui.verbose:
448 commands = cmds[f].replace("|",", ")
448 commands = cmds[f].replace("|",", ")
449 ui.write(" %s:\n %s\n"%(commands,h[f]))
449 ui.write(" %s:\n %s\n"%(commands,h[f]))
450 else:
450 else:
451 ui.write(' %-*s %s\n' % (m, f, h[f]))
451 ui.write(' %-*s %s\n' % (m, f, h[f]))
452
452
453 # global options
453 # global options
454 if ui.verbose:
454 if ui.verbose:
455 option_lists.append(("global options", globalopts))
455 option_lists.append(("global options", globalopts))
456
456
457 # list all option lists
457 # list all option lists
458 opt_output = []
458 opt_output = []
459 for title, options in option_lists:
459 for title, options in option_lists:
460 opt_output.append(("\n%s:\n" % title, None))
460 opt_output.append(("\n%s:\n" % title, None))
461 for shortopt, longopt, default, desc in options:
461 for shortopt, longopt, default, desc in options:
462 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
462 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
463 longopt and " --%s" % longopt),
463 longopt and " --%s" % longopt),
464 "%s%s" % (desc,
464 "%s%s" % (desc,
465 default and _(" (default: %s)") % default
465 default and _(" (default: %s)") % default
466 or "")))
466 or "")))
467
467
468 if opt_output:
468 if opt_output:
469 opts_len = max([len(line[0]) for line in opt_output if line[1]])
469 opts_len = max([len(line[0]) for line in opt_output if line[1]])
470 for first, second in opt_output:
470 for first, second in opt_output:
471 if second:
471 if second:
472 ui.write(" %-*s %s\n" % (opts_len, first, second))
472 ui.write(" %-*s %s\n" % (opts_len, first, second))
473 else:
473 else:
474 ui.write("%s\n" % first)
474 ui.write("%s\n" % first)
475
475
476 # Commands start here, listed alphabetically
476 # Commands start here, listed alphabetically
477
477
478 def add(ui, repo, *pats, **opts):
478 def add(ui, repo, *pats, **opts):
479 '''add the specified files on the next commit'''
479 '''add the specified files on the next commit'''
480 names = []
480 names = []
481 for src, abs, rel, exact in walk(repo, pats, opts):
481 for src, abs, rel, exact in walk(repo, pats, opts):
482 if exact:
482 if exact:
483 if ui.verbose: ui.status(_('adding %s\n') % rel)
483 if ui.verbose: ui.status(_('adding %s\n') % rel)
484 names.append(abs)
484 names.append(abs)
485 elif repo.dirstate.state(abs) == '?':
485 elif repo.dirstate.state(abs) == '?':
486 ui.status(_('adding %s\n') % rel)
486 ui.status(_('adding %s\n') % rel)
487 names.append(abs)
487 names.append(abs)
488 repo.add(names)
488 repo.add(names)
489
489
490 def addremove(ui, repo, *pats, **opts):
490 def addremove(ui, repo, *pats, **opts):
491 """add all new files, delete all missing files"""
491 """add all new files, delete all missing files"""
492 add, remove = [], []
492 add, remove = [], []
493 for src, abs, rel, exact in walk(repo, pats, opts):
493 for src, abs, rel, exact in walk(repo, pats, opts):
494 if src == 'f' and repo.dirstate.state(abs) == '?':
494 if src == 'f' and repo.dirstate.state(abs) == '?':
495 add.append(abs)
495 add.append(abs)
496 if ui.verbose or not exact:
496 if ui.verbose or not exact:
497 ui.status(_('adding %s\n') % rel)
497 ui.status(_('adding %s\n') % rel)
498 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
498 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
499 remove.append(abs)
499 remove.append(abs)
500 if ui.verbose or not exact:
500 if ui.verbose or not exact:
501 ui.status(_('removing %s\n') % rel)
501 ui.status(_('removing %s\n') % rel)
502 repo.add(add)
502 repo.add(add)
503 repo.remove(remove)
503 repo.remove(remove)
504
504
505 def annotate(ui, repo, *pats, **opts):
505 def annotate(ui, repo, *pats, **opts):
506 """show changeset information per file line"""
506 """show changeset information per file line"""
507 def getnode(rev):
507 def getnode(rev):
508 return short(repo.changelog.node(rev))
508 return short(repo.changelog.node(rev))
509
509
510 ucache = {}
510 ucache = {}
511 def getname(rev):
511 def getname(rev):
512 cl = repo.changelog.read(repo.changelog.node(rev))
512 cl = repo.changelog.read(repo.changelog.node(rev))
513 return trimuser(ui, cl[1], rev, ucache)
513 return trimuser(ui, cl[1], rev, ucache)
514
514
515 if not pats:
515 if not pats:
516 raise util.Abort(_('at least one file name or pattern required'))
516 raise util.Abort(_('at least one file name or pattern required'))
517
517
518 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
518 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
519 if not opts['user'] and not opts['changeset']:
519 if not opts['user'] and not opts['changeset']:
520 opts['number'] = 1
520 opts['number'] = 1
521
521
522 if opts['rev']:
522 if opts['rev']:
523 node = repo.changelog.lookup(opts['rev'])
523 node = repo.changelog.lookup(opts['rev'])
524 else:
524 else:
525 node = repo.dirstate.parents()[0]
525 node = repo.dirstate.parents()[0]
526 change = repo.changelog.read(node)
526 change = repo.changelog.read(node)
527 mmap = repo.manifest.read(change[0])
527 mmap = repo.manifest.read(change[0])
528
528
529 for src, abs, rel, exact in walk(repo, pats, opts):
529 for src, abs, rel, exact in walk(repo, pats, opts):
530 if abs not in mmap:
530 if abs not in mmap:
531 ui.warn(_("warning: %s is not in the repository!\n") % rel)
531 ui.warn(_("warning: %s is not in the repository!\n") % rel)
532 continue
532 continue
533
533
534 f = repo.file(abs)
534 f = repo.file(abs)
535 if not opts['text'] and util.binary(f.read(mmap[abs])):
535 if not opts['text'] and util.binary(f.read(mmap[abs])):
536 ui.write(_("%s: binary file\n") % rel)
536 ui.write(_("%s: binary file\n") % rel)
537 continue
537 continue
538
538
539 lines = f.annotate(mmap[abs])
539 lines = f.annotate(mmap[abs])
540 pieces = []
540 pieces = []
541
541
542 for o, f in opmap:
542 for o, f in opmap:
543 if opts[o]:
543 if opts[o]:
544 l = [f(n) for n, dummy in lines]
544 l = [f(n) for n, dummy in lines]
545 if l:
545 if l:
546 m = max(map(len, l))
546 m = max(map(len, l))
547 pieces.append(["%*s" % (m, x) for x in l])
547 pieces.append(["%*s" % (m, x) for x in l])
548
548
549 if pieces:
549 if pieces:
550 for p, l in zip(zip(*pieces), lines):
550 for p, l in zip(zip(*pieces), lines):
551 ui.write("%s: %s" % (" ".join(p), l[1]))
551 ui.write("%s: %s" % (" ".join(p), l[1]))
552
552
553 def bundle(ui, repo, fname, dest="default-push", **opts):
553 def bundle(ui, repo, fname, dest="default-push", **opts):
554 """create a changegroup file"""
554 """create a changegroup file"""
555 f = open(fname, "wb")
555 f = open(fname, "wb")
556 dest = ui.expandpath(dest)
556 dest = ui.expandpath(dest)
557 other = hg.repository(ui, dest)
557 other = hg.repository(ui, dest)
558 o = repo.findoutgoing(other)
558 o = repo.findoutgoing(other)
559 cg = repo.changegroup(o)
559 cg = repo.changegroup(o)
560
560
561 try:
561 try:
562 f.write("HG10")
562 f.write("HG10")
563 z = bz2.BZ2Compressor(9)
563 z = bz2.BZ2Compressor(9)
564 while 1:
564 while 1:
565 chunk = cg.read(4096)
565 chunk = cg.read(4096)
566 if not chunk:
566 if not chunk:
567 break
567 break
568 f.write(z.compress(chunk))
568 f.write(z.compress(chunk))
569 f.write(z.flush())
569 f.write(z.flush())
570 except:
570 except:
571 os.unlink(fname)
571 os.unlink(fname)
572 raise
572 raise
573
573
574 def cat(ui, repo, file1, *pats, **opts):
574 def cat(ui, repo, file1, *pats, **opts):
575 """output the latest or given revisions of files"""
575 """output the latest or given revisions of files"""
576 mf = {}
576 mf = {}
577 if opts['rev']:
577 if opts['rev']:
578 change = repo.changelog.read(repo.lookup(opts['rev']))
578 change = repo.changelog.read(repo.lookup(opts['rev']))
579 mf = repo.manifest.read(change[0])
579 mf = repo.manifest.read(change[0])
580 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
580 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
581 r = repo.file(abs)
581 r = repo.file(abs)
582 if opts['rev']:
582 if opts['rev']:
583 try:
583 try:
584 n = mf[abs]
584 n = mf[abs]
585 except (hg.RepoError, KeyError):
585 except (hg.RepoError, KeyError):
586 try:
586 try:
587 n = r.lookup(rev)
587 n = r.lookup(rev)
588 except KeyError, inst:
588 except KeyError, inst:
589 raise util.Abort(_('cannot find file %s in rev %s'), rel, rev)
589 raise util.Abort(_('cannot find file %s in rev %s'), rel, rev)
590 else:
590 else:
591 n = r.tip()
591 n = r.tip()
592 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
592 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
593 fp.write(r.read(n))
593 fp.write(r.read(n))
594
594
595 def clone(ui, source, dest=None, **opts):
595 def clone(ui, source, dest=None, **opts):
596 """make a copy of an existing repository"""
596 """make a copy of an existing repository"""
597 if dest is None:
597 if dest is None:
598 dest = os.path.basename(os.path.normpath(source))
598 dest = os.path.basename(os.path.normpath(source))
599
599
600 if os.path.exists(dest):
600 if os.path.exists(dest):
601 raise util.Abort(_("destination '%s' already exists"), dest)
601 raise util.Abort(_("destination '%s' already exists"), dest)
602
602
603 dest = os.path.realpath(dest)
603 dest = os.path.realpath(dest)
604
604
605 class Dircleanup:
605 class Dircleanup:
606 def __init__(self, dir_):
606 def __init__(self, dir_):
607 self.rmtree = shutil.rmtree
607 self.rmtree = shutil.rmtree
608 self.dir_ = dir_
608 self.dir_ = dir_
609 os.mkdir(dir_)
609 os.mkdir(dir_)
610 def close(self):
610 def close(self):
611 self.dir_ = None
611 self.dir_ = None
612 def __del__(self):
612 def __del__(self):
613 if self.dir_:
613 if self.dir_:
614 self.rmtree(self.dir_, True)
614 self.rmtree(self.dir_, True)
615
615
616 if opts['ssh']:
616 if opts['ssh']:
617 ui.setconfig("ui", "ssh", opts['ssh'])
617 ui.setconfig("ui", "ssh", opts['ssh'])
618 if opts['remotecmd']:
618 if opts['remotecmd']:
619 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
619 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
620
620
621 if not os.path.exists(source):
621 if not os.path.exists(source):
622 source = ui.expandpath(source)
622 source = ui.expandpath(source)
623
623
624 d = Dircleanup(dest)
624 d = Dircleanup(dest)
625 abspath = source
625 abspath = source
626 other = hg.repository(ui, source)
626 other = hg.repository(ui, source)
627
627
628 copy = False
628 copy = False
629 if other.dev() != -1:
629 if other.dev() != -1:
630 abspath = os.path.abspath(source)
630 abspath = os.path.abspath(source)
631 if not opts['pull']:
631 if not opts['pull']:
632 copy = True
632 copy = True
633
633
634 if copy:
634 if copy:
635 try:
635 try:
636 # we use a lock here because if we race with commit, we
636 # we use a lock here because if we race with commit, we
637 # can end up with extra data in the cloned revlogs that's
637 # can end up with extra data in the cloned revlogs that's
638 # not pointed to by changesets, thus causing verify to
638 # not pointed to by changesets, thus causing verify to
639 # fail
639 # fail
640 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
640 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
641 except OSError:
641 except OSError:
642 copy = False
642 copy = False
643
643
644 if copy:
644 if copy:
645 # we lock here to avoid premature writing to the target
645 # we lock here to avoid premature writing to the target
646 os.mkdir(os.path.join(dest, ".hg"))
646 os.mkdir(os.path.join(dest, ".hg"))
647 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
647 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
648
648
649 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
649 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
650 for f in files.split():
650 for f in files.split():
651 src = os.path.join(source, ".hg", f)
651 src = os.path.join(source, ".hg", f)
652 dst = os.path.join(dest, ".hg", f)
652 dst = os.path.join(dest, ".hg", f)
653 util.copyfiles(src, dst)
653 util.copyfiles(src, dst)
654
654
655 repo = hg.repository(ui, dest)
655 repo = hg.repository(ui, dest)
656
656
657 else:
657 else:
658 repo = hg.repository(ui, dest, create=1)
658 repo = hg.repository(ui, dest, create=1)
659 repo.pull(other)
659 repo.pull(other)
660
660
661 f = repo.opener("hgrc", "w", text=True)
661 f = repo.opener("hgrc", "w", text=True)
662 f.write("[paths]\n")
662 f.write("[paths]\n")
663 f.write("default = %s\n" % abspath)
663 f.write("default = %s\n" % abspath)
664
664
665 if not opts['noupdate']:
665 if not opts['noupdate']:
666 update(ui, repo)
666 update(ui, repo)
667
667
668 d.close()
668 d.close()
669
669
670 def commit(ui, repo, *pats, **opts):
670 def commit(ui, repo, *pats, **opts):
671 """commit the specified files or all outstanding changes"""
671 """commit the specified files or all outstanding changes"""
672 if opts['text']:
672 if opts['text']:
673 ui.warn(_("Warning: -t and --text is deprecated,"
673 ui.warn(_("Warning: -t and --text is deprecated,"
674 " please use -m or --message instead.\n"))
674 " please use -m or --message instead.\n"))
675 message = opts['message'] or opts['text']
675 message = opts['message'] or opts['text']
676 logfile = opts['logfile']
676 logfile = opts['logfile']
677
677
678 if message and logfile:
678 if message and logfile:
679 raise util.Abort(_('options --message and --logfile are mutually '
679 raise util.Abort(_('options --message and --logfile are mutually '
680 'exclusive'))
680 'exclusive'))
681 if not message and logfile:
681 if not message and logfile:
682 try:
682 try:
683 if logfile == '-':
683 if logfile == '-':
684 message = sys.stdin.read()
684 message = sys.stdin.read()
685 else:
685 else:
686 message = open(logfile).read()
686 message = open(logfile).read()
687 except IOError, inst:
687 except IOError, inst:
688 raise util.Abort(_("can't read commit message '%s': %s") %
688 raise util.Abort(_("can't read commit message '%s': %s") %
689 (logfile, inst.strerror))
689 (logfile, inst.strerror))
690
690
691 if opts['addremove']:
691 if opts['addremove']:
692 addremove(ui, repo, *pats, **opts)
692 addremove(ui, repo, *pats, **opts)
693 cwd = repo.getcwd()
693 cwd = repo.getcwd()
694 if not pats and cwd:
694 if not pats and cwd:
695 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
695 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
696 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
696 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
697 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
697 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
698 pats, opts)
698 pats, opts)
699 if pats:
699 if pats:
700 c, a, d, u = repo.changes(files=fns, match=match)
700 c, a, d, u = repo.changes(files=fns, match=match)
701 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
701 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
702 else:
702 else:
703 files = []
703 files = []
704 try:
704 try:
705 repo.commit(files, message, opts['user'], opts['date'], match)
705 repo.commit(files, message, opts['user'], opts['date'], match)
706 except ValueError, inst:
706 except ValueError, inst:
707 raise util.Abort(str(inst))
707 raise util.Abort(str(inst))
708
708
709 def docopy(ui, repo, pats, opts):
709 def docopy(ui, repo, pats, opts):
710 if not pats:
710 if not pats:
711 raise util.Abort(_('no source or destination specified'))
711 raise util.Abort(_('no source or destination specified'))
712 elif len(pats) == 1:
712 elif len(pats) == 1:
713 raise util.Abort(_('no destination specified'))
713 raise util.Abort(_('no destination specified'))
714 pats = list(pats)
714 pats = list(pats)
715 dest = pats.pop()
715 dest = pats.pop()
716 sources = []
716 sources = []
717
717
718 def okaytocopy(abs, rel, exact):
718 def okaytocopy(abs, rel, exact):
719 reasons = {'?': _('is not managed'),
719 reasons = {'?': _('is not managed'),
720 'a': _('has been marked for add')}
720 'a': _('has been marked for add')}
721 reason = reasons.get(repo.dirstate.state(abs))
721 reason = reasons.get(repo.dirstate.state(abs))
722 if reason:
722 if reason:
723 if exact: ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
723 if exact: ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
724 else:
724 else:
725 return True
725 return True
726
726
727 for src, abs, rel, exact in walk(repo, pats, opts):
727 for src, abs, rel, exact in walk(repo, pats, opts):
728 if okaytocopy(abs, rel, exact):
728 if okaytocopy(abs, rel, exact):
729 sources.append((abs, rel, exact))
729 sources.append((abs, rel, exact))
730 if not sources:
730 if not sources:
731 raise util.Abort(_('no files to copy'))
731 raise util.Abort(_('no files to copy'))
732
732
733 cwd = repo.getcwd()
733 cwd = repo.getcwd()
734 absdest = util.canonpath(repo.root, cwd, dest)
734 absdest = util.canonpath(repo.root, cwd, dest)
735 reldest = util.pathto(cwd, absdest)
735 reldest = util.pathto(cwd, absdest)
736 if os.path.exists(reldest):
736 if os.path.exists(reldest):
737 destisfile = not os.path.isdir(reldest)
737 destisfile = not os.path.isdir(reldest)
738 else:
738 else:
739 destisfile = len(sources) == 1 or repo.dirstate.state(absdest) != '?'
739 destisfile = len(sources) == 1 or repo.dirstate.state(absdest) != '?'
740
740
741 if destisfile:
741 if destisfile:
742 if opts['parents']:
742 if opts['parents']:
743 raise util.Abort(_('with --parents, destination must be a directory'))
743 raise util.Abort(_('with --parents, destination must be a directory'))
744 elif len(sources) > 1:
744 elif len(sources) > 1:
745 raise util.Abort(_('with multiple sources, destination must be a '
745 raise util.Abort(_('with multiple sources, destination must be a '
746 'directory'))
746 'directory'))
747 errs, copied = 0, []
747 errs, copied = 0, []
748 for abs, rel, exact in sources:
748 for abs, rel, exact in sources:
749 if opts['parents']:
749 if opts['parents']:
750 mydest = os.path.join(dest, rel)
750 mydest = os.path.join(dest, rel)
751 elif destisfile:
751 elif destisfile:
752 mydest = reldest
752 mydest = reldest
753 else:
753 else:
754 mydest = os.path.join(dest, os.path.basename(rel))
754 mydest = os.path.join(dest, os.path.basename(rel))
755 myabsdest = util.canonpath(repo.root, cwd, mydest)
755 myabsdest = util.canonpath(repo.root, cwd, mydest)
756 myreldest = util.pathto(cwd, myabsdest)
756 myreldest = util.pathto(cwd, myabsdest)
757 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
757 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
758 ui.warn(_('%s: not overwriting - file already managed\n') % myreldest)
758 ui.warn(_('%s: not overwriting - file already managed\n') % myreldest)
759 continue
759 continue
760 mydestdir = os.path.dirname(myreldest) or '.'
760 mydestdir = os.path.dirname(myreldest) or '.'
761 if not opts['after']:
761 if not opts['after']:
762 try:
762 try:
763 if opts['parents']: os.makedirs(mydestdir)
763 if opts['parents']: os.makedirs(mydestdir)
764 elif not destisfile: os.mkdir(mydestdir)
764 elif not destisfile: os.mkdir(mydestdir)
765 except OSError, inst:
765 except OSError, inst:
766 if inst.errno != errno.EEXIST: raise
766 if inst.errno != errno.EEXIST: raise
767 if ui.verbose or not exact:
767 if ui.verbose or not exact:
768 ui.status(_('copying %s to %s\n') % (rel, myreldest))
768 ui.status(_('copying %s to %s\n') % (rel, myreldest))
769 if not opts['after']:
769 if not opts['after']:
770 try:
770 try:
771 shutil.copyfile(rel, myreldest)
771 shutil.copyfile(rel, myreldest)
772 shutil.copymode(rel, myreldest)
772 shutil.copymode(rel, myreldest)
773 except shutil.Error, inst:
773 except shutil.Error, inst:
774 raise util.Abort(str(inst))
774 raise util.Abort(str(inst))
775 except IOError, inst:
775 except IOError, inst:
776 if inst.errno == errno.ENOENT:
776 if inst.errno == errno.ENOENT:
777 ui.warn(_('%s: deleted in working copy\n') % rel)
777 ui.warn(_('%s: deleted in working copy\n') % rel)
778 else:
778 else:
779 ui.warn(_('%s: cannot copy - %s\n') % (rel, inst.strerror))
779 ui.warn(_('%s: cannot copy - %s\n') % (rel, inst.strerror))
780 errs += 1
780 errs += 1
781 continue
781 continue
782 repo.copy(abs, myabsdest)
782 repo.copy(abs, myabsdest)
783 copied.append((abs, rel, exact))
783 copied.append((abs, rel, exact))
784 if errs:
784 if errs:
785 ui.warn(_('(consider using --after)\n'))
785 ui.warn(_('(consider using --after)\n'))
786 return errs, copied
786 return errs, copied
787
787
788 def copy(ui, repo, *pats, **opts):
788 def copy(ui, repo, *pats, **opts):
789 """mark files as copied for the next commit"""
789 """mark files as copied for the next commit"""
790 errs, copied = docopy(ui, repo, pats, opts)
790 errs, copied = docopy(ui, repo, pats, opts)
791 return errs
791 return errs
792
792
793 def debugancestor(ui, index, rev1, rev2):
793 def debugancestor(ui, index, rev1, rev2):
794 """find the ancestor revision of two revisions in a given index"""
794 """find the ancestor revision of two revisions in a given index"""
795 r = revlog.revlog(file, index, "")
795 r = revlog.revlog(file, index, "")
796 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
796 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
797 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
797 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
798
798
799 def debugcheckstate(ui, repo):
799 def debugcheckstate(ui, repo):
800 """validate the correctness of the current dirstate"""
800 """validate the correctness of the current dirstate"""
801 parent1, parent2 = repo.dirstate.parents()
801 parent1, parent2 = repo.dirstate.parents()
802 repo.dirstate.read()
802 repo.dirstate.read()
803 dc = repo.dirstate.map
803 dc = repo.dirstate.map
804 keys = dc.keys()
804 keys = dc.keys()
805 keys.sort()
805 keys.sort()
806 m1n = repo.changelog.read(parent1)[0]
806 m1n = repo.changelog.read(parent1)[0]
807 m2n = repo.changelog.read(parent2)[0]
807 m2n = repo.changelog.read(parent2)[0]
808 m1 = repo.manifest.read(m1n)
808 m1 = repo.manifest.read(m1n)
809 m2 = repo.manifest.read(m2n)
809 m2 = repo.manifest.read(m2n)
810 errors = 0
810 errors = 0
811 for f in dc:
811 for f in dc:
812 state = repo.dirstate.state(f)
812 state = repo.dirstate.state(f)
813 if state in "nr" and f not in m1:
813 if state in "nr" and f not in m1:
814 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
814 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
815 errors += 1
815 errors += 1
816 if state in "a" and f in m1:
816 if state in "a" and f in m1:
817 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
817 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
818 errors += 1
818 errors += 1
819 if state in "m" and f not in m1 and f not in m2:
819 if state in "m" and f not in m1 and f not in m2:
820 ui.warn(_("%s in state %s, but not in either manifest\n") %
820 ui.warn(_("%s in state %s, but not in either manifest\n") %
821 (f, state))
821 (f, state))
822 errors += 1
822 errors += 1
823 for f in m1:
823 for f in m1:
824 state = repo.dirstate.state(f)
824 state = repo.dirstate.state(f)
825 if state not in "nrm":
825 if state not in "nrm":
826 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
826 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
827 errors += 1
827 errors += 1
828 if errors:
828 if errors:
829 raise util.Abort(_(".hg/dirstate inconsistent with current parent's manifest"))
829 raise util.Abort(_(".hg/dirstate inconsistent with current parent's manifest"))
830
830
831 def debugconfig(ui):
831 def debugconfig(ui):
832 """show combined config settings from all hgrc files"""
832 """show combined config settings from all hgrc files"""
833 try:
833 try:
834 repo = hg.repository(ui)
834 repo = hg.repository(ui)
835 except hg.RepoError:
835 except hg.RepoError:
836 pass
836 pass
837 for section, name, value in ui.walkconfig():
837 for section, name, value in ui.walkconfig():
838 ui.write('%s.%s=%s\n' % (section, name, value))
838 ui.write('%s.%s=%s\n' % (section, name, value))
839
839
840 def debugsetparents(ui, repo, rev1, rev2=None):
840 def debugsetparents(ui, repo, rev1, rev2=None):
841 """
841 """
842 manually set the parents of the current working directory
842 manually set the parents of the current working directory
843
843
844 This is useful for writing repository conversion tools, but should
844 This is useful for writing repository conversion tools, but should
845 be used with care.
845 be used with care.
846 """
846 """
847
847
848 if not rev2:
848 if not rev2:
849 rev2 = hex(nullid)
849 rev2 = hex(nullid)
850
850
851 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
851 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
852
852
853 def debugstate(ui, repo):
853 def debugstate(ui, repo):
854 """show the contents of the current dirstate"""
854 """show the contents of the current dirstate"""
855 repo.dirstate.read()
855 repo.dirstate.read()
856 dc = repo.dirstate.map
856 dc = repo.dirstate.map
857 keys = dc.keys()
857 keys = dc.keys()
858 keys.sort()
858 keys.sort()
859 for file_ in keys:
859 for file_ in keys:
860 ui.write("%c %3o %10d %s %s\n"
860 ui.write("%c %3o %10d %s %s\n"
861 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
861 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
862 time.strftime("%x %X",
862 time.strftime("%x %X",
863 time.localtime(dc[file_][3])), file_))
863 time.localtime(dc[file_][3])), file_))
864 for f in repo.dirstate.copies:
864 for f in repo.dirstate.copies:
865 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
865 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
866
866
867 def debugdata(ui, file_, rev):
867 def debugdata(ui, file_, rev):
868 """dump the contents of an data file revision"""
868 """dump the contents of an data file revision"""
869 r = revlog.revlog(file, file_[:-2] + ".i", file_)
869 r = revlog.revlog(file, file_[:-2] + ".i", file_)
870 try:
870 try:
871 ui.write(r.revision(r.lookup(rev)))
871 ui.write(r.revision(r.lookup(rev)))
872 except KeyError:
872 except KeyError:
873 raise util.Abort(_('invalid revision identifier %s'), rev)
873 raise util.Abort(_('invalid revision identifier %s'), rev)
874
874
875 def debugindex(ui, file_):
875 def debugindex(ui, file_):
876 """dump the contents of an index file"""
876 """dump the contents of an index file"""
877 r = revlog.revlog(file, file_, "")
877 r = revlog.revlog(file, file_, "")
878 ui.write(" rev offset length base linkrev" +
878 ui.write(" rev offset length base linkrev" +
879 " nodeid p1 p2\n")
879 " nodeid p1 p2\n")
880 for i in range(r.count()):
880 for i in range(r.count()):
881 e = r.index[i]
881 e = r.index[i]
882 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
882 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
883 i, e[0], e[1], e[2], e[3],
883 i, e[0], e[1], e[2], e[3],
884 short(e[6]), short(e[4]), short(e[5])))
884 short(e[6]), short(e[4]), short(e[5])))
885
885
886 def debugindexdot(ui, file_):
886 def debugindexdot(ui, file_):
887 """dump an index DAG as a .dot file"""
887 """dump an index DAG as a .dot file"""
888 r = revlog.revlog(file, file_, "")
888 r = revlog.revlog(file, file_, "")
889 ui.write("digraph G {\n")
889 ui.write("digraph G {\n")
890 for i in range(r.count()):
890 for i in range(r.count()):
891 e = r.index[i]
891 e = r.index[i]
892 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
892 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
893 if e[5] != nullid:
893 if e[5] != nullid:
894 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
894 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
895 ui.write("}\n")
895 ui.write("}\n")
896
896
897 def debugrename(ui, repo, file, rev=None):
897 def debugrename(ui, repo, file, rev=None):
898 """dump rename information"""
898 """dump rename information"""
899 r = repo.file(relpath(repo, [file])[0])
899 r = repo.file(relpath(repo, [file])[0])
900 if rev:
900 if rev:
901 try:
901 try:
902 # assume all revision numbers are for changesets
902 # assume all revision numbers are for changesets
903 n = repo.lookup(rev)
903 n = repo.lookup(rev)
904 change = repo.changelog.read(n)
904 change = repo.changelog.read(n)
905 m = repo.manifest.read(change[0])
905 m = repo.manifest.read(change[0])
906 n = m[relpath(repo, [file])[0]]
906 n = m[relpath(repo, [file])[0]]
907 except hg.RepoError, KeyError:
907 except hg.RepoError, KeyError:
908 n = r.lookup(rev)
908 n = r.lookup(rev)
909 else:
909 else:
910 n = r.tip()
910 n = r.tip()
911 m = r.renamed(n)
911 m = r.renamed(n)
912 if m:
912 if m:
913 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
913 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
914 else:
914 else:
915 ui.write(_("not renamed\n"))
915 ui.write(_("not renamed\n"))
916
916
917 def debugwalk(ui, repo, *pats, **opts):
917 def debugwalk(ui, repo, *pats, **opts):
918 """show how files match on given patterns"""
918 """show how files match on given patterns"""
919 items = list(walk(repo, pats, opts))
919 items = list(walk(repo, pats, opts))
920 if not items:
920 if not items:
921 return
921 return
922 fmt = '%%s %%-%ds %%-%ds %%s' % (
922 fmt = '%%s %%-%ds %%-%ds %%s' % (
923 max([len(abs) for (src, abs, rel, exact) in items]),
923 max([len(abs) for (src, abs, rel, exact) in items]),
924 max([len(rel) for (src, abs, rel, exact) in items]))
924 max([len(rel) for (src, abs, rel, exact) in items]))
925 for src, abs, rel, exact in items:
925 for src, abs, rel, exact in items:
926 line = fmt % (src, abs, rel, exact and 'exact' or '')
926 line = fmt % (src, abs, rel, exact and 'exact' or '')
927 ui.write("%s\n" % line.rstrip())
927 ui.write("%s\n" % line.rstrip())
928
928
929 def diff(ui, repo, *pats, **opts):
929 def diff(ui, repo, *pats, **opts):
930 """diff working directory (or selected files)"""
930 """diff working directory (or selected files)"""
931 node1, node2 = None, None
931 node1, node2 = None, None
932 revs = [repo.lookup(x) for x in opts['rev']]
932 revs = [repo.lookup(x) for x in opts['rev']]
933
933
934 if len(revs) > 0:
934 if len(revs) > 0:
935 node1 = revs[0]
935 node1 = revs[0]
936 if len(revs) > 1:
936 if len(revs) > 1:
937 node2 = revs[1]
937 node2 = revs[1]
938 if len(revs) > 2:
938 if len(revs) > 2:
939 raise util.Abort(_("too many revisions to diff"))
939 raise util.Abort(_("too many revisions to diff"))
940
940
941 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
941 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
942
942
943 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
943 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
944 text=opts['text'])
944 text=opts['text'])
945
945
946 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
946 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
947 node = repo.lookup(changeset)
947 node = repo.lookup(changeset)
948 prev, other = repo.changelog.parents(node)
948 prev, other = repo.changelog.parents(node)
949 change = repo.changelog.read(node)
949 change = repo.changelog.read(node)
950
950
951 fp = make_file(repo, repo.changelog, opts['output'],
951 fp = make_file(repo, repo.changelog, opts['output'],
952 node=node, total=total, seqno=seqno,
952 node=node, total=total, seqno=seqno,
953 revwidth=revwidth)
953 revwidth=revwidth)
954 if fp != sys.stdout:
954 if fp != sys.stdout:
955 ui.note("%s\n" % fp.name)
955 ui.note("%s\n" % fp.name)
956
956
957 fp.write("# HG changeset patch\n")
957 fp.write("# HG changeset patch\n")
958 fp.write("# User %s\n" % change[1])
958 fp.write("# User %s\n" % change[1])
959 fp.write("# Node ID %s\n" % hex(node))
959 fp.write("# Node ID %s\n" % hex(node))
960 fp.write("# Parent %s\n" % hex(prev))
960 fp.write("# Parent %s\n" % hex(prev))
961 if other != nullid:
961 if other != nullid:
962 fp.write("# Parent %s\n" % hex(other))
962 fp.write("# Parent %s\n" % hex(other))
963 fp.write(change[4].rstrip())
963 fp.write(change[4].rstrip())
964 fp.write("\n\n")
964 fp.write("\n\n")
965
965
966 dodiff(fp, ui, repo, prev, node, text=opts['text'])
966 dodiff(fp, ui, repo, prev, node, text=opts['text'])
967 if fp != sys.stdout:
967 if fp != sys.stdout:
968 fp.close()
968 fp.close()
969
969
970 def export(ui, repo, *changesets, **opts):
970 def export(ui, repo, *changesets, **opts):
971 """dump the header and diffs for one or more changesets"""
971 """dump the header and diffs for one or more changesets"""
972 if not changesets:
972 if not changesets:
973 raise util.Abort(_("export requires at least one changeset"))
973 raise util.Abort(_("export requires at least one changeset"))
974 seqno = 0
974 seqno = 0
975 revs = list(revrange(ui, repo, changesets))
975 revs = list(revrange(ui, repo, changesets))
976 total = len(revs)
976 total = len(revs)
977 revwidth = max(map(len, revs))
977 revwidth = max(map(len, revs))
978 ui.note(len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n"))
978 ui.note(len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n"))
979 for cset in revs:
979 for cset in revs:
980 seqno += 1
980 seqno += 1
981 doexport(ui, repo, cset, seqno, total, revwidth, opts)
981 doexport(ui, repo, cset, seqno, total, revwidth, opts)
982
982
983 def forget(ui, repo, *pats, **opts):
983 def forget(ui, repo, *pats, **opts):
984 """don't add the specified files on the next commit"""
984 """don't add the specified files on the next commit"""
985 forget = []
985 forget = []
986 for src, abs, rel, exact in walk(repo, pats, opts):
986 for src, abs, rel, exact in walk(repo, pats, opts):
987 if repo.dirstate.state(abs) == 'a':
987 if repo.dirstate.state(abs) == 'a':
988 forget.append(abs)
988 forget.append(abs)
989 if ui.verbose or not exact:
989 if ui.verbose or not exact:
990 ui.status(_('forgetting %s\n') % rel)
990 ui.status(_('forgetting %s\n') % rel)
991 repo.forget(forget)
991 repo.forget(forget)
992
992
993 def grep(ui, repo, pattern, *pats, **opts):
993 def grep(ui, repo, pattern, *pats, **opts):
994 """search for a pattern in specified files and revisions"""
994 """search for a pattern in specified files and revisions"""
995 reflags = 0
995 reflags = 0
996 if opts['ignore_case']:
996 if opts['ignore_case']:
997 reflags |= re.I
997 reflags |= re.I
998 regexp = re.compile(pattern, reflags)
998 regexp = re.compile(pattern, reflags)
999 sep, eol = ':', '\n'
999 sep, eol = ':', '\n'
1000 if opts['print0']:
1000 if opts['print0']:
1001 sep = eol = '\0'
1001 sep = eol = '\0'
1002
1002
1003 fcache = {}
1003 fcache = {}
1004 def getfile(fn):
1004 def getfile(fn):
1005 if fn not in fcache:
1005 if fn not in fcache:
1006 fcache[fn] = repo.file(fn)
1006 fcache[fn] = repo.file(fn)
1007 return fcache[fn]
1007 return fcache[fn]
1008
1008
1009 def matchlines(body):
1009 def matchlines(body):
1010 begin = 0
1010 begin = 0
1011 linenum = 0
1011 linenum = 0
1012 while True:
1012 while True:
1013 match = regexp.search(body, begin)
1013 match = regexp.search(body, begin)
1014 if not match:
1014 if not match:
1015 break
1015 break
1016 mstart, mend = match.span()
1016 mstart, mend = match.span()
1017 linenum += body.count('\n', begin, mstart) + 1
1017 linenum += body.count('\n', begin, mstart) + 1
1018 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1018 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1019 lend = body.find('\n', mend)
1019 lend = body.find('\n', mend)
1020 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1020 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1021 begin = lend + 1
1021 begin = lend + 1
1022
1022
1023 class linestate:
1023 class linestate:
1024 def __init__(self, line, linenum, colstart, colend):
1024 def __init__(self, line, linenum, colstart, colend):
1025 self.line = line
1025 self.line = line
1026 self.linenum = linenum
1026 self.linenum = linenum
1027 self.colstart = colstart
1027 self.colstart = colstart
1028 self.colend = colend
1028 self.colend = colend
1029 def __eq__(self, other):
1029 def __eq__(self, other):
1030 return self.line == other.line
1030 return self.line == other.line
1031 def __hash__(self):
1031 def __hash__(self):
1032 return hash(self.line)
1032 return hash(self.line)
1033
1033
1034 matches = {}
1034 matches = {}
1035 def grepbody(fn, rev, body):
1035 def grepbody(fn, rev, body):
1036 matches[rev].setdefault(fn, {})
1036 matches[rev].setdefault(fn, {})
1037 m = matches[rev][fn]
1037 m = matches[rev][fn]
1038 for lnum, cstart, cend, line in matchlines(body):
1038 for lnum, cstart, cend, line in matchlines(body):
1039 s = linestate(line, lnum, cstart, cend)
1039 s = linestate(line, lnum, cstart, cend)
1040 m[s] = s
1040 m[s] = s
1041
1041
1042 prev = {}
1042 prev = {}
1043 ucache = {}
1043 ucache = {}
1044 def display(fn, rev, states, prevstates):
1044 def display(fn, rev, states, prevstates):
1045 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1045 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1046 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1046 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1047 counts = {'-': 0, '+': 0}
1047 counts = {'-': 0, '+': 0}
1048 filerevmatches = {}
1048 filerevmatches = {}
1049 for l in diff:
1049 for l in diff:
1050 if incrementing or not opts['all']:
1050 if incrementing or not opts['all']:
1051 change = ((l in prevstates) and '-') or '+'
1051 change = ((l in prevstates) and '-') or '+'
1052 r = rev
1052 r = rev
1053 else:
1053 else:
1054 change = ((l in states) and '-') or '+'
1054 change = ((l in states) and '-') or '+'
1055 r = prev[fn]
1055 r = prev[fn]
1056 cols = [fn, str(rev)]
1056 cols = [fn, str(rev)]
1057 if opts['line_number']: cols.append(str(l.linenum))
1057 if opts['line_number']: cols.append(str(l.linenum))
1058 if opts['all']: cols.append(change)
1058 if opts['all']: cols.append(change)
1059 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1059 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1060 ucache))
1060 ucache))
1061 if opts['files_with_matches']:
1061 if opts['files_with_matches']:
1062 c = (fn, rev)
1062 c = (fn, rev)
1063 if c in filerevmatches: continue
1063 if c in filerevmatches: continue
1064 filerevmatches[c] = 1
1064 filerevmatches[c] = 1
1065 else:
1065 else:
1066 cols.append(l.line)
1066 cols.append(l.line)
1067 ui.write(sep.join(cols), eol)
1067 ui.write(sep.join(cols), eol)
1068 counts[change] += 1
1068 counts[change] += 1
1069 return counts['+'], counts['-']
1069 return counts['+'], counts['-']
1070
1070
1071 fstate = {}
1071 fstate = {}
1072 skip = {}
1072 skip = {}
1073 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1073 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1074 count = 0
1074 count = 0
1075 incrementing = False
1075 incrementing = False
1076 for st, rev, fns in changeiter:
1076 for st, rev, fns in changeiter:
1077 if st == 'window':
1077 if st == 'window':
1078 incrementing = rev
1078 incrementing = rev
1079 matches.clear()
1079 matches.clear()
1080 elif st == 'add':
1080 elif st == 'add':
1081 change = repo.changelog.read(repo.lookup(str(rev)))
1081 change = repo.changelog.read(repo.lookup(str(rev)))
1082 mf = repo.manifest.read(change[0])
1082 mf = repo.manifest.read(change[0])
1083 matches[rev] = {}
1083 matches[rev] = {}
1084 for fn in fns:
1084 for fn in fns:
1085 if fn in skip: continue
1085 if fn in skip: continue
1086 fstate.setdefault(fn, {})
1086 fstate.setdefault(fn, {})
1087 try:
1087 try:
1088 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1088 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1089 except KeyError:
1089 except KeyError:
1090 pass
1090 pass
1091 elif st == 'iter':
1091 elif st == 'iter':
1092 states = matches[rev].items()
1092 states = matches[rev].items()
1093 states.sort()
1093 states.sort()
1094 for fn, m in states:
1094 for fn, m in states:
1095 if fn in skip: continue
1095 if fn in skip: continue
1096 if incrementing or not opts['all'] or fstate[fn]:
1096 if incrementing or not opts['all'] or fstate[fn]:
1097 pos, neg = display(fn, rev, m, fstate[fn])
1097 pos, neg = display(fn, rev, m, fstate[fn])
1098 count += pos + neg
1098 count += pos + neg
1099 if pos and not opts['all']:
1099 if pos and not opts['all']:
1100 skip[fn] = True
1100 skip[fn] = True
1101 fstate[fn] = m
1101 fstate[fn] = m
1102 prev[fn] = rev
1102 prev[fn] = rev
1103
1103
1104 if not incrementing:
1104 if not incrementing:
1105 fstate = fstate.items()
1105 fstate = fstate.items()
1106 fstate.sort()
1106 fstate.sort()
1107 for fn, state in fstate:
1107 for fn, state in fstate:
1108 if fn in skip: continue
1108 if fn in skip: continue
1109 display(fn, rev, {}, state)
1109 display(fn, rev, {}, state)
1110 return (count == 0 and 1) or 0
1110 return (count == 0 and 1) or 0
1111
1111
1112 def heads(ui, repo, **opts):
1112 def heads(ui, repo, **opts):
1113 """show current repository heads"""
1113 """show current repository heads"""
1114 heads = repo.changelog.heads()
1114 heads = repo.changelog.heads()
1115 br = None
1115 br = None
1116 if opts['branches']:
1116 if opts['branches']:
1117 br = repo.branchlookup(heads)
1117 br = repo.branchlookup(heads)
1118 for n in repo.changelog.heads():
1118 for n in repo.changelog.heads():
1119 show_changeset(ui, repo, changenode=n, brinfo=br)
1119 show_changeset(ui, repo, changenode=n, brinfo=br)
1120
1120
1121 def identify(ui, repo):
1121 def identify(ui, repo):
1122 """print information about the working copy"""
1122 """print information about the working copy"""
1123 parents = [p for p in repo.dirstate.parents() if p != nullid]
1123 parents = [p for p in repo.dirstate.parents() if p != nullid]
1124 if not parents:
1124 if not parents:
1125 ui.write(_("unknown\n"))
1125 ui.write(_("unknown\n"))
1126 return
1126 return
1127
1127
1128 hexfunc = ui.verbose and hex or short
1128 hexfunc = ui.verbose and hex or short
1129 (c, a, d, u) = repo.changes()
1129 (c, a, d, u) = repo.changes()
1130 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1130 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1131 (c or a or d) and "+" or "")]
1131 (c or a or d) and "+" or "")]
1132
1132
1133 if not ui.quiet:
1133 if not ui.quiet:
1134 # multiple tags for a single parent separated by '/'
1134 # multiple tags for a single parent separated by '/'
1135 parenttags = ['/'.join(tags)
1135 parenttags = ['/'.join(tags)
1136 for tags in map(repo.nodetags, parents) if tags]
1136 for tags in map(repo.nodetags, parents) if tags]
1137 # tags for multiple parents separated by ' + '
1137 # tags for multiple parents separated by ' + '
1138 if parenttags:
1138 if parenttags:
1139 output.append(' + '.join(parenttags))
1139 output.append(' + '.join(parenttags))
1140
1140
1141 ui.write("%s\n" % ' '.join(output))
1141 ui.write("%s\n" % ' '.join(output))
1142
1142
1143 def import_(ui, repo, patch1, *patches, **opts):
1143 def import_(ui, repo, patch1, *patches, **opts):
1144 """import an ordered set of patches"""
1144 """import an ordered set of patches"""
1145 patches = (patch1,) + patches
1145 patches = (patch1,) + patches
1146
1146
1147 if not opts['force']:
1147 if not opts['force']:
1148 (c, a, d, u) = repo.changes()
1148 (c, a, d, u) = repo.changes()
1149 if c or a or d:
1149 if c or a or d:
1150 raise util.Abort(_("outstanding uncommitted changes"))
1150 raise util.Abort(_("outstanding uncommitted changes"))
1151
1151
1152 d = opts["base"]
1152 d = opts["base"]
1153 strip = opts["strip"]
1153 strip = opts["strip"]
1154
1154
1155 mailre = re.compile(r'(?:From |[\w-]+:)')
1155 mailre = re.compile(r'(?:From |[\w-]+:)')
1156
1156
1157 # attempt to detect the start of a patch
1157 # attempt to detect the start of a patch
1158 # (this heuristic is borrowed from quilt)
1158 # (this heuristic is borrowed from quilt)
1159 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1159 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1160 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1160 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1161 '(---|\*\*\*)[ \t])')
1161 '(---|\*\*\*)[ \t])')
1162
1162
1163 for patch in patches:
1163 for patch in patches:
1164 ui.status(_("applying %s\n") % patch)
1164 ui.status(_("applying %s\n") % patch)
1165 pf = os.path.join(d, patch)
1165 pf = os.path.join(d, patch)
1166
1166
1167 message = []
1167 message = []
1168 user = None
1168 user = None
1169 hgpatch = False
1169 hgpatch = False
1170 for line in file(pf):
1170 for line in file(pf):
1171 line = line.rstrip()
1171 line = line.rstrip()
1172 if (not message and not hgpatch and
1172 if (not message and not hgpatch and
1173 mailre.match(line) and not opts['force']):
1173 mailre.match(line) and not opts['force']):
1174 if len(line) > 35: line = line[:32] + '...'
1174 if len(line) > 35: line = line[:32] + '...'
1175 raise util.Abort(_('first line looks like a '
1175 raise util.Abort(_('first line looks like a '
1176 'mail header: ') + line)
1176 'mail header: ') + line)
1177 if diffre.match(line):
1177 if diffre.match(line):
1178 break
1178 break
1179 elif hgpatch:
1179 elif hgpatch:
1180 # parse values when importing the result of an hg export
1180 # parse values when importing the result of an hg export
1181 if line.startswith("# User "):
1181 if line.startswith("# User "):
1182 user = line[7:]
1182 user = line[7:]
1183 ui.debug(_('User: %s\n') % user)
1183 ui.debug(_('User: %s\n') % user)
1184 elif not line.startswith("# ") and line:
1184 elif not line.startswith("# ") and line:
1185 message.append(line)
1185 message.append(line)
1186 hgpatch = False
1186 hgpatch = False
1187 elif line == '# HG changeset patch':
1187 elif line == '# HG changeset patch':
1188 hgpatch = True
1188 hgpatch = True
1189 message = [] # We may have collected garbage
1189 message = [] # We may have collected garbage
1190 else:
1190 else:
1191 message.append(line)
1191 message.append(line)
1192
1192
1193 # make sure message isn't empty
1193 # make sure message isn't empty
1194 if not message:
1194 if not message:
1195 message = _("imported patch %s\n") % patch
1195 message = _("imported patch %s\n") % patch
1196 else:
1196 else:
1197 message = "%s\n" % '\n'.join(message)
1197 message = "%s\n" % '\n'.join(message)
1198 ui.debug(_('message:\n%s\n') % message)
1198 ui.debug(_('message:\n%s\n') % message)
1199
1199
1200 files = util.patch(strip, pf, ui)
1200 files = util.patch(strip, pf, ui)
1201
1201
1202 if len(files) > 0:
1202 if len(files) > 0:
1203 addremove(ui, repo, *files)
1203 addremove(ui, repo, *files)
1204 repo.commit(files, message, user)
1204 repo.commit(files, message, user)
1205
1205
1206 def incoming(ui, repo, source="default", **opts):
1206 def incoming(ui, repo, source="default", **opts):
1207 """show new changesets found in source"""
1207 """show new changesets found in source"""
1208 source = ui.expandpath(source)
1208 source = ui.expandpath(source)
1209 other = hg.repository(ui, source)
1209 other = hg.repository(ui, source)
1210 if not other.local():
1210 if not other.local():
1211 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1211 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1212 o = repo.findincoming(other)
1212 o = repo.findincoming(other)
1213 if not o:
1213 if not o:
1214 return
1214 return
1215 o = other.newer(o)
1215 o = other.newer(o)
1216 for n in o:
1216 for n in o:
1217 show_changeset(ui, other, changenode=n)
1217 show_changeset(ui, other, changenode=n)
1218 if opts['patch']:
1218 if opts['patch']:
1219 prev = other.changelog.parents(n)[0]
1219 prev = other.changelog.parents(n)[0]
1220 dodiff(ui, ui, other, prev, n)
1220 dodiff(ui, ui, other, prev, n)
1221 ui.write("\n")
1221 ui.write("\n")
1222
1222
1223 def init(ui, dest="."):
1223 def init(ui, dest="."):
1224 """create a new repository in the given directory"""
1224 """create a new repository in the given directory"""
1225 if not os.path.exists(dest):
1225 if not os.path.exists(dest):
1226 os.mkdir(dest)
1226 os.mkdir(dest)
1227 hg.repository(ui, dest, create=1)
1227 hg.repository(ui, dest, create=1)
1228
1228
1229 def locate(ui, repo, *pats, **opts):
1229 def locate(ui, repo, *pats, **opts):
1230 """locate files matching specific patterns"""
1230 """locate files matching specific patterns"""
1231 end = opts['print0'] and '\0' or '\n'
1231 end = opts['print0'] and '\0' or '\n'
1232
1232
1233 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1233 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1234 if repo.dirstate.state(abs) == '?':
1234 if repo.dirstate.state(abs) == '?':
1235 continue
1235 continue
1236 if opts['fullpath']:
1236 if opts['fullpath']:
1237 ui.write(os.path.join(repo.root, abs), end)
1237 ui.write(os.path.join(repo.root, abs), end)
1238 else:
1238 else:
1239 ui.write(rel, end)
1239 ui.write(rel, end)
1240
1240
1241 def log(ui, repo, *pats, **opts):
1241 def log(ui, repo, *pats, **opts):
1242 """show revision history of entire repository or files"""
1242 """show revision history of entire repository or files"""
1243 class dui:
1243 class dui:
1244 # Implement and delegate some ui protocol. Save hunks of
1244 # Implement and delegate some ui protocol. Save hunks of
1245 # output for later display in the desired order.
1245 # output for later display in the desired order.
1246 def __init__(self, ui):
1246 def __init__(self, ui):
1247 self.ui = ui
1247 self.ui = ui
1248 self.hunk = {}
1248 self.hunk = {}
1249 def bump(self, rev):
1249 def bump(self, rev):
1250 self.rev = rev
1250 self.rev = rev
1251 self.hunk[rev] = []
1251 self.hunk[rev] = []
1252 def note(self, *args):
1252 def note(self, *args):
1253 if self.verbose:
1253 if self.verbose:
1254 self.write(*args)
1254 self.write(*args)
1255 def status(self, *args):
1255 def status(self, *args):
1256 if not self.quiet:
1256 if not self.quiet:
1257 self.write(*args)
1257 self.write(*args)
1258 def write(self, *args):
1258 def write(self, *args):
1259 self.hunk[self.rev].append(args)
1259 self.hunk[self.rev].append(args)
1260 def debug(self, *args):
1260 def debug(self, *args):
1261 if self.debugflag:
1261 if self.debugflag:
1262 self.write(*args)
1262 self.write(*args)
1263 def __getattr__(self, key):
1263 def __getattr__(self, key):
1264 return getattr(self.ui, key)
1264 return getattr(self.ui, key)
1265 cwd = repo.getcwd()
1265 cwd = repo.getcwd()
1266 if not pats and cwd:
1266 if not pats and cwd:
1267 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1267 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1268 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1268 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1269 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1269 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1270 pats, opts)
1270 pats, opts)
1271 for st, rev, fns in changeiter:
1271 for st, rev, fns in changeiter:
1272 if st == 'window':
1272 if st == 'window':
1273 du = dui(ui)
1273 du = dui(ui)
1274 elif st == 'add':
1274 elif st == 'add':
1275 du.bump(rev)
1275 du.bump(rev)
1276 br = None
1276 br = None
1277 if opts['keyword']:
1277 if opts['keyword']:
1278 changes = repo.changelog.read(repo.changelog.node(rev))
1278 changes = repo.changelog.read(repo.changelog.node(rev))
1279 miss = 0
1279 miss = 0
1280 for k in [kw.lower() for kw in opts['keyword']]:
1280 for k in [kw.lower() for kw in opts['keyword']]:
1281 if not (k in changes[1].lower() or
1281 if not (k in changes[1].lower() or
1282 k in changes[4].lower() or
1282 k in changes[4].lower() or
1283 k in " ".join(changes[3][:20]).lower()):
1283 k in " ".join(changes[3][:20]).lower()):
1284 miss = 1
1284 miss = 1
1285 break
1285 break
1286 if miss:
1286 if miss:
1287 continue
1287 continue
1288
1288
1289 if opts['branch']:
1289 if opts['branch']:
1290 br = repo.branchlookup([repo.changelog.node(rev)])
1290 br = repo.branchlookup([repo.changelog.node(rev)])
1291
1291
1292 show_changeset(du, repo, rev, brinfo=br)
1292 show_changeset(du, repo, rev, brinfo=br)
1293 if opts['patch']:
1293 if opts['patch']:
1294 changenode = repo.changelog.node(rev)
1294 changenode = repo.changelog.node(rev)
1295 prev, other = repo.changelog.parents(changenode)
1295 prev, other = repo.changelog.parents(changenode)
1296 dodiff(du, du, repo, prev, changenode, fns)
1296 dodiff(du, du, repo, prev, changenode, fns)
1297 du.write("\n\n")
1297 du.write("\n\n")
1298 elif st == 'iter':
1298 elif st == 'iter':
1299 for args in du.hunk[rev]:
1299 for args in du.hunk[rev]:
1300 ui.write(*args)
1300 ui.write(*args)
1301
1301
1302 def manifest(ui, repo, rev=None):
1302 def manifest(ui, repo, rev=None):
1303 """output the latest or given revision of the project manifest"""
1303 """output the latest or given revision of the project manifest"""
1304 if rev:
1304 if rev:
1305 try:
1305 try:
1306 # assume all revision numbers are for changesets
1306 # assume all revision numbers are for changesets
1307 n = repo.lookup(rev)
1307 n = repo.lookup(rev)
1308 change = repo.changelog.read(n)
1308 change = repo.changelog.read(n)
1309 n = change[0]
1309 n = change[0]
1310 except hg.RepoError:
1310 except hg.RepoError:
1311 n = repo.manifest.lookup(rev)
1311 n = repo.manifest.lookup(rev)
1312 else:
1312 else:
1313 n = repo.manifest.tip()
1313 n = repo.manifest.tip()
1314 m = repo.manifest.read(n)
1314 m = repo.manifest.read(n)
1315 mf = repo.manifest.readflags(n)
1315 mf = repo.manifest.readflags(n)
1316 files = m.keys()
1316 files = m.keys()
1317 files.sort()
1317 files.sort()
1318
1318
1319 for f in files:
1319 for f in files:
1320 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1320 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1321
1321
1322 def outgoing(ui, repo, dest="default-push", **opts):
1322 def outgoing(ui, repo, dest="default-push", **opts):
1323 """show changesets not found in destination"""
1323 """show changesets not found in destination"""
1324 dest = ui.expandpath(dest)
1324 dest = ui.expandpath(dest)
1325 other = hg.repository(ui, dest)
1325 other = hg.repository(ui, dest)
1326 o = repo.findoutgoing(other)
1326 o = repo.findoutgoing(other)
1327 o = repo.newer(o)
1327 o = repo.newer(o)
1328 for n in o:
1328 for n in o:
1329 show_changeset(ui, repo, changenode=n)
1329 show_changeset(ui, repo, changenode=n)
1330 if opts['patch']:
1330 if opts['patch']:
1331 prev = repo.changelog.parents(n)[0]
1331 prev = repo.changelog.parents(n)[0]
1332 dodiff(ui, ui, repo, prev, n)
1332 dodiff(ui, ui, repo, prev, n)
1333 ui.write("\n")
1333 ui.write("\n")
1334
1334
1335 def parents(ui, repo, rev=None):
1335 def parents(ui, repo, rev=None):
1336 """show the parents of the working dir or revision"""
1336 """show the parents of the working dir or revision"""
1337 if rev:
1337 if rev:
1338 p = repo.changelog.parents(repo.lookup(rev))
1338 p = repo.changelog.parents(repo.lookup(rev))
1339 else:
1339 else:
1340 p = repo.dirstate.parents()
1340 p = repo.dirstate.parents()
1341
1341
1342 for n in p:
1342 for n in p:
1343 if n != nullid:
1343 if n != nullid:
1344 show_changeset(ui, repo, changenode=n)
1344 show_changeset(ui, repo, changenode=n)
1345
1345
1346 def paths(ui, search=None):
1346 def paths(ui, search=None):
1347 """show definition of symbolic path names"""
1347 """show definition of symbolic path names"""
1348 try:
1348 try:
1349 repo = hg.repository(ui=ui)
1349 repo = hg.repository(ui=ui)
1350 except hg.RepoError:
1350 except hg.RepoError:
1351 pass
1351 pass
1352
1352
1353 if search:
1353 if search:
1354 for name, path in ui.configitems("paths"):
1354 for name, path in ui.configitems("paths"):
1355 if name == search:
1355 if name == search:
1356 ui.write("%s\n" % path)
1356 ui.write("%s\n" % path)
1357 return
1357 return
1358 ui.warn(_("not found!\n"))
1358 ui.warn(_("not found!\n"))
1359 return 1
1359 return 1
1360 else:
1360 else:
1361 for name, path in ui.configitems("paths"):
1361 for name, path in ui.configitems("paths"):
1362 ui.write("%s = %s\n" % (name, path))
1362 ui.write("%s = %s\n" % (name, path))
1363
1363
1364 def pull(ui, repo, source="default", **opts):
1364 def pull(ui, repo, source="default", **opts):
1365 """pull changes from the specified source"""
1365 """pull changes from the specified source"""
1366 source = ui.expandpath(source)
1366 source = ui.expandpath(source)
1367 ui.status(_('pulling from %s\n') % (source))
1367 ui.status(_('pulling from %s\n') % (source))
1368
1368
1369 if opts['ssh']:
1369 if opts['ssh']:
1370 ui.setconfig("ui", "ssh", opts['ssh'])
1370 ui.setconfig("ui", "ssh", opts['ssh'])
1371 if opts['remotecmd']:
1371 if opts['remotecmd']:
1372 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1372 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1373
1373
1374 other = hg.repository(ui, source)
1374 other = hg.repository(ui, source)
1375 r = repo.pull(other)
1375 r = repo.pull(other)
1376 if not r:
1376 if not r:
1377 if opts['update']:
1377 if opts['update']:
1378 return update(ui, repo)
1378 return update(ui, repo)
1379 else:
1379 else:
1380 ui.status(_("(run 'hg update' to get a working copy)\n"))
1380 ui.status(_("(run 'hg update' to get a working copy)\n"))
1381
1381
1382 return r
1382 return r
1383
1383
1384 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1384 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1385 """push changes to the specified destination"""
1385 """push changes to the specified destination"""
1386 dest = ui.expandpath(dest)
1386 dest = ui.expandpath(dest)
1387 ui.status('pushing to %s\n' % (dest))
1387 ui.status('pushing to %s\n' % (dest))
1388
1388
1389 if ssh:
1389 if ssh:
1390 ui.setconfig("ui", "ssh", ssh)
1390 ui.setconfig("ui", "ssh", ssh)
1391 if remotecmd:
1391 if remotecmd:
1392 ui.setconfig("ui", "remotecmd", remotecmd)
1392 ui.setconfig("ui", "remotecmd", remotecmd)
1393
1393
1394 other = hg.repository(ui, dest)
1394 other = hg.repository(ui, dest)
1395 r = repo.push(other, force)
1395 r = repo.push(other, force)
1396 return r
1396 return r
1397
1397
1398 def rawcommit(ui, repo, *flist, **rc):
1398 def rawcommit(ui, repo, *flist, **rc):
1399 "raw commit interface"
1399 "raw commit interface"
1400 if rc['text']:
1400 if rc['text']:
1401 ui.warn(_("Warning: -t and --text is deprecated,"
1401 ui.warn(_("Warning: -t and --text is deprecated,"
1402 " please use -m or --message instead.\n"))
1402 " please use -m or --message instead.\n"))
1403 message = rc['message'] or rc['text']
1403 message = rc['message'] or rc['text']
1404 if not message and rc['logfile']:
1404 if not message and rc['logfile']:
1405 try:
1405 try:
1406 message = open(rc['logfile']).read()
1406 message = open(rc['logfile']).read()
1407 except IOError:
1407 except IOError:
1408 pass
1408 pass
1409 if not message and not rc['logfile']:
1409 if not message and not rc['logfile']:
1410 raise util.Abort(_("missing commit message"))
1410 raise util.Abort(_("missing commit message"))
1411
1411
1412 files = relpath(repo, list(flist))
1412 files = relpath(repo, list(flist))
1413 if rc['files']:
1413 if rc['files']:
1414 files += open(rc['files']).read().splitlines()
1414 files += open(rc['files']).read().splitlines()
1415
1415
1416 rc['parent'] = map(repo.lookup, rc['parent'])
1416 rc['parent'] = map(repo.lookup, rc['parent'])
1417
1417
1418 try:
1418 try:
1419 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1419 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1420 except ValueError, inst:
1420 except ValueError, inst:
1421 raise util.Abort(str(inst))
1421 raise util.Abort(str(inst))
1422
1422
1423 def recover(ui, repo):
1423 def recover(ui, repo):
1424 """roll back an interrupted transaction"""
1424 """roll back an interrupted transaction"""
1425 repo.recover()
1425 repo.recover()
1426
1426
1427 def remove(ui, repo, pat, *pats, **opts):
1427 def remove(ui, repo, pat, *pats, **opts):
1428 """remove the specified files on the next commit"""
1428 """remove the specified files on the next commit"""
1429 names = []
1429 names = []
1430 def okaytoremove(abs, rel, exact):
1430 def okaytoremove(abs, rel, exact):
1431 c, a, d, u = repo.changes(files = [abs])
1431 c, a, d, u = repo.changes(files = [abs])
1432 reason = None
1432 reason = None
1433 if c: reason = _('is modified')
1433 if c: reason = _('is modified')
1434 elif a: reason = _('has been marked for add')
1434 elif a: reason = _('has been marked for add')
1435 elif u: reason = _('is not managed')
1435 elif u: reason = _('is not managed')
1436 if reason:
1436 if reason:
1437 if exact: ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1437 if exact: ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1438 else:
1438 else:
1439 return True
1439 return True
1440 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1440 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1441 if okaytoremove(abs, rel, exact):
1441 if okaytoremove(abs, rel, exact):
1442 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1442 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1443 names.append(abs)
1443 names.append(abs)
1444 for name in names:
1444 for name in names:
1445 try:
1445 try:
1446 os.unlink(name)
1446 os.unlink(name)
1447 except OSError, inst:
1447 except OSError, inst:
1448 if inst.errno != errno.ENOENT: raise
1448 if inst.errno != errno.ENOENT: raise
1449 repo.remove(names)
1449 repo.remove(names)
1450
1450
1451 def rename(ui, repo, *pats, **opts):
1451 def rename(ui, repo, *pats, **opts):
1452 """rename files; equivalent of copy + remove"""
1452 """rename files; equivalent of copy + remove"""
1453 errs, copied = docopy(ui, repo, pats, opts)
1453 errs, copied = docopy(ui, repo, pats, opts)
1454 names = []
1454 names = []
1455 for abs, rel, exact in copied:
1455 for abs, rel, exact in copied:
1456 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1456 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1457 try:
1457 try:
1458 os.unlink(rel)
1458 os.unlink(rel)
1459 except OSError, inst:
1459 except OSError, inst:
1460 if inst.errno != errno.ENOENT: raise
1460 if inst.errno != errno.ENOENT: raise
1461 names.append(abs)
1461 names.append(abs)
1462 repo.remove(names)
1462 repo.remove(names)
1463 return errs
1463 return errs
1464
1464
1465 def revert(ui, repo, *names, **opts):
1465 def revert(ui, repo, *names, **opts):
1466 """revert modified files or dirs back to their unmodified states"""
1466 """revert modified files or dirs back to their unmodified states"""
1467 node = opts['rev'] and repo.lookup(opts['rev']) or \
1467 node = opts['rev'] and repo.lookup(opts['rev']) or \
1468 repo.dirstate.parents()[0]
1468 repo.dirstate.parents()[0]
1469 root = os.path.realpath(repo.root)
1469 root = os.path.realpath(repo.root)
1470
1470
1471 def trimpath(p):
1471 def trimpath(p):
1472 p = os.path.realpath(p)
1472 p = os.path.realpath(p)
1473 if p.startswith(root):
1473 if p.startswith(root):
1474 rest = p[len(root):]
1474 rest = p[len(root):]
1475 if not rest:
1475 if not rest:
1476 return rest
1476 return rest
1477 if p.startswith(os.sep):
1477 if p.startswith(os.sep):
1478 return rest[1:]
1478 return rest[1:]
1479 return p
1479 return p
1480
1480
1481 relnames = map(trimpath, names or [os.getcwd()])
1481 relnames = map(trimpath, names or [os.getcwd()])
1482 chosen = {}
1482 chosen = {}
1483
1483
1484 def choose(name):
1484 def choose(name):
1485 def body(name):
1485 def body(name):
1486 for r in relnames:
1486 for r in relnames:
1487 if not name.startswith(r):
1487 if not name.startswith(r):
1488 continue
1488 continue
1489 rest = name[len(r):]
1489 rest = name[len(r):]
1490 if not rest:
1490 if not rest:
1491 return r, True
1491 return r, True
1492 depth = rest.count(os.sep)
1492 depth = rest.count(os.sep)
1493 if not r:
1493 if not r:
1494 if depth == 0 or not opts['nonrecursive']:
1494 if depth == 0 or not opts['nonrecursive']:
1495 return r, True
1495 return r, True
1496 elif rest[0] == os.sep:
1496 elif rest[0] == os.sep:
1497 if depth == 1 or not opts['nonrecursive']:
1497 if depth == 1 or not opts['nonrecursive']:
1498 return r, True
1498 return r, True
1499 return None, False
1499 return None, False
1500 relname, ret = body(name)
1500 relname, ret = body(name)
1501 if ret:
1501 if ret:
1502 chosen[relname] = 1
1502 chosen[relname] = 1
1503 return ret
1503 return ret
1504
1504
1505 r = repo.update(node, False, True, choose, False)
1505 r = repo.update(node, False, True, choose, False)
1506 for n in relnames:
1506 for n in relnames:
1507 if n not in chosen:
1507 if n not in chosen:
1508 ui.warn(_('error: no matches for %s\n') % n)
1508 ui.warn(_('error: no matches for %s\n') % n)
1509 r = 1
1509 r = 1
1510 sys.stdout.flush()
1510 sys.stdout.flush()
1511 return r
1511 return r
1512
1512
1513 def root(ui, repo):
1513 def root(ui, repo):
1514 """print the root (top) of the current working dir"""
1514 """print the root (top) of the current working dir"""
1515 ui.write(repo.root + "\n")
1515 ui.write(repo.root + "\n")
1516
1516
1517 def serve(ui, repo, **opts):
1517 def serve(ui, repo, **opts):
1518 """export the repository via HTTP"""
1518 """export the repository via HTTP"""
1519
1519
1520 if opts["stdio"]:
1520 if opts["stdio"]:
1521 fin, fout = sys.stdin, sys.stdout
1521 fin, fout = sys.stdin, sys.stdout
1522 sys.stdout = sys.stderr
1522 sys.stdout = sys.stderr
1523
1523
1524 def getarg():
1524 def getarg():
1525 argline = fin.readline()[:-1]
1525 argline = fin.readline()[:-1]
1526 arg, l = argline.split()
1526 arg, l = argline.split()
1527 val = fin.read(int(l))
1527 val = fin.read(int(l))
1528 return arg, val
1528 return arg, val
1529 def respond(v):
1529 def respond(v):
1530 fout.write("%d\n" % len(v))
1530 fout.write("%d\n" % len(v))
1531 fout.write(v)
1531 fout.write(v)
1532 fout.flush()
1532 fout.flush()
1533
1533
1534 lock = None
1534 lock = None
1535
1535
1536 while 1:
1536 while 1:
1537 cmd = fin.readline()[:-1]
1537 cmd = fin.readline()[:-1]
1538 if cmd == '':
1538 if cmd == '':
1539 return
1539 return
1540 if cmd == "heads":
1540 if cmd == "heads":
1541 h = repo.heads()
1541 h = repo.heads()
1542 respond(" ".join(map(hex, h)) + "\n")
1542 respond(" ".join(map(hex, h)) + "\n")
1543 if cmd == "lock":
1543 if cmd == "lock":
1544 lock = repo.lock()
1544 lock = repo.lock()
1545 respond("")
1545 respond("")
1546 if cmd == "unlock":
1546 if cmd == "unlock":
1547 if lock:
1547 if lock:
1548 lock.release()
1548 lock.release()
1549 lock = None
1549 lock = None
1550 respond("")
1550 respond("")
1551 elif cmd == "branches":
1551 elif cmd == "branches":
1552 arg, nodes = getarg()
1552 arg, nodes = getarg()
1553 nodes = map(bin, nodes.split(" "))
1553 nodes = map(bin, nodes.split(" "))
1554 r = []
1554 r = []
1555 for b in repo.branches(nodes):
1555 for b in repo.branches(nodes):
1556 r.append(" ".join(map(hex, b)) + "\n")
1556 r.append(" ".join(map(hex, b)) + "\n")
1557 respond("".join(r))
1557 respond("".join(r))
1558 elif cmd == "between":
1558 elif cmd == "between":
1559 arg, pairs = getarg()
1559 arg, pairs = getarg()
1560 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1560 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1561 r = []
1561 r = []
1562 for b in repo.between(pairs):
1562 for b in repo.between(pairs):
1563 r.append(" ".join(map(hex, b)) + "\n")
1563 r.append(" ".join(map(hex, b)) + "\n")
1564 respond("".join(r))
1564 respond("".join(r))
1565 elif cmd == "changegroup":
1565 elif cmd == "changegroup":
1566 nodes = []
1566 nodes = []
1567 arg, roots = getarg()
1567 arg, roots = getarg()
1568 nodes = map(bin, roots.split(" "))
1568 nodes = map(bin, roots.split(" "))
1569
1569
1570 cg = repo.changegroup(nodes)
1570 cg = repo.changegroup(nodes)
1571 while 1:
1571 while 1:
1572 d = cg.read(4096)
1572 d = cg.read(4096)
1573 if not d:
1573 if not d:
1574 break
1574 break
1575 fout.write(d)
1575 fout.write(d)
1576
1576
1577 fout.flush()
1577 fout.flush()
1578
1578
1579 elif cmd == "addchangegroup":
1579 elif cmd == "addchangegroup":
1580 if not lock:
1580 if not lock:
1581 respond("not locked")
1581 respond("not locked")
1582 continue
1582 continue
1583 respond("")
1583 respond("")
1584
1584
1585 r = repo.addchangegroup(fin)
1585 r = repo.addchangegroup(fin)
1586 respond("")
1586 respond("")
1587
1587
1588 optlist = "name templates style address port ipv6 accesslog errorlog"
1588 optlist = "name templates style address port ipv6 accesslog errorlog"
1589 for o in optlist.split():
1589 for o in optlist.split():
1590 if opts[o]:
1590 if opts[o]:
1591 ui.setconfig("web", o, opts[o])
1591 ui.setconfig("web", o, opts[o])
1592
1592
1593 try:
1593 try:
1594 httpd = hgweb.create_server(repo)
1594 httpd = hgweb.create_server(repo)
1595 except socket.error, inst:
1595 except socket.error, inst:
1596 raise util.Abort('cannot start server: ' + inst.args[1])
1596 raise util.Abort('cannot start server: ' + inst.args[1])
1597
1597
1598 if ui.verbose:
1598 if ui.verbose:
1599 addr, port = httpd.socket.getsockname()
1599 addr, port = httpd.socket.getsockname()
1600 if addr == '0.0.0.0':
1600 if addr == '0.0.0.0':
1601 addr = socket.gethostname()
1601 addr = socket.gethostname()
1602 else:
1602 else:
1603 try:
1603 try:
1604 addr = socket.gethostbyaddr(addr)[0]
1604 addr = socket.gethostbyaddr(addr)[0]
1605 except socket.error:
1605 except socket.error:
1606 pass
1606 pass
1607 if port != 80:
1607 if port != 80:
1608 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
1608 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
1609 else:
1609 else:
1610 ui.status(_('listening at http://%s/\n') % addr)
1610 ui.status(_('listening at http://%s/\n') % addr)
1611 httpd.serve_forever()
1611 httpd.serve_forever()
1612
1612
1613 def status(ui, repo, *pats, **opts):
1613 def status(ui, repo, *pats, **opts):
1614 '''show changed files in the working directory
1614 '''show changed files in the working directory
1615
1615
1616 M = modified
1616 M = modified
1617 A = added
1617 A = added
1618 R = removed
1618 R = removed
1619 ? = not tracked
1619 ? = not tracked
1620 '''
1620 '''
1621
1621
1622 cwd = repo.getcwd()
1622 cwd = repo.getcwd()
1623 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1623 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1624 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1624 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1625 for n in repo.changes(files=files, match=matchfn)]
1625 for n in repo.changes(files=files, match=matchfn)]
1626
1626
1627 changetypes = [(_('modified'), 'M', c),
1627 changetypes = [(_('modified'), 'M', c),
1628 (_('added'), 'A', a),
1628 (_('added'), 'A', a),
1629 (_('removed'), 'R', d),
1629 (_('removed'), 'R', d),
1630 (_('unknown'), '?', u)]
1630 (_('unknown'), '?', u)]
1631
1631
1632 end = opts['print0'] and '\0' or '\n'
1632 end = opts['print0'] and '\0' or '\n'
1633
1633
1634 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1634 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1635 or changetypes):
1635 or changetypes):
1636 if opts['no_status']:
1636 if opts['no_status']:
1637 format = "%%s%s" % end
1637 format = "%%s%s" % end
1638 else:
1638 else:
1639 format = "%s %%s%s" % (char, end);
1639 format = "%s %%s%s" % (char, end);
1640
1640
1641 for f in changes:
1641 for f in changes:
1642 ui.write(format % f)
1642 ui.write(format % f)
1643
1643
1644 def tag(ui, repo, name, rev=None, **opts):
1644 def tag(ui, repo, name, rev=None, **opts):
1645 """add a tag for the current tip or a given revision"""
1645 """add a tag for the current tip or a given revision"""
1646 if opts['text']:
1646 if opts['text']:
1647 ui.warn(_("Warning: -t and --text is deprecated,"
1647 ui.warn(_("Warning: -t and --text is deprecated,"
1648 " please use -m or --message instead.\n"))
1648 " please use -m or --message instead.\n"))
1649 if name == "tip":
1649 if name == "tip":
1650 raise util.Abort(_("the name 'tip' is reserved"))
1650 raise util.Abort(_("the name 'tip' is reserved"))
1651 if rev:
1651 if rev:
1652 r = hex(repo.lookup(rev))
1652 r = hex(repo.lookup(rev))
1653 else:
1653 else:
1654 r = hex(repo.changelog.tip())
1654 r = hex(repo.changelog.tip())
1655
1655
1656 if name.find(revrangesep) >= 0:
1656 if name.find(revrangesep) >= 0:
1657 raise util.Abort(_("'%s' cannot be used in a tag name") % revrangesep)
1657 raise util.Abort(_("'%s' cannot be used in a tag name") % revrangesep)
1658
1658
1659 if opts['local']:
1659 if opts['local']:
1660 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1660 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1661 return
1661 return
1662
1662
1663 (c, a, d, u) = repo.changes()
1663 (c, a, d, u) = repo.changes()
1664 for x in (c, a, d, u):
1664 for x in (c, a, d, u):
1665 if ".hgtags" in x:
1665 if ".hgtags" in x:
1666 raise util.Abort(_("working copy of .hgtags is changed "
1666 raise util.Abort(_("working copy of .hgtags is changed "
1667 "(please commit .hgtags manually)"))
1667 "(please commit .hgtags manually)"))
1668
1668
1669 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1669 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1670 if repo.dirstate.state(".hgtags") == '?':
1670 if repo.dirstate.state(".hgtags") == '?':
1671 repo.add([".hgtags"])
1671 repo.add([".hgtags"])
1672
1672
1673 message = (opts['message'] or opts['text'] or
1673 message = (opts['message'] or opts['text'] or
1674 _("Added tag %s for changeset %s") % (name, r))
1674 _("Added tag %s for changeset %s") % (name, r))
1675 try:
1675 try:
1676 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1676 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1677 except ValueError, inst:
1677 except ValueError, inst:
1678 raise util.Abort(str(inst))
1678 raise util.Abort(str(inst))
1679
1679
1680 def tags(ui, repo):
1680 def tags(ui, repo):
1681 """list repository tags"""
1681 """list repository tags"""
1682
1682
1683 l = repo.tagslist()
1683 l = repo.tagslist()
1684 l.reverse()
1684 l.reverse()
1685 for t, n in l:
1685 for t, n in l:
1686 try:
1686 try:
1687 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1687 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1688 except KeyError:
1688 except KeyError:
1689 r = " ?:?"
1689 r = " ?:?"
1690 ui.write("%-30s %s\n" % (t, r))
1690 ui.write("%-30s %s\n" % (t, r))
1691
1691
1692 def tip(ui, repo):
1692 def tip(ui, repo):
1693 """show the tip revision"""
1693 """show the tip revision"""
1694 n = repo.changelog.tip()
1694 n = repo.changelog.tip()
1695 show_changeset(ui, repo, changenode=n)
1695 show_changeset(ui, repo, changenode=n)
1696
1696
1697 def unbundle(ui, repo, fname):
1697 def unbundle(ui, repo, fname):
1698 """apply a changegroup file"""
1698 """apply a changegroup file"""
1699 f = urllib.urlopen(fname)
1699 f = urllib.urlopen(fname)
1700
1700
1701 if f.read(4) != "HG10":
1701 if f.read(4) != "HG10":
1702 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
1702 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
1703
1703
1704 def bzgenerator(f):
1704 def bzgenerator(f):
1705 zd = bz2.BZ2Decompressor()
1705 zd = bz2.BZ2Decompressor()
1706 for chunk in f:
1706 for chunk in f:
1707 yield zd.decompress(chunk)
1707 yield zd.decompress(chunk)
1708 yield zd.flush()
1708 yield zd.flush()
1709
1709
1710 bzgen = bzgenerator(util.filechunkiter(f, 4096))
1710 bzgen = bzgenerator(util.filechunkiter(f, 4096))
1711 repo.addchangegroup(util.chunkbuffer(bzgen))
1711 repo.addchangegroup(util.chunkbuffer(bzgen))
1712
1712
1713 def undo(ui, repo):
1713 def undo(ui, repo):
1714 """undo the last commit or pull
1714 """undo the last commit or pull
1715
1715
1716 Roll back the last pull or commit transaction on the
1716 Roll back the last pull or commit transaction on the
1717 repository, restoring the project to its earlier state.
1717 repository, restoring the project to its earlier state.
1718
1718
1719 This command should be used with care. There is only one level of
1719 This command should be used with care. There is only one level of
1720 undo and there is no redo.
1720 undo and there is no redo.
1721
1721
1722 This command is not intended for use on public repositories. Once
1722 This command is not intended for use on public repositories. Once
1723 a change is visible for pull by other users, undoing it locally is
1723 a change is visible for pull by other users, undoing it locally is
1724 ineffective.
1724 ineffective.
1725 """
1725 """
1726 repo.undo()
1726 repo.undo()
1727
1727
1728 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1728 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1729 '''update or merge working directory
1729 '''update or merge working directory
1730
1730
1731 If there are no outstanding changes in the working directory and
1731 If there are no outstanding changes in the working directory and
1732 there is a linear relationship between the current version and the
1732 there is a linear relationship between the current version and the
1733 requested version, the result is the requested version.
1733 requested version, the result is the requested version.
1734
1734
1735 Otherwise the result is a merge between the contents of the
1735 Otherwise the result is a merge between the contents of the
1736 current working directory and the requested version. Files that
1736 current working directory and the requested version. Files that
1737 changed between either parent are marked as changed for the next
1737 changed between either parent are marked as changed for the next
1738 commit and a commit must be performed before any further updates
1738 commit and a commit must be performed before any further updates
1739 are allowed.
1739 are allowed.
1740 '''
1740 '''
1741 if branch:
1741 if branch:
1742 br = repo.branchlookup(branch=branch)
1742 br = repo.branchlookup(branch=branch)
1743 found = []
1743 found = []
1744 for x in br:
1744 for x in br:
1745 if branch in br[x]:
1745 if branch in br[x]:
1746 found.append(x)
1746 found.append(x)
1747 if len(found) > 1:
1747 if len(found) > 1:
1748 ui.warn(_("Found multiple heads for %s\n") % branch)
1748 ui.warn(_("Found multiple heads for %s\n") % branch)
1749 for x in found:
1749 for x in found:
1750 show_changeset(ui, repo, changenode=x, brinfo=br)
1750 show_changeset(ui, repo, changenode=x, brinfo=br)
1751 return 1
1751 return 1
1752 if len(found) == 1:
1752 if len(found) == 1:
1753 node = found[0]
1753 node = found[0]
1754 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
1754 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
1755 else:
1755 else:
1756 ui.warn(_("branch %s not found\n") % (branch))
1756 ui.warn(_("branch %s not found\n") % (branch))
1757 return 1
1757 return 1
1758 else:
1758 else:
1759 node = node and repo.lookup(node) or repo.changelog.tip()
1759 node = node and repo.lookup(node) or repo.changelog.tip()
1760 return repo.update(node, allow=merge, force=clean)
1760 return repo.update(node, allow=merge, force=clean)
1761
1761
1762 def verify(ui, repo):
1762 def verify(ui, repo):
1763 """verify the integrity of the repository"""
1763 """verify the integrity of the repository"""
1764 return repo.verify()
1764 return repo.verify()
1765
1765
1766 # Command options and aliases are listed here, alphabetically
1766 # Command options and aliases are listed here, alphabetically
1767
1767
1768 table = {
1768 table = {
1769 "^add":
1769 "^add":
1770 (add,
1770 (add,
1771 [('I', 'include', [], _('include path in search')),
1771 [('I', 'include', [], _('include path in search')),
1772 ('X', 'exclude', [], _('exclude path from search'))],
1772 ('X', 'exclude', [], _('exclude path from search'))],
1773 "hg add [OPTION]... [FILE]..."),
1773 "hg add [OPTION]... [FILE]..."),
1774 "addremove":
1774 "addremove":
1775 (addremove,
1775 (addremove,
1776 [('I', 'include', [], _('include path in search')),
1776 [('I', 'include', [], _('include path in search')),
1777 ('X', 'exclude', [], _('exclude path from search'))],
1777 ('X', 'exclude', [], _('exclude path from search'))],
1778 _("hg addremove [OPTION]... [FILE]...")),
1778 _("hg addremove [OPTION]... [FILE]...")),
1779 "^annotate":
1779 "^annotate":
1780 (annotate,
1780 (annotate,
1781 [('r', 'rev', '', _('revision')),
1781 [('r', 'rev', '', _('revision')),
1782 ('a', 'text', None, _('treat all files as text')),
1782 ('a', 'text', None, _('treat all files as text')),
1783 ('u', 'user', None, _('show user')),
1783 ('u', 'user', None, _('show user')),
1784 ('n', 'number', None, _('show revision number')),
1784 ('n', 'number', None, _('show revision number')),
1785 ('c', 'changeset', None, _('show changeset')),
1785 ('c', 'changeset', None, _('show changeset')),
1786 ('I', 'include', [], _('include path in search')),
1786 ('I', 'include', [], _('include path in search')),
1787 ('X', 'exclude', [], _('exclude path from search'))],
1787 ('X', 'exclude', [], _('exclude path from search'))],
1788 _('hg annotate [OPTION]... FILE...')),
1788 _('hg annotate [OPTION]... FILE...')),
1789 "bundle":
1789 "bundle":
1790 (bundle,
1790 (bundle,
1791 [],
1791 [],
1792 _('hg bundle FILE DEST')),
1792 _('hg bundle FILE DEST')),
1793 "cat":
1793 "cat":
1794 (cat,
1794 (cat,
1795 [('I', 'include', [], _('include path in search')),
1795 [('I', 'include', [], _('include path in search')),
1796 ('X', 'exclude', [], _('exclude path from search')),
1796 ('X', 'exclude', [], _('exclude path from search')),
1797 ('o', 'output', "", _('output to file')),
1797 ('o', 'output', "", _('output to file')),
1798 ('r', 'rev', '', _('revision'))],
1798 ('r', 'rev', '', _('revision'))],
1799 _('hg cat [OPTION]... FILE...')),
1799 _('hg cat [OPTION]... FILE...')),
1800 "^clone":
1800 "^clone":
1801 (clone,
1801 (clone,
1802 [('U', 'noupdate', None, _('skip update after cloning')),
1802 [('U', 'noupdate', None, _('skip update after cloning')),
1803 ('e', 'ssh', "", _('ssh command')),
1803 ('e', 'ssh', "", _('ssh command')),
1804 ('', 'pull', None, _('use pull protocol to copy metadata')),
1804 ('', 'pull', None, _('use pull protocol to copy metadata')),
1805 ('', 'remotecmd', "", _('remote hg command'))],
1805 ('', 'remotecmd', "", _('remote hg command'))],
1806 _('hg clone [OPTION]... SOURCE [DEST]')),
1806 _('hg clone [OPTION]... SOURCE [DEST]')),
1807 "^commit|ci":
1807 "^commit|ci":
1808 (commit,
1808 (commit,
1809 [('A', 'addremove', None, _('run add/remove during commit')),
1809 [('A', 'addremove', None, _('run add/remove during commit')),
1810 ('I', 'include', [], _('include path in search')),
1810 ('I', 'include', [], _('include path in search')),
1811 ('X', 'exclude', [], _('exclude path from search')),
1811 ('X', 'exclude', [], _('exclude path from search')),
1812 ('m', 'message', "", _('commit message')),
1812 ('m', 'message', "", _('commit message')),
1813 ('t', 'text', "", _('commit message (deprecated: use -m)')),
1813 ('t', 'text', "", _('commit message (deprecated: use -m)')),
1814 ('l', 'logfile', "", _('commit message file')),
1814 ('l', 'logfile', "", _('commit message file')),
1815 ('d', 'date', "", _('date code')),
1815 ('d', 'date', "", _('date code')),
1816 ('u', 'user', "", _('user'))],
1816 ('u', 'user', "", _('user'))],
1817 _('hg commit [OPTION]... [FILE]...')),
1817 _('hg commit [OPTION]... [FILE]...')),
1818 "copy|cp": (copy,
1818 "copy|cp": (copy,
1819 [('I', 'include', [], _('include path in search')),
1819 [('I', 'include', [], _('include path in search')),
1820 ('X', 'exclude', [], _('exclude path from search')),
1820 ('X', 'exclude', [], _('exclude path from search')),
1821 ('A', 'after', None, _('record a copy after it has happened')),
1821 ('A', 'after', None, _('record a copy after it has happened')),
1822 ('f', 'force', None, _('replace destination if it exists')),
1822 ('f', 'force', None, _('replace destination if it exists')),
1823 ('p', 'parents', None, _('append source path to dest'))],
1823 ('p', 'parents', None, _('append source path to dest'))],
1824 _('hg copy [OPTION]... [SOURCE]... DEST')),
1824 _('hg copy [OPTION]... [SOURCE]... DEST')),
1825 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
1825 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
1826 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
1826 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
1827 "debugconfig": (debugconfig, [], _('debugconfig')),
1827 "debugconfig": (debugconfig, [], _('debugconfig')),
1828 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
1828 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
1829 "debugstate": (debugstate, [], _('debugstate')),
1829 "debugstate": (debugstate, [], _('debugstate')),
1830 "debugdata": (debugdata, [], _('debugdata FILE REV')),
1830 "debugdata": (debugdata, [], _('debugdata FILE REV')),
1831 "debugindex": (debugindex, [], _('debugindex FILE')),
1831 "debugindex": (debugindex, [], _('debugindex FILE')),
1832 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
1832 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
1833 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
1833 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
1834 "debugwalk":
1834 "debugwalk":
1835 (debugwalk,
1835 (debugwalk,
1836 [('I', 'include', [], _('include path in search')),
1836 [('I', 'include', [], _('include path in search')),
1837 ('X', 'exclude', [], _('exclude path from search'))],
1837 ('X', 'exclude', [], _('exclude path from search'))],
1838 _('debugwalk [OPTION]... [FILE]...')),
1838 _('debugwalk [OPTION]... [FILE]...')),
1839 "^diff":
1839 "^diff":
1840 (diff,
1840 (diff,
1841 [('r', 'rev', [], _('revision')),
1841 [('r', 'rev', [], _('revision')),
1842 ('a', 'text', None, _('treat all files as text')),
1842 ('a', 'text', None, _('treat all files as text')),
1843 ('I', 'include', [], _('include path in search')),
1843 ('I', 'include', [], _('include path in search')),
1844 ('X', 'exclude', [], _('exclude path from search'))],
1844 ('X', 'exclude', [], _('exclude path from search'))],
1845 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
1845 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
1846 "^export":
1846 "^export":
1847 (export,
1847 (export,
1848 [('o', 'output', "", _('output to file')),
1848 [('o', 'output', "", _('output to file')),
1849 ('a', 'text', None, _('treat all files as text'))],
1849 ('a', 'text', None, _('treat all files as text'))],
1850 _("hg export [-a] [-o OUTFILE] REV...")),
1850 _("hg export [-a] [-o OUTFILE] REV...")),
1851 "forget":
1851 "forget":
1852 (forget,
1852 (forget,
1853 [('I', 'include', [], _('include path in search')),
1853 [('I', 'include', [], _('include path in search')),
1854 ('X', 'exclude', [], _('exclude path from search'))],
1854 ('X', 'exclude', [], _('exclude path from search'))],
1855 _("hg forget [OPTION]... FILE...")),
1855 _("hg forget [OPTION]... FILE...")),
1856 "grep":
1856 "grep":
1857 (grep,
1857 (grep,
1858 [('0', 'print0', None, _('end fields with NUL')),
1858 [('0', 'print0', None, _('end fields with NUL')),
1859 ('I', 'include', [], _('include path in search')),
1859 ('I', 'include', [], _('include path in search')),
1860 ('X', 'exclude', [], _('include path in search')),
1860 ('X', 'exclude', [], _('include path in search')),
1861 ('', 'all', None, _('print all revisions with matches')),
1861 ('', 'all', None, _('print all revisions with matches')),
1862 ('i', 'ignore-case', None, _('ignore case when matching')),
1862 ('i', 'ignore-case', None, _('ignore case when matching')),
1863 ('l', 'files-with-matches', None, _('print names of files and revs with matches')),
1863 ('l', 'files-with-matches', None, _('print names of files and revs with matches')),
1864 ('n', 'line-number', None, _('print line numbers')),
1864 ('n', 'line-number', None, _('print line numbers')),
1865 ('r', 'rev', [], _('search in revision rev')),
1865 ('r', 'rev', [], _('search in revision rev')),
1866 ('u', 'user', None, _('print user who made change'))],
1866 ('u', 'user', None, _('print user who made change'))],
1867 _("hg grep [OPTION]... PATTERN [FILE]...")),
1867 _("hg grep [OPTION]... PATTERN [FILE]...")),
1868 "heads":
1868 "heads":
1869 (heads,
1869 (heads,
1870 [('b', 'branches', None, _('find branch info'))],
1870 [('b', 'branches', None, _('find branch info'))],
1871 _('hg heads [-b]')),
1871 _('hg heads [-b]')),
1872 "help": (help_, [], _('hg help [COMMAND]')),
1872 "help": (help_, [], _('hg help [COMMAND]')),
1873 "identify|id": (identify, [], _('hg identify')),
1873 "identify|id": (identify, [], _('hg identify')),
1874 "import|patch":
1874 "import|patch":
1875 (import_,
1875 (import_,
1876 [('p', 'strip', 1, _('path strip')),
1876 [('p', 'strip', 1, _('path strip')),
1877 ('f', 'force', None, _('skip check for outstanding changes')),
1877 ('f', 'force', None, _('skip check for outstanding changes')),
1878 ('b', 'base', "", _('base path'))],
1878 ('b', 'base', "", _('base path'))],
1879 _("hg import [-f] [-p NUM] [-b BASE] PATCH...")),
1879 _("hg import [-f] [-p NUM] [-b BASE] PATCH...")),
1880 "incoming|in": (incoming,
1880 "incoming|in": (incoming,
1881 [('p', 'patch', None, _('show patch'))],
1881 [('p', 'patch', None, _('show patch'))],
1882 _('hg incoming [-p] [SOURCE]')),
1882 _('hg incoming [-p] [SOURCE]')),
1883 "^init": (init, [], _('hg init [DEST]')),
1883 "^init": (init, [], _('hg init [DEST]')),
1884 "locate":
1884 "locate":
1885 (locate,
1885 (locate,
1886 [('r', 'rev', '', _('revision')),
1886 [('r', 'rev', '', _('revision')),
1887 ('0', 'print0', None, _('end filenames with NUL')),
1887 ('0', 'print0', None, _('end filenames with NUL')),
1888 ('f', 'fullpath', None, _('print complete paths')),
1888 ('f', 'fullpath', None, _('print complete paths')),
1889 ('I', 'include', [], _('include path in search')),
1889 ('I', 'include', [], _('include path in search')),
1890 ('X', 'exclude', [], _('exclude path from search'))],
1890 ('X', 'exclude', [], _('exclude path from search'))],
1891 _('hg locate [OPTION]... [PATTERN]...')),
1891 _('hg locate [OPTION]... [PATTERN]...')),
1892 "^log|history":
1892 "^log|history":
1893 (log,
1893 (log,
1894 [('I', 'include', [], _('include path in search')),
1894 [('I', 'include', [], _('include path in search')),
1895 ('X', 'exclude', [], _('exclude path from search')),
1895 ('X', 'exclude', [], _('exclude path from search')),
1896 ('b', 'branch', None, _('show branches')),
1896 ('b', 'branch', None, _('show branches')),
1897 ('k', 'keyword', [], _('search for a keyword')),
1897 ('k', 'keyword', [], _('search for a keyword')),
1898 ('r', 'rev', [], _('revision')),
1898 ('r', 'rev', [], _('revision')),
1899 ('p', 'patch', None, _('show patch'))],
1899 ('p', 'patch', None, _('show patch'))],
1900 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
1900 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
1901 "manifest": (manifest, [], _('hg manifest [REV]')),
1901 "manifest": (manifest, [], _('hg manifest [REV]')),
1902 "outgoing|out": (outgoing,
1902 "outgoing|out": (outgoing,
1903 [('p', 'patch', None, _('show patch'))],
1903 [('p', 'patch', None, _('show patch'))],
1904 _('hg outgoing [-p] [DEST]')),
1904 _('hg outgoing [-p] [DEST]')),
1905 "parents": (parents, [], _('hg parents [REV]')),
1905 "parents": (parents, [], _('hg parents [REV]')),
1906 "paths": (paths, [], _('hg paths [NAME]')),
1906 "paths": (paths, [], _('hg paths [NAME]')),
1907 "^pull":
1907 "^pull":
1908 (pull,
1908 (pull,
1909 [('u', 'update', None, _('update working directory')),
1909 [('u', 'update', None, _('update working directory')),
1910 ('e', 'ssh', "", _('ssh command')),
1910 ('e', 'ssh', "", _('ssh command')),
1911 ('', 'remotecmd', "", _('remote hg command'))],
1911 ('', 'remotecmd', "", _('remote hg command'))],
1912 _('hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]')),
1912 _('hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]')),
1913 "^push":
1913 "^push":
1914 (push,
1914 (push,
1915 [('f', 'force', None, _('force push')),
1915 [('f', 'force', None, _('force push')),
1916 ('e', 'ssh', "", _('ssh command')),
1916 ('e', 'ssh', "", _('ssh command')),
1917 ('', 'remotecmd', "", _('remote hg command'))],
1917 ('', 'remotecmd', "", _('remote hg command'))],
1918 _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
1918 _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
1919 "rawcommit":
1919 "rawcommit":
1920 (rawcommit,
1920 (rawcommit,
1921 [('p', 'parent', [], _('parent')),
1921 [('p', 'parent', [], _('parent')),
1922 ('d', 'date', "", _('date code')),
1922 ('d', 'date', "", _('date code')),
1923 ('u', 'user', "", _('user')),
1923 ('u', 'user', "", _('user')),
1924 ('F', 'files', "", _('file list')),
1924 ('F', 'files', "", _('file list')),
1925 ('m', 'message', "", _('commit message')),
1925 ('m', 'message', "", _('commit message')),
1926 ('t', 'text', "", _('commit message (deprecated: use -m)')),
1926 ('t', 'text', "", _('commit message (deprecated: use -m)')),
1927 ('l', 'logfile', "", _('commit message file'))],
1927 ('l', 'logfile', "", _('commit message file'))],
1928 _('hg rawcommit [OPTION]... [FILE]...')),
1928 _('hg rawcommit [OPTION]... [FILE]...')),
1929 "recover": (recover, [], _("hg recover")),
1929 "recover": (recover, [], _("hg recover")),
1930 "^remove|rm": (remove,
1930 "^remove|rm": (remove,
1931 [('I', 'include', [], _('include path in search')),
1931 [('I', 'include', [], _('include path in search')),
1932 ('X', 'exclude', [], _('exclude path from search'))],
1932 ('X', 'exclude', [], _('exclude path from search'))],
1933 _("hg remove [OPTION]... FILE...")),
1933 _("hg remove [OPTION]... FILE...")),
1934 "rename|mv": (rename,
1934 "rename|mv": (rename,
1935 [('I', 'include', [], _('include path in search')),
1935 [('I', 'include', [], _('include path in search')),
1936 ('X', 'exclude', [], _('exclude path from search')),
1936 ('X', 'exclude', [], _('exclude path from search')),
1937 ('A', 'after', None, _('record a copy after it has happened')),
1937 ('A', 'after', None, _('record a copy after it has happened')),
1938 ('f', 'force', None, _('replace destination if it exists')),
1938 ('f', 'force', None, _('replace destination if it exists')),
1939 ('p', 'parents', None, _('append source path to dest'))],
1939 ('p', 'parents', None, _('append source path to dest'))],
1940 _('hg rename [OPTION]... [SOURCE]... DEST')),
1940 _('hg rename [OPTION]... [SOURCE]... DEST')),
1941 "^revert":
1941 "^revert":
1942 (revert,
1942 (revert,
1943 [("n", "nonrecursive", None, _("don't recurse into subdirs")),
1943 [("n", "nonrecursive", None, _("don't recurse into subdirs")),
1944 ("r", "rev", "", _("revision"))],
1944 ("r", "rev", "", _("revision"))],
1945 _("hg revert [-n] [-r REV] [NAME]...")),
1945 _("hg revert [-n] [-r REV] [NAME]...")),
1946 "root": (root, [], _("hg root")),
1946 "root": (root, [], _("hg root")),
1947 "^serve":
1947 "^serve":
1948 (serve,
1948 (serve,
1949 [('A', 'accesslog', '', _('access log file')),
1949 [('A', 'accesslog', '', _('access log file')),
1950 ('E', 'errorlog', '', _('error log file')),
1950 ('E', 'errorlog', '', _('error log file')),
1951 ('p', 'port', 0, _('listen port')),
1951 ('p', 'port', 0, _('listen port')),
1952 ('a', 'address', '', _('interface address')),
1952 ('a', 'address', '', _('interface address')),
1953 ('n', 'name', "", _('repository name')),
1953 ('n', 'name', "", _('repository name')),
1954 ('', 'stdio', None, _('for remote clients')),
1954 ('', 'stdio', None, _('for remote clients')),
1955 ('t', 'templates', "", _('template directory')),
1955 ('t', 'templates', "", _('template directory')),
1956 ('', 'style', "", _('template style')),
1956 ('', 'style', "", _('template style')),
1957 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
1957 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
1958 _("hg serve [OPTION]...")),
1958 _("hg serve [OPTION]...")),
1959 "^status":
1959 "^status":
1960 (status,
1960 (status,
1961 [('m', 'modified', None, _('show only modified files')),
1961 [('m', 'modified', None, _('show only modified files')),
1962 ('a', 'added', None, _('show only added files')),
1962 ('a', 'added', None, _('show only added files')),
1963 ('r', 'removed', None, _('show only removed files')),
1963 ('r', 'removed', None, _('show only removed files')),
1964 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
1964 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
1965 ('n', 'no-status', None, _('hide status prefix')),
1965 ('n', 'no-status', None, _('hide status prefix')),
1966 ('0', 'print0', None, _('end filenames with NUL')),
1966 ('0', 'print0', None, _('end filenames with NUL')),
1967 ('I', 'include', [], _('include path in search')),
1967 ('I', 'include', [], _('include path in search')),
1968 ('X', 'exclude', [], _('exclude path from search'))],
1968 ('X', 'exclude', [], _('exclude path from search'))],
1969 _("hg status [OPTION]... [FILE]...")),
1969 _("hg status [OPTION]... [FILE]...")),
1970 "tag":
1970 "tag":
1971 (tag,
1971 (tag,
1972 [('l', 'local', None, _('make the tag local')),
1972 [('l', 'local', None, _('make the tag local')),
1973 ('m', 'message', "", _('commit message')),
1973 ('m', 'message', "", _('commit message')),
1974 ('t', 'text', "", _('commit message (deprecated: use -m)')),
1974 ('t', 'text', "", _('commit message (deprecated: use -m)')),
1975 ('d', 'date', "", _('date code')),
1975 ('d', 'date', "", _('date code')),
1976 ('u', 'user', "", _('user'))],
1976 ('u', 'user', "", _('user'))],
1977 _('hg tag [OPTION]... NAME [REV]')),
1977 _('hg tag [OPTION]... NAME [REV]')),
1978 "tags": (tags, [], _('hg tags')),
1978 "tags": (tags, [], _('hg tags')),
1979 "tip": (tip, [], _('hg tip')),
1979 "tip": (tip, [], _('hg tip')),
1980 "unbundle":
1980 "unbundle":
1981 (unbundle,
1981 (unbundle,
1982 [],
1982 [],
1983 _('hg unbundle FILE')),
1983 _('hg unbundle FILE')),
1984 "undo": (undo, [], _('hg undo')),
1984 "undo": (undo, [], _('hg undo')),
1985 "^update|up|checkout|co":
1985 "^update|up|checkout|co":
1986 (update,
1986 (update,
1987 [('b', 'branch', "", _('checkout the head of a specific branch')),
1987 [('b', 'branch', "", _('checkout the head of a specific branch')),
1988 ('m', 'merge', None, _('allow merging of conflicts')),
1988 ('m', 'merge', None, _('allow merging of conflicts')),
1989 ('C', 'clean', None, _('overwrite locally modified files'))],
1989 ('C', 'clean', None, _('overwrite locally modified files'))],
1990 _('hg update [-b TAG] [-m] [-C] [REV]')),
1990 _('hg update [-b TAG] [-m] [-C] [REV]')),
1991 "verify": (verify, [], _('hg verify')),
1991 "verify": (verify, [], _('hg verify')),
1992 "version": (show_version, [], _('hg version')),
1992 "version": (show_version, [], _('hg version')),
1993 }
1993 }
1994
1994
1995 globalopts = [
1995 globalopts = [
1996 ('R', 'repository', "", _('repository root directory')),
1996 ('R', 'repository', "", _('repository root directory')),
1997 ('', 'cwd', '', _('change working directory')),
1997 ('', 'cwd', '', _('change working directory')),
1998 ('y', 'noninteractive', None, _('run non-interactively')),
1998 ('y', 'noninteractive', None, _('run non-interactively')),
1999 ('q', 'quiet', None, _('quiet mode')),
1999 ('q', 'quiet', None, _('quiet mode')),
2000 ('v', 'verbose', None, _('verbose mode')),
2000 ('v', 'verbose', None, _('verbose mode')),
2001 ('', 'debug', None, _('debug mode')),
2001 ('', 'debug', None, _('debug mode')),
2002 ('', 'debugger', None, _('start debugger')),
2002 ('', 'debugger', None, _('start debugger')),
2003 ('', 'traceback', None, _('print traceback on exception')),
2003 ('', 'traceback', None, _('print traceback on exception')),
2004 ('', 'time', None, _('time how long the command takes')),
2004 ('', 'time', None, _('time how long the command takes')),
2005 ('', 'profile', None, _('profile')),
2005 ('', 'profile', None, _('profile')),
2006 ('', 'version', None, _('output version information and exit')),
2006 ('', 'version', None, _('output version information and exit')),
2007 ('h', 'help', None, _('display help and exit')),
2007 ('h', 'help', None, _('display help and exit')),
2008 ]
2008 ]
2009
2009
2010 norepo = ("clone init version help debugancestor debugconfig debugdata"
2010 norepo = ("clone init version help debugancestor debugconfig debugdata"
2011 " debugindex debugindexdot paths")
2011 " debugindex debugindexdot paths")
2012
2012
2013 def find(cmd):
2013 def find(cmd):
2014 for e in table.keys():
2014 for e in table.keys():
2015 if re.match("(%s)$" % e, cmd):
2015 if re.match("(%s)$" % e, cmd):
2016 return e, table[e]
2016 return e, table[e]
2017
2017
2018 raise UnknownCommand(cmd)
2018 raise UnknownCommand(cmd)
2019
2019
2020 class SignalInterrupt(Exception):
2020 class SignalInterrupt(Exception):
2021 """Exception raised on SIGTERM and SIGHUP."""
2021 """Exception raised on SIGTERM and SIGHUP."""
2022
2022
2023 def catchterm(*args):
2023 def catchterm(*args):
2024 raise SignalInterrupt
2024 raise SignalInterrupt
2025
2025
2026 def run():
2026 def run():
2027 sys.exit(dispatch(sys.argv[1:]))
2027 sys.exit(dispatch(sys.argv[1:]))
2028
2028
2029 class ParseError(Exception):
2029 class ParseError(Exception):
2030 """Exception raised on errors in parsing the command line."""
2030 """Exception raised on errors in parsing the command line."""
2031
2031
2032 def parse(args):
2032 def parse(args):
2033 options = {}
2033 options = {}
2034 cmdoptions = {}
2034 cmdoptions = {}
2035
2035
2036 try:
2036 try:
2037 args = fancyopts.fancyopts(args, globalopts, options)
2037 args = fancyopts.fancyopts(args, globalopts, options)
2038 except fancyopts.getopt.GetoptError, inst:
2038 except fancyopts.getopt.GetoptError, inst:
2039 raise ParseError(None, inst)
2039 raise ParseError(None, inst)
2040
2040
2041 if args:
2041 if args:
2042 cmd, args = args[0], args[1:]
2042 cmd, args = args[0], args[1:]
2043 i = find(cmd)[1]
2043 i = find(cmd)[1]
2044 c = list(i[1])
2044 c = list(i[1])
2045 else:
2045 else:
2046 cmd = None
2046 cmd = None
2047 c = []
2047 c = []
2048
2048
2049 # combine global options into local
2049 # combine global options into local
2050 for o in globalopts:
2050 for o in globalopts:
2051 c.append((o[0], o[1], options[o[1]], o[3]))
2051 c.append((o[0], o[1], options[o[1]], o[3]))
2052
2052
2053 try:
2053 try:
2054 args = fancyopts.fancyopts(args, c, cmdoptions)
2054 args = fancyopts.fancyopts(args, c, cmdoptions)
2055 except fancyopts.getopt.GetoptError, inst:
2055 except fancyopts.getopt.GetoptError, inst:
2056 raise ParseError(cmd, inst)
2056 raise ParseError(cmd, inst)
2057
2057
2058 # separate global options back out
2058 # separate global options back out
2059 for o in globalopts:
2059 for o in globalopts:
2060 n = o[1]
2060 n = o[1]
2061 options[n] = cmdoptions[n]
2061 options[n] = cmdoptions[n]
2062 del cmdoptions[n]
2062 del cmdoptions[n]
2063
2063
2064 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2064 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2065
2065
2066 def dispatch(args):
2066 def dispatch(args):
2067 signal.signal(signal.SIGTERM, catchterm)
2067 signal.signal(signal.SIGTERM, catchterm)
2068 try:
2068 try:
2069 signal.signal(signal.SIGHUP, catchterm)
2069 signal.signal(signal.SIGHUP, catchterm)
2070 except AttributeError:
2070 except AttributeError:
2071 pass
2071 pass
2072
2072
2073 u = ui.ui()
2073 u = ui.ui()
2074 external = []
2074 external = []
2075 for x in u.extensions():
2075 for x in u.extensions():
2076 if x[1]:
2076 if x[1]:
2077 try:
2077 try:
2078 mod = imp.load_source(x[0], x[1])
2078 mod = imp.load_source(x[0], x[1])
2079 except:
2079 except:
2080 u.warn(_("*** failed to import extension %s\n") % x[1])
2080 u.warn(_("*** failed to import extension %s\n") % x[1])
2081 continue
2081 continue
2082 else:
2082 else:
2083 def importh(name):
2083 def importh(name):
2084 mod = __import__(name)
2084 mod = __import__(name)
2085 components = name.split('.')
2085 components = name.split('.')
2086 for comp in components[1:]:
2086 for comp in components[1:]:
2087 mod = getattr(mod, comp)
2087 mod = getattr(mod, comp)
2088 return mod
2088 return mod
2089 try:
2089 try:
2090 mod = importh(x[0])
2090 mod = importh(x[0])
2091 except:
2091 except:
2092 u.warn(_("failed to import extension %s\n") % x[0])
2092 u.warn(_("failed to import extension %s\n") % x[0])
2093 continue
2093 continue
2094
2094
2095 external.append(mod)
2095 external.append(mod)
2096 for x in external:
2096 for x in external:
2097 cmdtable = getattr(x, 'cmdtable', {})
2097 cmdtable = getattr(x, 'cmdtable', {})
2098 for t in cmdtable:
2098 for t in cmdtable:
2099 if t in table:
2099 if t in table:
2100 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2100 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2101 table.update(cmdtable)
2101 table.update(cmdtable)
2102
2102
2103 try:
2103 try:
2104 cmd, func, args, options, cmdoptions = parse(args)
2104 cmd, func, args, options, cmdoptions = parse(args)
2105 except ParseError, inst:
2105 except ParseError, inst:
2106 if inst.args[0]:
2106 if inst.args[0]:
2107 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2107 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2108 help_(u, inst.args[0])
2108 help_(u, inst.args[0])
2109 else:
2109 else:
2110 u.warn(_("hg: %s\n") % inst.args[1])
2110 u.warn(_("hg: %s\n") % inst.args[1])
2111 help_(u, 'shortlist')
2111 help_(u, 'shortlist')
2112 sys.exit(-1)
2112 sys.exit(-1)
2113 except UnknownCommand, inst:
2113 except UnknownCommand, inst:
2114 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2114 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2115 help_(u, 'shortlist')
2115 help_(u, 'shortlist')
2116 sys.exit(1)
2116 sys.exit(1)
2117
2117
2118 if options["time"]:
2118 if options["time"]:
2119 def get_times():
2119 def get_times():
2120 t = os.times()
2120 t = os.times()
2121 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2121 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2122 t = (t[0], t[1], t[2], t[3], time.clock())
2122 t = (t[0], t[1], t[2], t[3], time.clock())
2123 return t
2123 return t
2124 s = get_times()
2124 s = get_times()
2125 def print_time():
2125 def print_time():
2126 t = get_times()
2126 t = get_times()
2127 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2127 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2128 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2128 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2129 atexit.register(print_time)
2129 atexit.register(print_time)
2130
2130
2131 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2131 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2132 not options["noninteractive"])
2132 not options["noninteractive"])
2133
2133
2134 # enter the debugger before command execution
2134 # enter the debugger before command execution
2135 if options['debugger']:
2135 if options['debugger']:
2136 pdb.set_trace()
2136 pdb.set_trace()
2137
2137
2138 try:
2138 try:
2139 try:
2139 try:
2140 if options['help']:
2140 if options['help']:
2141 help_(u, cmd, options['version'])
2141 help_(u, cmd, options['version'])
2142 sys.exit(0)
2142 sys.exit(0)
2143 elif options['version']:
2143 elif options['version']:
2144 show_version(u)
2144 show_version(u)
2145 sys.exit(0)
2145 sys.exit(0)
2146 elif not cmd:
2146 elif not cmd:
2147 help_(u, 'shortlist')
2147 help_(u, 'shortlist')
2148 sys.exit(0)
2148 sys.exit(0)
2149
2149
2150 if options['cwd']:
2150 if options['cwd']:
2151 try:
2151 try:
2152 os.chdir(options['cwd'])
2152 os.chdir(options['cwd'])
2153 except OSError, inst:
2153 except OSError, inst:
2154 raise util.Abort('%s: %s' %
2154 raise util.Abort('%s: %s' %
2155 (options['cwd'], inst.strerror))
2155 (options['cwd'], inst.strerror))
2156
2156
2157 if cmd not in norepo.split():
2157 if cmd not in norepo.split():
2158 path = options["repository"] or ""
2158 path = options["repository"] or ""
2159 repo = hg.repository(ui=u, path=path)
2159 repo = hg.repository(ui=u, path=path)
2160 for x in external:
2160 for x in external:
2161 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2161 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2162 d = lambda: func(u, repo, *args, **cmdoptions)
2162 d = lambda: func(u, repo, *args, **cmdoptions)
2163 else:
2163 else:
2164 d = lambda: func(u, *args, **cmdoptions)
2164 d = lambda: func(u, *args, **cmdoptions)
2165
2165
2166 if options['profile']:
2166 if options['profile']:
2167 import hotshot, hotshot.stats
2167 import hotshot, hotshot.stats
2168 prof = hotshot.Profile("hg.prof")
2168 prof = hotshot.Profile("hg.prof")
2169 r = prof.runcall(d)
2169 r = prof.runcall(d)
2170 prof.close()
2170 prof.close()
2171 stats = hotshot.stats.load("hg.prof")
2171 stats = hotshot.stats.load("hg.prof")
2172 stats.strip_dirs()
2172 stats.strip_dirs()
2173 stats.sort_stats('time', 'calls')
2173 stats.sort_stats('time', 'calls')
2174 stats.print_stats(40)
2174 stats.print_stats(40)
2175 return r
2175 return r
2176 else:
2176 else:
2177 return d()
2177 return d()
2178 except:
2178 except:
2179 # enter the debugger when we hit an exception
2179 # enter the debugger when we hit an exception
2180 if options['debugger']:
2180 if options['debugger']:
2181 pdb.post_mortem(sys.exc_info()[2])
2181 pdb.post_mortem(sys.exc_info()[2])
2182 if options['traceback']:
2182 if options['traceback']:
2183 traceback.print_exc()
2183 traceback.print_exc()
2184 raise
2184 raise
2185 except hg.RepoError, inst:
2185 except hg.RepoError, inst:
2186 u.warn(_("abort: "), inst, "!\n")
2186 u.warn(_("abort: "), inst, "!\n")
2187 except revlog.RevlogError, inst:
2187 except revlog.RevlogError, inst:
2188 u.warn(_("abort: "), inst, "!\n")
2188 u.warn(_("abort: "), inst, "!\n")
2189 except SignalInterrupt:
2189 except SignalInterrupt:
2190 u.warn(_("killed!\n"))
2190 u.warn(_("killed!\n"))
2191 except KeyboardInterrupt:
2191 except KeyboardInterrupt:
2192 try:
2192 try:
2193 u.warn(_("interrupted!\n"))
2193 u.warn(_("interrupted!\n"))
2194 except IOError, inst:
2194 except IOError, inst:
2195 if inst.errno == errno.EPIPE:
2195 if inst.errno == errno.EPIPE:
2196 if u.debugflag:
2196 if u.debugflag:
2197 u.warn(_("\nbroken pipe\n"))
2197 u.warn(_("\nbroken pipe\n"))
2198 else:
2198 else:
2199 raise
2199 raise
2200 except IOError, inst:
2200 except IOError, inst:
2201 if hasattr(inst, "code"):
2201 if hasattr(inst, "code"):
2202 u.warn(_("abort: %s\n") % inst)
2202 u.warn(_("abort: %s\n") % inst)
2203 elif hasattr(inst, "reason"):
2203 elif hasattr(inst, "reason"):
2204 u.warn(_("abort: error: %s\n") % inst.reason[1])
2204 u.warn(_("abort: error: %s\n") % inst.reason[1])
2205 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2205 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2206 if u.debugflag:
2206 if u.debugflag:
2207 u.warn(_("broken pipe\n"))
2207 u.warn(_("broken pipe\n"))
2208 elif getattr(inst, "strerror", None):
2208 elif getattr(inst, "strerror", None):
2209 if getattr(inst, "filename", None):
2209 if getattr(inst, "filename", None):
2210 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2210 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2211 else:
2211 else:
2212 u.warn(_("abort: %s\n") % inst.strerror)
2212 u.warn(_("abort: %s\n") % inst.strerror)
2213 else:
2213 else:
2214 raise
2214 raise
2215 except OSError, inst:
2215 except OSError, inst:
2216 if hasattr(inst, "filename"):
2216 if hasattr(inst, "filename"):
2217 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2217 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2218 else:
2218 else:
2219 u.warn(_("abort: %s\n") % inst.strerror)
2219 u.warn(_("abort: %s\n") % inst.strerror)
2220 except util.Abort, inst:
2220 except util.Abort, inst:
2221 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2221 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2222 sys.exit(1)
2222 sys.exit(1)
2223 except TypeError, inst:
2223 except TypeError, inst:
2224 # was this an argument error?
2224 # was this an argument error?
2225 tb = traceback.extract_tb(sys.exc_info()[2])
2225 tb = traceback.extract_tb(sys.exc_info()[2])
2226 if len(tb) > 2: # no
2226 if len(tb) > 2: # no
2227 raise
2227 raise
2228 u.debug(inst, "\n")
2228 u.debug(inst, "\n")
2229 u.warn(_("%s: invalid arguments\n") % cmd)
2229 u.warn(_("%s: invalid arguments\n") % cmd)
2230 help_(u, cmd)
2230 help_(u, cmd)
2231 except UnknownCommand, inst:
2231 except UnknownCommand, inst:
2232 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2232 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2233 help_(u, 'shortlist')
2233 help_(u, 'shortlist')
2234 except SystemExit:
2234 except SystemExit:
2235 # don't catch this in the catch-all below
2235 # don't catch this in the catch-all below
2236 raise
2236 raise
2237 except:
2237 except:
2238 u.warn(_("** unknown exception encountered, details follow\n"))
2238 u.warn(_("** unknown exception encountered, details follow\n"))
2239 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2239 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2240 raise
2240 raise
2241
2241
2242 sys.exit(-1)
2242 sys.exit(-1)
@@ -1,576 +1,589
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8
8
9 This contains helper routines that are independent of the SCM core and hide
9 This contains helper routines that are independent of the SCM core and hide
10 platform-specific details from the core.
10 platform-specific details from the core.
11 """
11 """
12
12
13 import os, errno
13 import os, errno
14 from i18n import gettext as _
14 from i18n import gettext as _
15 from demandload import *
15 from demandload import *
16 demandload(globals(), "re cStringIO shutil popen2 tempfile threading time")
16 demandload(globals(), "re cStringIO shutil popen2 tempfile threading time")
17
17
18 def pipefilter(s, cmd):
18 def pipefilter(s, cmd):
19 '''filter string S through command CMD, returning its output'''
19 '''filter string S through command CMD, returning its output'''
20 (pout, pin) = popen2.popen2(cmd, -1, 'b')
20 (pout, pin) = popen2.popen2(cmd, -1, 'b')
21 def writer():
21 def writer():
22 pin.write(s)
22 pin.write(s)
23 pin.close()
23 pin.close()
24
24
25 # we should use select instead on UNIX, but this will work on most
25 # we should use select instead on UNIX, but this will work on most
26 # systems, including Windows
26 # systems, including Windows
27 w = threading.Thread(target=writer)
27 w = threading.Thread(target=writer)
28 w.start()
28 w.start()
29 f = pout.read()
29 f = pout.read()
30 pout.close()
30 pout.close()
31 w.join()
31 w.join()
32 return f
32 return f
33
33
34 def tempfilter(s, cmd):
34 def tempfilter(s, cmd):
35 '''filter string S through a pair of temporary files with CMD.
35 '''filter string S through a pair of temporary files with CMD.
36 CMD is used as a template to create the real command to be run,
36 CMD is used as a template to create the real command to be run,
37 with the strings INFILE and OUTFILE replaced by the real names of
37 with the strings INFILE and OUTFILE replaced by the real names of
38 the temporary files generated.'''
38 the temporary files generated.'''
39 inname, outname = None, None
39 inname, outname = None, None
40 try:
40 try:
41 infd, inname = tempfile.mkstemp(prefix='hgfin')
41 infd, inname = tempfile.mkstemp(prefix='hgfin')
42 fp = os.fdopen(infd, 'wb')
42 fp = os.fdopen(infd, 'wb')
43 fp.write(s)
43 fp.write(s)
44 fp.close()
44 fp.close()
45 outfd, outname = tempfile.mkstemp(prefix='hgfout')
45 outfd, outname = tempfile.mkstemp(prefix='hgfout')
46 os.close(outfd)
46 os.close(outfd)
47 cmd = cmd.replace('INFILE', inname)
47 cmd = cmd.replace('INFILE', inname)
48 cmd = cmd.replace('OUTFILE', outname)
48 cmd = cmd.replace('OUTFILE', outname)
49 code = os.system(cmd)
49 code = os.system(cmd)
50 if code: raise Abort(_("command '%s' failed: %s") %
50 if code: raise Abort(_("command '%s' failed: %s") %
51 (cmd, explain_exit(code)))
51 (cmd, explain_exit(code)))
52 return open(outname, 'rb').read()
52 return open(outname, 'rb').read()
53 finally:
53 finally:
54 try:
54 try:
55 if inname: os.unlink(inname)
55 if inname: os.unlink(inname)
56 except: pass
56 except: pass
57 try:
57 try:
58 if outname: os.unlink(outname)
58 if outname: os.unlink(outname)
59 except: pass
59 except: pass
60
60
61 filtertable = {
61 filtertable = {
62 'tempfile:': tempfilter,
62 'tempfile:': tempfilter,
63 'pipe:': pipefilter,
63 'pipe:': pipefilter,
64 }
64 }
65
65
66 def filter(s, cmd):
66 def filter(s, cmd):
67 "filter a string through a command that transforms its input to its output"
67 "filter a string through a command that transforms its input to its output"
68 for name, fn in filtertable.iteritems():
68 for name, fn in filtertable.iteritems():
69 if cmd.startswith(name):
69 if cmd.startswith(name):
70 return fn(s, cmd[len(name):].lstrip())
70 return fn(s, cmd[len(name):].lstrip())
71 return pipefilter(s, cmd)
71 return pipefilter(s, cmd)
72
72
73 def patch(strip, patchname, ui):
73 def patch(strip, patchname, ui):
74 """apply the patch <patchname> to the working directory.
74 """apply the patch <patchname> to the working directory.
75 a list of patched files is returned"""
75 a list of patched files is returned"""
76 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
76 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
77 files = {}
77 files = {}
78 for line in fp:
78 for line in fp:
79 line = line.rstrip()
79 line = line.rstrip()
80 ui.status("%s\n" % line)
80 ui.status("%s\n" % line)
81 if line.startswith('patching file '):
81 if line.startswith('patching file '):
82 pf = parse_patch_output(line)
82 pf = parse_patch_output(line)
83 files.setdefault(pf, 1)
83 files.setdefault(pf, 1)
84 code = fp.close()
84 code = fp.close()
85 if code:
85 if code:
86 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
86 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
87 return files.keys()
87 return files.keys()
88
88
89 def binary(s):
89 def binary(s):
90 """return true if a string is binary data using diff's heuristic"""
90 """return true if a string is binary data using diff's heuristic"""
91 if s and '\0' in s[:4096]:
91 if s and '\0' in s[:4096]:
92 return True
92 return True
93 return False
93 return False
94
94
95 def unique(g):
95 def unique(g):
96 """return the uniq elements of iterable g"""
96 """return the uniq elements of iterable g"""
97 seen = {}
97 seen = {}
98 for f in g:
98 for f in g:
99 if f not in seen:
99 if f not in seen:
100 seen[f] = 1
100 seen[f] = 1
101 yield f
101 yield f
102
102
103 class Abort(Exception):
103 class Abort(Exception):
104 """Raised if a command needs to print an error and exit."""
104 """Raised if a command needs to print an error and exit."""
105
105
106 def always(fn): return True
106 def always(fn): return True
107 def never(fn): return False
107 def never(fn): return False
108
108
109 def globre(pat, head='^', tail='$'):
109 def globre(pat, head='^', tail='$'):
110 "convert a glob pattern into a regexp"
110 "convert a glob pattern into a regexp"
111 i, n = 0, len(pat)
111 i, n = 0, len(pat)
112 res = ''
112 res = ''
113 group = False
113 group = False
114 def peek(): return i < n and pat[i]
114 def peek(): return i < n and pat[i]
115 while i < n:
115 while i < n:
116 c = pat[i]
116 c = pat[i]
117 i = i+1
117 i = i+1
118 if c == '*':
118 if c == '*':
119 if peek() == '*':
119 if peek() == '*':
120 i += 1
120 i += 1
121 res += '.*'
121 res += '.*'
122 else:
122 else:
123 res += '[^/]*'
123 res += '[^/]*'
124 elif c == '?':
124 elif c == '?':
125 res += '.'
125 res += '.'
126 elif c == '[':
126 elif c == '[':
127 j = i
127 j = i
128 if j < n and pat[j] in '!]':
128 if j < n and pat[j] in '!]':
129 j += 1
129 j += 1
130 while j < n and pat[j] != ']':
130 while j < n and pat[j] != ']':
131 j += 1
131 j += 1
132 if j >= n:
132 if j >= n:
133 res += '\\['
133 res += '\\['
134 else:
134 else:
135 stuff = pat[i:j].replace('\\','\\\\')
135 stuff = pat[i:j].replace('\\','\\\\')
136 i = j + 1
136 i = j + 1
137 if stuff[0] == '!':
137 if stuff[0] == '!':
138 stuff = '^' + stuff[1:]
138 stuff = '^' + stuff[1:]
139 elif stuff[0] == '^':
139 elif stuff[0] == '^':
140 stuff = '\\' + stuff
140 stuff = '\\' + stuff
141 res = '%s[%s]' % (res, stuff)
141 res = '%s[%s]' % (res, stuff)
142 elif c == '{':
142 elif c == '{':
143 group = True
143 group = True
144 res += '(?:'
144 res += '(?:'
145 elif c == '}' and group:
145 elif c == '}' and group:
146 res += ')'
146 res += ')'
147 group = False
147 group = False
148 elif c == ',' and group:
148 elif c == ',' and group:
149 res += '|'
149 res += '|'
150 else:
150 else:
151 res += re.escape(c)
151 res += re.escape(c)
152 return head + res + tail
152 return head + res + tail
153
153
154 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
154 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
155
155
156 def pathto(n1, n2):
156 def pathto(n1, n2):
157 '''return the relative path from one place to another.
157 '''return the relative path from one place to another.
158 this returns a path in the form used by the local filesystem, not hg.'''
158 this returns a path in the form used by the local filesystem, not hg.'''
159 if not n1: return localpath(n2)
159 if not n1: return localpath(n2)
160 a, b = n1.split('/'), n2.split('/')
160 a, b = n1.split('/'), n2.split('/')
161 a.reverse(), b.reverse()
161 a.reverse(), b.reverse()
162 while a and b and a[-1] == b[-1]:
162 while a and b and a[-1] == b[-1]:
163 a.pop(), b.pop()
163 a.pop(), b.pop()
164 b.reverse()
164 b.reverse()
165 return os.sep.join((['..'] * len(a)) + b)
165 return os.sep.join((['..'] * len(a)) + b)
166
166
167 def canonpath(root, cwd, myname):
167 def canonpath(root, cwd, myname):
168 """return the canonical path of myname, given cwd and root"""
168 """return the canonical path of myname, given cwd and root"""
169 rootsep = root + os.sep
169 rootsep = root + os.sep
170 name = myname
170 name = myname
171 if not name.startswith(os.sep):
171 if not name.startswith(os.sep):
172 name = os.path.join(root, cwd, name)
172 name = os.path.join(root, cwd, name)
173 name = os.path.normpath(name)
173 name = os.path.normpath(name)
174 if name.startswith(rootsep):
174 if name.startswith(rootsep):
175 return pconvert(name[len(rootsep):])
175 return pconvert(name[len(rootsep):])
176 elif name == root:
176 elif name == root:
177 return ''
177 return ''
178 else:
178 else:
179 raise Abort('%s not under root' % myname)
179 raise Abort('%s not under root' % myname)
180
180
181 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
181 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
182 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob')
183
184 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
185 if os.name == 'nt':
186 dflt_pat = 'glob'
187 else:
188 dflt_pat = 'relpath'
189 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat)
190
191 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat):
182 """build a function to match a set of file patterns
192 """build a function to match a set of file patterns
183
193
184 arguments:
194 arguments:
185 canonroot - the canonical root of the tree you're matching against
195 canonroot - the canonical root of the tree you're matching against
186 cwd - the current working directory, if relevant
196 cwd - the current working directory, if relevant
187 names - patterns to find
197 names - patterns to find
188 inc - patterns to include
198 inc - patterns to include
189 exc - patterns to exclude
199 exc - patterns to exclude
190 head - a regex to prepend to patterns to control whether a match is rooted
200 head - a regex to prepend to patterns to control whether a match is rooted
191
201
192 a pattern is one of:
202 a pattern is one of:
193 'glob:<rooted glob>'
203 'glob:<rooted glob>'
194 're:<rooted regexp>'
204 're:<rooted regexp>'
195 'path:<rooted path>'
205 'path:<rooted path>'
196 'relglob:<relative glob>'
206 'relglob:<relative glob>'
197 'relpath:<relative path>'
207 'relpath:<relative path>'
198 'relre:<relative regexp>'
208 'relre:<relative regexp>'
199 '<rooted path or regexp>'
209 '<rooted path or regexp>'
200
210
201 returns:
211 returns:
202 a 3-tuple containing
212 a 3-tuple containing
203 - list of explicit non-pattern names passed in
213 - list of explicit non-pattern names passed in
204 - a bool match(filename) function
214 - a bool match(filename) function
205 - a bool indicating if any patterns were passed in
215 - a bool indicating if any patterns were passed in
206
216
207 todo:
217 todo:
208 make head regex a rooted bool
218 make head regex a rooted bool
209 """
219 """
210
220
211 def patkind(name):
221 def patkind(name, dflt_pat='glob'):
212 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
222 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
213 if name.startswith(prefix + ':'): return name.split(':', 1)
223 if name.startswith(prefix + ':'): return name.split(':', 1)
224 return dflt_pat, name
225
226 def contains_glob(name):
214 for c in name:
227 for c in name:
215 if c in _globchars: return 'glob', name
228 if c in _globchars: return True
216 return 'relpath', name
229 return False
217
230
218 def regex(kind, name, tail):
231 def regex(kind, name, tail):
219 '''convert a pattern into a regular expression'''
232 '''convert a pattern into a regular expression'''
220 if kind == 're':
233 if kind == 're':
221 return name
234 return name
222 elif kind == 'path':
235 elif kind == 'path':
223 return '^' + re.escape(name) + '(?:/|$)'
236 return '^' + re.escape(name) + '(?:/|$)'
224 elif kind == 'relglob':
237 elif kind == 'relglob':
225 return head + globre(name, '(?:|.*/)', tail)
238 return head + globre(name, '(?:|.*/)', tail)
226 elif kind == 'relpath':
239 elif kind == 'relpath':
227 return head + re.escape(name) + tail
240 return head + re.escape(name) + tail
228 elif kind == 'relre':
241 elif kind == 'relre':
229 if name.startswith('^'):
242 if name.startswith('^'):
230 return name
243 return name
231 return '.*' + name
244 return '.*' + name
232 return head + globre(name, '', tail)
245 return head + globre(name, '', tail)
233
246
234 def matchfn(pats, tail):
247 def matchfn(pats, tail):
235 """build a matching function from a set of patterns"""
248 """build a matching function from a set of patterns"""
236 if pats:
249 if pats:
237 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
250 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
238 return re.compile(pat).match
251 return re.compile(pat).match
239
252
240 def globprefix(pat):
253 def globprefix(pat):
241 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
254 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
242 root = []
255 root = []
243 for p in pat.split(os.sep):
256 for p in pat.split(os.sep):
244 if patkind(p)[0] == 'glob': break
257 if contains_glob(p): break
245 root.append(p)
258 root.append(p)
246 return '/'.join(root)
259 return '/'.join(root)
247
260
248 pats = []
261 pats = []
249 files = []
262 files = []
250 roots = []
263 roots = []
251 for kind, name in map(patkind, names):
264 for kind, name in [patkind(p, dflt_pat) for p in names]:
252 if kind in ('glob', 'relpath'):
265 if kind in ('glob', 'relpath'):
253 name = canonpath(canonroot, cwd, name)
266 name = canonpath(canonroot, cwd, name)
254 if name == '':
267 if name == '':
255 kind, name = 'glob', '**'
268 kind, name = 'glob', '**'
256 if kind in ('glob', 'path', 're'):
269 if kind in ('glob', 'path', 're'):
257 pats.append((kind, name))
270 pats.append((kind, name))
258 if kind == 'glob':
271 if kind == 'glob':
259 root = globprefix(name)
272 root = globprefix(name)
260 if root: roots.append(root)
273 if root: roots.append(root)
261 elif kind == 'relpath':
274 elif kind == 'relpath':
262 files.append((kind, name))
275 files.append((kind, name))
263 roots.append(name)
276 roots.append(name)
264
277
265 patmatch = matchfn(pats, '$') or always
278 patmatch = matchfn(pats, '$') or always
266 filematch = matchfn(files, '(?:/|$)') or always
279 filematch = matchfn(files, '(?:/|$)') or always
267 incmatch = always
280 incmatch = always
268 if inc:
281 if inc:
269 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
282 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
270 excmatch = lambda fn: False
283 excmatch = lambda fn: False
271 if exc:
284 if exc:
272 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
285 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
273
286
274 return (roots,
287 return (roots,
275 lambda fn: (incmatch(fn) and not excmatch(fn) and
288 lambda fn: (incmatch(fn) and not excmatch(fn) and
276 (fn.endswith('/') or
289 (fn.endswith('/') or
277 (not pats and not files) or
290 (not pats and not files) or
278 (pats and patmatch(fn)) or
291 (pats and patmatch(fn)) or
279 (files and filematch(fn)))),
292 (files and filematch(fn)))),
280 (inc or exc or (pats and pats != [('glob', '**')])) and True)
293 (inc or exc or (pats and pats != [('glob', '**')])) and True)
281
294
282 def system(cmd, errprefix=None):
295 def system(cmd, errprefix=None):
283 """execute a shell command that must succeed"""
296 """execute a shell command that must succeed"""
284 rc = os.system(cmd)
297 rc = os.system(cmd)
285 if rc:
298 if rc:
286 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
299 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
287 explain_exit(rc)[0])
300 explain_exit(rc)[0])
288 if errprefix:
301 if errprefix:
289 errmsg = "%s: %s" % (errprefix, errmsg)
302 errmsg = "%s: %s" % (errprefix, errmsg)
290 raise Abort(errmsg)
303 raise Abort(errmsg)
291
304
292 def rename(src, dst):
305 def rename(src, dst):
293 """forcibly rename a file"""
306 """forcibly rename a file"""
294 try:
307 try:
295 os.rename(src, dst)
308 os.rename(src, dst)
296 except:
309 except:
297 os.unlink(dst)
310 os.unlink(dst)
298 os.rename(src, dst)
311 os.rename(src, dst)
299
312
300 def copyfiles(src, dst, hardlink=None):
313 def copyfiles(src, dst, hardlink=None):
301 """Copy a directory tree using hardlinks if possible"""
314 """Copy a directory tree using hardlinks if possible"""
302
315
303 if hardlink is None:
316 if hardlink is None:
304 hardlink = (os.stat(src).st_dev ==
317 hardlink = (os.stat(src).st_dev ==
305 os.stat(os.path.dirname(dst)).st_dev)
318 os.stat(os.path.dirname(dst)).st_dev)
306
319
307 if os.path.isdir(src):
320 if os.path.isdir(src):
308 os.mkdir(dst)
321 os.mkdir(dst)
309 for name in os.listdir(src):
322 for name in os.listdir(src):
310 srcname = os.path.join(src, name)
323 srcname = os.path.join(src, name)
311 dstname = os.path.join(dst, name)
324 dstname = os.path.join(dst, name)
312 copyfiles(srcname, dstname, hardlink)
325 copyfiles(srcname, dstname, hardlink)
313 else:
326 else:
314 if hardlink:
327 if hardlink:
315 try:
328 try:
316 os_link(src, dst)
329 os_link(src, dst)
317 except:
330 except:
318 hardlink = False
331 hardlink = False
319 shutil.copy2(src, dst)
332 shutil.copy2(src, dst)
320 else:
333 else:
321 shutil.copy2(src, dst)
334 shutil.copy2(src, dst)
322
335
323 def opener(base):
336 def opener(base):
324 """
337 """
325 return a function that opens files relative to base
338 return a function that opens files relative to base
326
339
327 this function is used to hide the details of COW semantics and
340 this function is used to hide the details of COW semantics and
328 remote file access from higher level code.
341 remote file access from higher level code.
329 """
342 """
330 p = base
343 p = base
331 def o(path, mode="r", text=False):
344 def o(path, mode="r", text=False):
332 f = os.path.join(p, path)
345 f = os.path.join(p, path)
333
346
334 if not text:
347 if not text:
335 mode += "b" # for that other OS
348 mode += "b" # for that other OS
336
349
337 if mode[0] != "r":
350 if mode[0] != "r":
338 try:
351 try:
339 nlink = nlinks(f)
352 nlink = nlinks(f)
340 except OSError:
353 except OSError:
341 d = os.path.dirname(f)
354 d = os.path.dirname(f)
342 if not os.path.isdir(d):
355 if not os.path.isdir(d):
343 os.makedirs(d)
356 os.makedirs(d)
344 else:
357 else:
345 if nlink > 1:
358 if nlink > 1:
346 file(f + ".tmp", "wb").write(file(f, "rb").read())
359 file(f + ".tmp", "wb").write(file(f, "rb").read())
347 rename(f+".tmp", f)
360 rename(f+".tmp", f)
348
361
349 return file(f, mode)
362 return file(f, mode)
350
363
351 return o
364 return o
352
365
353 def _makelock_file(info, pathname):
366 def _makelock_file(info, pathname):
354 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
367 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
355 os.write(ld, info)
368 os.write(ld, info)
356 os.close(ld)
369 os.close(ld)
357
370
358 def _readlock_file(pathname):
371 def _readlock_file(pathname):
359 return file(pathname).read()
372 return file(pathname).read()
360
373
361 def nlinks(pathname):
374 def nlinks(pathname):
362 """Return number of hardlinks for the given file."""
375 """Return number of hardlinks for the given file."""
363 return os.stat(pathname).st_nlink
376 return os.stat(pathname).st_nlink
364
377
365 if hasattr(os, 'link'):
378 if hasattr(os, 'link'):
366 os_link = os.link
379 os_link = os.link
367 else:
380 else:
368 def os_link(src, dst):
381 def os_link(src, dst):
369 raise OSError(0, _("Hardlinks not supported"))
382 raise OSError(0, _("Hardlinks not supported"))
370
383
371 # Platform specific variants
384 # Platform specific variants
372 if os.name == 'nt':
385 if os.name == 'nt':
373 nulldev = 'NUL:'
386 nulldev = 'NUL:'
374
387
375 try:
388 try:
376 import win32api, win32process
389 import win32api, win32process
377 filename = win32process.GetModuleFileNameEx(win32api.GetCurrentProcess(), 0)
390 filename = win32process.GetModuleFileNameEx(win32api.GetCurrentProcess(), 0)
378 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
391 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
379
392
380 except ImportError:
393 except ImportError:
381 systemrc = r'c:\mercurial\mercurial.ini'
394 systemrc = r'c:\mercurial\mercurial.ini'
382 pass
395 pass
383
396
384 rcpath = (systemrc,
397 rcpath = (systemrc,
385 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
398 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
386
399
387 def parse_patch_output(output_line):
400 def parse_patch_output(output_line):
388 """parses the output produced by patch and returns the file name"""
401 """parses the output produced by patch and returns the file name"""
389 pf = output_line[14:]
402 pf = output_line[14:]
390 if pf[0] == '`':
403 if pf[0] == '`':
391 pf = pf[1:-1] # Remove the quotes
404 pf = pf[1:-1] # Remove the quotes
392 return pf
405 return pf
393
406
394 try: # ActivePython can create hard links using win32file module
407 try: # ActivePython can create hard links using win32file module
395 import win32file
408 import win32file
396
409
397 def os_link(src, dst): # NB will only succeed on NTFS
410 def os_link(src, dst): # NB will only succeed on NTFS
398 win32file.CreateHardLink(dst, src)
411 win32file.CreateHardLink(dst, src)
399
412
400 def nlinks(pathname):
413 def nlinks(pathname):
401 """Return number of hardlinks for the given file."""
414 """Return number of hardlinks for the given file."""
402 try:
415 try:
403 fh = win32file.CreateFile(pathname,
416 fh = win32file.CreateFile(pathname,
404 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
417 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
405 None, win32file.OPEN_EXISTING, 0, None)
418 None, win32file.OPEN_EXISTING, 0, None)
406 res = win32file.GetFileInformationByHandle(fh)
419 res = win32file.GetFileInformationByHandle(fh)
407 fh.Close()
420 fh.Close()
408 return res[7]
421 return res[7]
409 except:
422 except:
410 return os.stat(pathname).st_nlink
423 return os.stat(pathname).st_nlink
411
424
412 except ImportError:
425 except ImportError:
413 pass
426 pass
414
427
415 def is_exec(f, last):
428 def is_exec(f, last):
416 return last
429 return last
417
430
418 def set_exec(f, mode):
431 def set_exec(f, mode):
419 pass
432 pass
420
433
421 def pconvert(path):
434 def pconvert(path):
422 return path.replace("\\", "/")
435 return path.replace("\\", "/")
423
436
424 def localpath(path):
437 def localpath(path):
425 return path.replace('/', '\\')
438 return path.replace('/', '\\')
426
439
427 def normpath(path):
440 def normpath(path):
428 return pconvert(os.path.normpath(path))
441 return pconvert(os.path.normpath(path))
429
442
430 makelock = _makelock_file
443 makelock = _makelock_file
431 readlock = _readlock_file
444 readlock = _readlock_file
432
445
433 def explain_exit(code):
446 def explain_exit(code):
434 return _("exited with status %d") % code, code
447 return _("exited with status %d") % code, code
435
448
436 else:
449 else:
437 nulldev = '/dev/null'
450 nulldev = '/dev/null'
438
451
439 hgrcd = '/etc/mercurial/hgrc.d'
452 hgrcd = '/etc/mercurial/hgrc.d'
440 hgrcs = []
453 hgrcs = []
441 if os.path.isdir(hgrcd):
454 if os.path.isdir(hgrcd):
442 hgrcs = [f for f in os.listdir(hgrcd) if f.endswith(".rc")]
455 hgrcs = [f for f in os.listdir(hgrcd) if f.endswith(".rc")]
443 rcpath = map(os.path.normpath, hgrcs +
456 rcpath = map(os.path.normpath, hgrcs +
444 ['/etc/mercurial/hgrc', os.path.expanduser('~/.hgrc')])
457 ['/etc/mercurial/hgrc', os.path.expanduser('~/.hgrc')])
445
458
446 def parse_patch_output(output_line):
459 def parse_patch_output(output_line):
447 """parses the output produced by patch and returns the file name"""
460 """parses the output produced by patch and returns the file name"""
448 return output_line[14:]
461 return output_line[14:]
449
462
450 def is_exec(f, last):
463 def is_exec(f, last):
451 """check whether a file is executable"""
464 """check whether a file is executable"""
452 return (os.stat(f).st_mode & 0100 != 0)
465 return (os.stat(f).st_mode & 0100 != 0)
453
466
454 def set_exec(f, mode):
467 def set_exec(f, mode):
455 s = os.stat(f).st_mode
468 s = os.stat(f).st_mode
456 if (s & 0100 != 0) == mode:
469 if (s & 0100 != 0) == mode:
457 return
470 return
458 if mode:
471 if mode:
459 # Turn on +x for every +r bit when making a file executable
472 # Turn on +x for every +r bit when making a file executable
460 # and obey umask.
473 # and obey umask.
461 umask = os.umask(0)
474 umask = os.umask(0)
462 os.umask(umask)
475 os.umask(umask)
463 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
476 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
464 else:
477 else:
465 os.chmod(f, s & 0666)
478 os.chmod(f, s & 0666)
466
479
467 def pconvert(path):
480 def pconvert(path):
468 return path
481 return path
469
482
470 def localpath(path):
483 def localpath(path):
471 return path
484 return path
472
485
473 normpath = os.path.normpath
486 normpath = os.path.normpath
474
487
475 def makelock(info, pathname):
488 def makelock(info, pathname):
476 try:
489 try:
477 os.symlink(info, pathname)
490 os.symlink(info, pathname)
478 except OSError, why:
491 except OSError, why:
479 if why.errno == errno.EEXIST:
492 if why.errno == errno.EEXIST:
480 raise
493 raise
481 else:
494 else:
482 _makelock_file(info, pathname)
495 _makelock_file(info, pathname)
483
496
484 def readlock(pathname):
497 def readlock(pathname):
485 try:
498 try:
486 return os.readlink(pathname)
499 return os.readlink(pathname)
487 except OSError, why:
500 except OSError, why:
488 if why.errno == errno.EINVAL:
501 if why.errno == errno.EINVAL:
489 return _readlock_file(pathname)
502 return _readlock_file(pathname)
490 else:
503 else:
491 raise
504 raise
492
505
493 def explain_exit(code):
506 def explain_exit(code):
494 """return a 2-tuple (desc, code) describing a process's status"""
507 """return a 2-tuple (desc, code) describing a process's status"""
495 if os.WIFEXITED(code):
508 if os.WIFEXITED(code):
496 val = os.WEXITSTATUS(code)
509 val = os.WEXITSTATUS(code)
497 return _("exited with status %d") % val, val
510 return _("exited with status %d") % val, val
498 elif os.WIFSIGNALED(code):
511 elif os.WIFSIGNALED(code):
499 val = os.WTERMSIG(code)
512 val = os.WTERMSIG(code)
500 return _("killed by signal %d") % val, val
513 return _("killed by signal %d") % val, val
501 elif os.WIFSTOPPED(code):
514 elif os.WIFSTOPPED(code):
502 val = os.WSTOPSIG(code)
515 val = os.WSTOPSIG(code)
503 return _("stopped by signal %d") % val, val
516 return _("stopped by signal %d") % val, val
504 raise ValueError(_("invalid exit code"))
517 raise ValueError(_("invalid exit code"))
505
518
506 class chunkbuffer(object):
519 class chunkbuffer(object):
507 """Allow arbitrary sized chunks of data to be efficiently read from an
520 """Allow arbitrary sized chunks of data to be efficiently read from an
508 iterator over chunks of arbitrary size."""
521 iterator over chunks of arbitrary size."""
509
522
510 def __init__(self, in_iter, targetsize = 2**16):
523 def __init__(self, in_iter, targetsize = 2**16):
511 """in_iter is the iterator that's iterating over the input chunks.
524 """in_iter is the iterator that's iterating over the input chunks.
512 targetsize is how big a buffer to try to maintain."""
525 targetsize is how big a buffer to try to maintain."""
513 self.in_iter = iter(in_iter)
526 self.in_iter = iter(in_iter)
514 self.buf = ''
527 self.buf = ''
515 self.targetsize = int(targetsize)
528 self.targetsize = int(targetsize)
516 if self.targetsize <= 0:
529 if self.targetsize <= 0:
517 raise ValueError(_("targetsize must be greater than 0, was %d") %
530 raise ValueError(_("targetsize must be greater than 0, was %d") %
518 targetsize)
531 targetsize)
519 self.iterempty = False
532 self.iterempty = False
520
533
521 def fillbuf(self):
534 def fillbuf(self):
522 """Ignore target size; read every chunk from iterator until empty."""
535 """Ignore target size; read every chunk from iterator until empty."""
523 if not self.iterempty:
536 if not self.iterempty:
524 collector = cStringIO.StringIO()
537 collector = cStringIO.StringIO()
525 collector.write(self.buf)
538 collector.write(self.buf)
526 for ch in self.in_iter:
539 for ch in self.in_iter:
527 collector.write(ch)
540 collector.write(ch)
528 self.buf = collector.getvalue()
541 self.buf = collector.getvalue()
529 self.iterempty = True
542 self.iterempty = True
530
543
531 def read(self, l):
544 def read(self, l):
532 """Read L bytes of data from the iterator of chunks of data.
545 """Read L bytes of data from the iterator of chunks of data.
533 Returns less than L bytes if the iterator runs dry."""
546 Returns less than L bytes if the iterator runs dry."""
534 if l > len(self.buf) and not self.iterempty:
547 if l > len(self.buf) and not self.iterempty:
535 # Clamp to a multiple of self.targetsize
548 # Clamp to a multiple of self.targetsize
536 targetsize = self.targetsize * ((l // self.targetsize) + 1)
549 targetsize = self.targetsize * ((l // self.targetsize) + 1)
537 collector = cStringIO.StringIO()
550 collector = cStringIO.StringIO()
538 collector.write(self.buf)
551 collector.write(self.buf)
539 collected = len(self.buf)
552 collected = len(self.buf)
540 for chunk in self.in_iter:
553 for chunk in self.in_iter:
541 collector.write(chunk)
554 collector.write(chunk)
542 collected += len(chunk)
555 collected += len(chunk)
543 if collected >= targetsize:
556 if collected >= targetsize:
544 break
557 break
545 if collected < targetsize:
558 if collected < targetsize:
546 self.iterempty = True
559 self.iterempty = True
547 self.buf = collector.getvalue()
560 self.buf = collector.getvalue()
548 s, self.buf = self.buf[:l], buffer(self.buf, l)
561 s, self.buf = self.buf[:l], buffer(self.buf, l)
549 return s
562 return s
550
563
551 def filechunkiter(f, size = 65536):
564 def filechunkiter(f, size = 65536):
552 """Create a generator that produces all the data in the file size
565 """Create a generator that produces all the data in the file size
553 (default 65536) bytes at a time. Chunks may be less than size
566 (default 65536) bytes at a time. Chunks may be less than size
554 bytes if the chunk is the last chunk in the file, or the file is a
567 bytes if the chunk is the last chunk in the file, or the file is a
555 socket or some other type of file that sometimes reads less data
568 socket or some other type of file that sometimes reads less data
556 than is requested."""
569 than is requested."""
557 s = f.read(size)
570 s = f.read(size)
558 while len(s) > 0:
571 while len(s) > 0:
559 yield s
572 yield s
560 s = f.read(size)
573 s = f.read(size)
561
574
562 def makedate():
575 def makedate():
563 t = time.time()
576 t = time.time()
564 if time.daylight: tz = time.altzone
577 if time.daylight: tz = time.altzone
565 else: tz = time.timezone
578 else: tz = time.timezone
566 return t, tz
579 return t, tz
567
580
568 def datestr(date=None, format='%c'):
581 def datestr(date=None, format='%c'):
569 """represent a (unixtime, offset) tuple as a localized time.
582 """represent a (unixtime, offset) tuple as a localized time.
570 unixtime is seconds since the epoch, and offset is the time zone's
583 unixtime is seconds since the epoch, and offset is the time zone's
571 number of seconds away from UTC."""
584 number of seconds away from UTC."""
572 t, tz = date or makedate()
585 t, tz = date or makedate()
573 return ("%s %+03d%02d" %
586 return ("%s %+03d%02d" %
574 (time.strftime(format, time.gmtime(float(t) - tz)),
587 (time.strftime(format, time.gmtime(float(t) - tz)),
575 -tz / 3600,
588 -tz / 3600,
576 ((-tz % 3600) / 60)))
589 ((-tz % 3600) / 60)))
@@ -1,55 +1,55
1 #!/bin/sh
1 #!/bin/sh
2
2
3 mkdir t
3 mkdir t
4 cd t
4 cd t
5 hg init
5 hg init
6 mkdir -p beans
6 mkdir -p beans
7 for b in kidney navy turtle borlotti black pinto; do
7 for b in kidney navy turtle borlotti black pinto; do
8 echo $b > beans/$b
8 echo $b > beans/$b
9 done
9 done
10 mkdir -p mammals/Procyonidae
10 mkdir -p mammals/Procyonidae
11 for m in cacomistle coatimundi raccoon; do
11 for m in cacomistle coatimundi raccoon; do
12 echo $m > mammals/Procyonidae/$m
12 echo $m > mammals/Procyonidae/$m
13 done
13 done
14 echo skunk > mammals/skunk
14 echo skunk > mammals/skunk
15 echo fennel > fennel
15 echo fennel > fennel
16 echo fenugreek > fenugreek
16 echo fenugreek > fenugreek
17 echo fiddlehead > fiddlehead
17 echo fiddlehead > fiddlehead
18 echo glob:glob > glob:glob
18 echo glob:glob > glob:glob
19 hg addremove
19 hg addremove
20 hg commit -m "commit #0" -d "0 0"
20 hg commit -m "commit #0" -d "0 0"
21 hg debugwalk
21 hg debugwalk
22 cd mammals
22 cd mammals
23 hg debugwalk
23 hg debugwalk
24 hg debugwalk Procyonidae
24 hg debugwalk Procyonidae
25 cd Procyonidae
25 cd Procyonidae
26 hg debugwalk
26 hg debugwalk
27 hg debugwalk ..
27 hg debugwalk ..
28 cd ..
28 cd ..
29 hg debugwalk ../beans
29 hg debugwalk ../beans
30 hg debugwalk
30 hg debugwalk
31 cd ..
31 cd ..
32 hg debugwalk -Ibeans
32 hg debugwalk -Ibeans
33 hg debugwalk 'mammals/../beans/b*'
33 hg debugwalk 'glob:mammals/../beans/b*'
34 hg debugwalk '-X*/Procyonidae' mammals
34 hg debugwalk '-X*/Procyonidae' mammals
35 hg debugwalk path:mammals
35 hg debugwalk path:mammals
36 hg debugwalk ..
36 hg debugwalk ..
37 hg debugwalk beans/../..
37 hg debugwalk beans/../..
38 # Don't know how to test absolute paths without always getting a false
38 # Don't know how to test absolute paths without always getting a false
39 # error.
39 # error.
40 #hg debugwalk `pwd`/beans
40 #hg debugwalk `pwd`/beans
41 #hg debugwalk `pwd`/..
41 #hg debugwalk `pwd`/..
42 hg debugwalk glob:\*
42 hg debugwalk glob:\*
43 hg debugwalk 're:.*[kb]$'
43 hg debugwalk 're:.*[kb]$'
44 hg debugwalk path:beans/black
44 hg debugwalk path:beans/black
45 hg debugwalk beans 'beans/*'
45 hg debugwalk beans 'glob:beans/*'
46 hg debugwalk 'j*'
46 hg debugwalk 'glob:j*'
47 hg debugwalk NOEXIST
47 hg debugwalk NOEXIST
48 mkfifo fifo
48 mkfifo fifo
49 hg debugwalk fifo
49 hg debugwalk fifo
50 rm fenugreek
50 rm fenugreek
51 hg debugwalk fenugreek
51 hg debugwalk fenugreek
52 hg rm fenugreek
52 hg rm fenugreek
53 hg debugwalk fenugreek
53 hg debugwalk fenugreek
54 touch new
54 touch new
55 hg debugwalk new
55 hg debugwalk new
General Comments 0
You need to be logged in to leave comments. Login now