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