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