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