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