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