##// END OF EJS Templates
Add a --time command line option to time hg commands
Stephen Darnell -
r783:4b06fc1c default
parent child Browse files
Show More
@@ -1,1392 +1,1407 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 demandload(globals(), "os re sys signal shutil")
9 demandload(globals(), "os re sys signal shutil")
10 demandload(globals(), "fancyopts ui hg util")
10 demandload(globals(), "fancyopts ui hg util")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 demandload(globals(), "errno socket version struct")
12 demandload(globals(), "errno socket version struct atexit")
13
13
14 class UnknownCommand(Exception):
14 class UnknownCommand(Exception):
15 """Exception raised if command is not in the command table."""
15 """Exception raised if command is not in the command table."""
16
16
17 class Abort(Exception):
17 class Abort(Exception):
18 """Raised if a command needs to print an error and exit."""
18 """Raised if a command needs to print an error and exit."""
19
19
20 def filterfiles(filters, files):
20 def filterfiles(filters, files):
21 l = [x for x in files if x in filters]
21 l = [x for x in files if x in filters]
22
22
23 for t in filters:
23 for t in filters:
24 if t and t[-1] != "/":
24 if t and t[-1] != "/":
25 t += "/"
25 t += "/"
26 l += [x for x in files if x.startswith(t)]
26 l += [x for x in files if x.startswith(t)]
27 return l
27 return l
28
28
29 def relfilter(repo, files):
29 def relfilter(repo, files):
30 cwd = repo.getcwd()
30 cwd = repo.getcwd()
31 if cwd:
31 if cwd:
32 return filterfiles([util.pconvert(cwd)], files)
32 return filterfiles([util.pconvert(cwd)], files)
33 return files
33 return files
34
34
35 def relpath(repo, args):
35 def relpath(repo, args):
36 cwd = repo.getcwd()
36 cwd = repo.getcwd()
37 if cwd:
37 if cwd:
38 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
38 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
39 for x in args]
39 for x in args]
40 return args
40 return args
41
41
42 def matchpats(cwd, pats = [], opts = {}, head = ''):
42 def matchpats(cwd, pats = [], opts = {}, head = ''):
43 return util.matcher(cwd, pats, opts.get('include'),
43 return util.matcher(cwd, pats, opts.get('include'),
44 opts.get('exclude'), head)
44 opts.get('exclude'), head)
45
45
46 def walk(repo, pats, opts, head = ''):
46 def walk(repo, pats, opts, head = ''):
47 cwd = repo.getcwd()
47 cwd = repo.getcwd()
48 c = 0
48 c = 0
49 if cwd: c = len(cwd) + 1
49 if cwd: c = len(cwd) + 1
50 for src, fn in repo.walk(match = matchpats(cwd, pats, opts, head)):
50 for src, fn in repo.walk(match = matchpats(cwd, pats, opts, head)):
51 yield src, fn, fn[c:]
51 yield src, fn, fn[c:]
52
52
53 revrangesep = ':'
53 revrangesep = ':'
54
54
55 def revrange(ui, repo, revs, revlog=None):
55 def revrange(ui, repo, revs, revlog=None):
56 if revlog is None:
56 if revlog is None:
57 revlog = repo.changelog
57 revlog = repo.changelog
58 revcount = revlog.count()
58 revcount = revlog.count()
59 def fix(val, defval):
59 def fix(val, defval):
60 if not val:
60 if not val:
61 return defval
61 return defval
62 try:
62 try:
63 num = int(val)
63 num = int(val)
64 if str(num) != val:
64 if str(num) != val:
65 raise ValueError
65 raise ValueError
66 if num < 0:
66 if num < 0:
67 num += revcount
67 num += revcount
68 if not (0 <= num < revcount):
68 if not (0 <= num < revcount):
69 raise ValueError
69 raise ValueError
70 except ValueError:
70 except ValueError:
71 try:
71 try:
72 num = repo.changelog.rev(repo.lookup(val))
72 num = repo.changelog.rev(repo.lookup(val))
73 except KeyError:
73 except KeyError:
74 try:
74 try:
75 num = revlog.rev(revlog.lookup(val))
75 num = revlog.rev(revlog.lookup(val))
76 except KeyError:
76 except KeyError:
77 raise Abort('invalid revision identifier %s', val)
77 raise Abort('invalid revision identifier %s', val)
78 return num
78 return num
79 for spec in revs:
79 for spec in revs:
80 if spec.find(revrangesep) >= 0:
80 if spec.find(revrangesep) >= 0:
81 start, end = spec.split(revrangesep, 1)
81 start, end = spec.split(revrangesep, 1)
82 start = fix(start, 0)
82 start = fix(start, 0)
83 end = fix(end, revcount - 1)
83 end = fix(end, revcount - 1)
84 if end > start:
84 if end > start:
85 end += 1
85 end += 1
86 step = 1
86 step = 1
87 else:
87 else:
88 end -= 1
88 end -= 1
89 step = -1
89 step = -1
90 for rev in xrange(start, end, step):
90 for rev in xrange(start, end, step):
91 yield str(rev)
91 yield str(rev)
92 else:
92 else:
93 yield spec
93 yield spec
94
94
95 def make_filename(repo, r, pat, node=None,
95 def make_filename(repo, r, pat, node=None,
96 total=None, seqno=None, revwidth=None):
96 total=None, seqno=None, revwidth=None):
97 node_expander = {
97 node_expander = {
98 'H': lambda: hg.hex(node),
98 'H': lambda: hg.hex(node),
99 'R': lambda: str(r.rev(node)),
99 'R': lambda: str(r.rev(node)),
100 'h': lambda: hg.short(node),
100 'h': lambda: hg.short(node),
101 }
101 }
102 expander = {
102 expander = {
103 '%': lambda: '%',
103 '%': lambda: '%',
104 'b': lambda: os.path.basename(repo.root),
104 'b': lambda: os.path.basename(repo.root),
105 }
105 }
106
106
107 try:
107 try:
108 if node:
108 if node:
109 expander.update(node_expander)
109 expander.update(node_expander)
110 if node and revwidth is not None:
110 if node and revwidth is not None:
111 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
111 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
112 if total is not None:
112 if total is not None:
113 expander['N'] = lambda: str(total)
113 expander['N'] = lambda: str(total)
114 if seqno is not None:
114 if seqno is not None:
115 expander['n'] = lambda: str(seqno)
115 expander['n'] = lambda: str(seqno)
116 if total is not None and seqno is not None:
116 if total is not None and seqno is not None:
117 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
117 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
118
118
119 newname = []
119 newname = []
120 patlen = len(pat)
120 patlen = len(pat)
121 i = 0
121 i = 0
122 while i < patlen:
122 while i < patlen:
123 c = pat[i]
123 c = pat[i]
124 if c == '%':
124 if c == '%':
125 i += 1
125 i += 1
126 c = pat[i]
126 c = pat[i]
127 c = expander[c]()
127 c = expander[c]()
128 newname.append(c)
128 newname.append(c)
129 i += 1
129 i += 1
130 return ''.join(newname)
130 return ''.join(newname)
131 except KeyError, inst:
131 except KeyError, inst:
132 raise Abort("invalid format spec '%%%s' in output file name",
132 raise Abort("invalid format spec '%%%s' in output file name",
133 inst.args[0])
133 inst.args[0])
134
134
135 def make_file(repo, r, pat, node=None,
135 def make_file(repo, r, pat, node=None,
136 total=None, seqno=None, revwidth=None, mode='wb'):
136 total=None, seqno=None, revwidth=None, mode='wb'):
137 if not pat or pat == '-':
137 if not pat or pat == '-':
138 if 'w' in mode: return sys.stdout
138 if 'w' in mode: return sys.stdout
139 else: return sys.stdin
139 else: return sys.stdin
140 if hasattr(pat, 'write') and 'w' in mode:
140 if hasattr(pat, 'write') and 'w' in mode:
141 return pat
141 return pat
142 if hasattr(pat, 'read') and 'r' in mode:
142 if hasattr(pat, 'read') and 'r' in mode:
143 return pat
143 return pat
144 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
144 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
145 mode)
145 mode)
146
146
147 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
147 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
148 def date(c):
148 def date(c):
149 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
149 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
150
150
151 (c, a, d, u) = repo.changes(node1, node2, files)
151 (c, a, d, u) = repo.changes(node1, node2, files)
152 if files:
152 if files:
153 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
153 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
154
154
155 if not c and not a and not d:
155 if not c and not a and not d:
156 return
156 return
157
157
158 if node2:
158 if node2:
159 change = repo.changelog.read(node2)
159 change = repo.changelog.read(node2)
160 mmap2 = repo.manifest.read(change[0])
160 mmap2 = repo.manifest.read(change[0])
161 date2 = date(change)
161 date2 = date(change)
162 def read(f):
162 def read(f):
163 return repo.file(f).read(mmap2[f])
163 return repo.file(f).read(mmap2[f])
164 else:
164 else:
165 date2 = time.asctime()
165 date2 = time.asctime()
166 if not node1:
166 if not node1:
167 node1 = repo.dirstate.parents()[0]
167 node1 = repo.dirstate.parents()[0]
168 def read(f):
168 def read(f):
169 return repo.wfile(f).read()
169 return repo.wfile(f).read()
170
170
171 if ui.quiet:
171 if ui.quiet:
172 r = None
172 r = None
173 else:
173 else:
174 hexfunc = ui.verbose and hg.hex or hg.short
174 hexfunc = ui.verbose and hg.hex or hg.short
175 r = [hexfunc(node) for node in [node1, node2] if node]
175 r = [hexfunc(node) for node in [node1, node2] if node]
176
176
177 change = repo.changelog.read(node1)
177 change = repo.changelog.read(node1)
178 mmap = repo.manifest.read(change[0])
178 mmap = repo.manifest.read(change[0])
179 date1 = date(change)
179 date1 = date(change)
180
180
181 for f in c:
181 for f in c:
182 to = None
182 to = None
183 if f in mmap:
183 if f in mmap:
184 to = repo.file(f).read(mmap[f])
184 to = repo.file(f).read(mmap[f])
185 tn = read(f)
185 tn = read(f)
186 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
186 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
187 for f in a:
187 for f in a:
188 to = None
188 to = None
189 tn = read(f)
189 tn = read(f)
190 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
190 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
191 for f in d:
191 for f in d:
192 to = repo.file(f).read(mmap[f])
192 to = repo.file(f).read(mmap[f])
193 tn = None
193 tn = None
194 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
194 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
195
195
196 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
196 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
197 """show a single changeset or file revision"""
197 """show a single changeset or file revision"""
198 changelog = repo.changelog
198 changelog = repo.changelog
199 if filelog:
199 if filelog:
200 log = filelog
200 log = filelog
201 filerev = rev
201 filerev = rev
202 node = filenode = filelog.node(filerev)
202 node = filenode = filelog.node(filerev)
203 changerev = filelog.linkrev(filenode)
203 changerev = filelog.linkrev(filenode)
204 changenode = changenode or changelog.node(changerev)
204 changenode = changenode or changelog.node(changerev)
205 else:
205 else:
206 log = changelog
206 log = changelog
207 changerev = rev
207 changerev = rev
208 if changenode is None:
208 if changenode is None:
209 changenode = changelog.node(changerev)
209 changenode = changelog.node(changerev)
210 elif not changerev:
210 elif not changerev:
211 rev = changerev = changelog.rev(changenode)
211 rev = changerev = changelog.rev(changenode)
212 node = changenode
212 node = changenode
213
213
214 if ui.quiet:
214 if ui.quiet:
215 ui.write("%d:%s\n" % (rev, hg.hex(node)))
215 ui.write("%d:%s\n" % (rev, hg.hex(node)))
216 return
216 return
217
217
218 changes = changelog.read(changenode)
218 changes = changelog.read(changenode)
219
219
220 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
220 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
221 for p in log.parents(node)
221 for p in log.parents(node)
222 if ui.debugflag or p != hg.nullid]
222 if ui.debugflag or p != hg.nullid]
223 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
223 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
224 parents = []
224 parents = []
225
225
226 if ui.verbose:
226 if ui.verbose:
227 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
227 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
228 else:
228 else:
229 ui.write("changeset: %d:%s\n" % (changerev, hg.short(changenode)))
229 ui.write("changeset: %d:%s\n" % (changerev, hg.short(changenode)))
230
230
231 for tag in repo.nodetags(changenode):
231 for tag in repo.nodetags(changenode):
232 ui.status("tag: %s\n" % tag)
232 ui.status("tag: %s\n" % tag)
233 for parent in parents:
233 for parent in parents:
234 ui.write("parent: %d:%s\n" % parent)
234 ui.write("parent: %d:%s\n" % parent)
235 if filelog:
235 if filelog:
236 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
236 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
237
237
238 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
238 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
239 hg.hex(changes[0])))
239 hg.hex(changes[0])))
240 ui.status("user: %s\n" % changes[1])
240 ui.status("user: %s\n" % changes[1])
241 ui.status("date: %s\n" % time.asctime(
241 ui.status("date: %s\n" % time.asctime(
242 time.localtime(float(changes[2].split(' ')[0]))))
242 time.localtime(float(changes[2].split(' ')[0]))))
243
243
244 if ui.debugflag:
244 if ui.debugflag:
245 files = repo.changes(changelog.parents(changenode)[0], changenode)
245 files = repo.changes(changelog.parents(changenode)[0], changenode)
246 for key, value in zip(["files:", "files+:", "files-:"], files):
246 for key, value in zip(["files:", "files+:", "files-:"], files):
247 if value:
247 if value:
248 ui.note("%-12s %s\n" % (key, " ".join(value)))
248 ui.note("%-12s %s\n" % (key, " ".join(value)))
249 else:
249 else:
250 ui.note("files: %s\n" % " ".join(changes[3]))
250 ui.note("files: %s\n" % " ".join(changes[3]))
251
251
252 description = changes[4].strip()
252 description = changes[4].strip()
253 if description:
253 if description:
254 if ui.verbose:
254 if ui.verbose:
255 ui.status("description:\n")
255 ui.status("description:\n")
256 ui.status(description)
256 ui.status(description)
257 ui.status("\n\n")
257 ui.status("\n\n")
258 else:
258 else:
259 ui.status("summary: %s\n" % description.splitlines()[0])
259 ui.status("summary: %s\n" % description.splitlines()[0])
260 ui.status("\n")
260 ui.status("\n")
261
261
262 def show_version(ui):
262 def show_version(ui):
263 """output version and copyright information"""
263 """output version and copyright information"""
264 ui.write("Mercurial version %s\n" % version.get_version())
264 ui.write("Mercurial version %s\n" % version.get_version())
265 ui.status(
265 ui.status(
266 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
266 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
267 "This is free software; see the source for copying conditions. "
267 "This is free software; see the source for copying conditions. "
268 "There is NO\nwarranty; "
268 "There is NO\nwarranty; "
269 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
269 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
270 )
270 )
271
271
272 def help_(ui, cmd=None):
272 def help_(ui, cmd=None):
273 """show help for a given command or all commands"""
273 """show help for a given command or all commands"""
274 if cmd:
274 if cmd:
275 try:
275 try:
276 i = find(cmd)
276 i = find(cmd)
277 ui.write("%s\n\n" % i[2])
277 ui.write("%s\n\n" % i[2])
278
278
279 if i[1]:
279 if i[1]:
280 for s, l, d, c in i[1]:
280 for s, l, d, c in i[1]:
281 opt = ' '
281 opt = ' '
282 if s:
282 if s:
283 opt = opt + '-' + s + ' '
283 opt = opt + '-' + s + ' '
284 if l:
284 if l:
285 opt = opt + '--' + l + ' '
285 opt = opt + '--' + l + ' '
286 if d:
286 if d:
287 opt = opt + '(' + str(d) + ')'
287 opt = opt + '(' + str(d) + ')'
288 ui.write(opt, "\n")
288 ui.write(opt, "\n")
289 if c:
289 if c:
290 ui.write(' %s\n' % c)
290 ui.write(' %s\n' % c)
291 ui.write("\n")
291 ui.write("\n")
292
292
293 ui.write(i[0].__doc__, "\n")
293 ui.write(i[0].__doc__, "\n")
294 except UnknownCommand:
294 except UnknownCommand:
295 ui.warn("hg: unknown command %s\n" % cmd)
295 ui.warn("hg: unknown command %s\n" % cmd)
296 sys.exit(0)
296 sys.exit(0)
297 else:
297 else:
298 if ui.verbose:
298 if ui.verbose:
299 show_version(ui)
299 show_version(ui)
300 ui.write('\n')
300 ui.write('\n')
301 if ui.verbose:
301 if ui.verbose:
302 ui.write('hg commands:\n\n')
302 ui.write('hg commands:\n\n')
303 else:
303 else:
304 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
304 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
305
305
306 h = {}
306 h = {}
307 for c, e in table.items():
307 for c, e in table.items():
308 f = c.split("|")[0]
308 f = c.split("|")[0]
309 if not ui.verbose and not f.startswith("^"):
309 if not ui.verbose and not f.startswith("^"):
310 continue
310 continue
311 if not ui.debugflag and f.startswith("debug"):
311 if not ui.debugflag and f.startswith("debug"):
312 continue
312 continue
313 f = f.lstrip("^")
313 f = f.lstrip("^")
314 d = ""
314 d = ""
315 if e[0].__doc__:
315 if e[0].__doc__:
316 d = e[0].__doc__.splitlines(0)[0].rstrip()
316 d = e[0].__doc__.splitlines(0)[0].rstrip()
317 h[f] = d
317 h[f] = d
318
318
319 fns = h.keys()
319 fns = h.keys()
320 fns.sort()
320 fns.sort()
321 m = max(map(len, fns))
321 m = max(map(len, fns))
322 for f in fns:
322 for f in fns:
323 ui.write(' %-*s %s\n' % (m, f, h[f]))
323 ui.write(' %-*s %s\n' % (m, f, h[f]))
324
324
325 # Commands start here, listed alphabetically
325 # Commands start here, listed alphabetically
326
326
327 def add(ui, repo, *pats, **opts):
327 def add(ui, repo, *pats, **opts):
328 '''add the specified files on the next commit'''
328 '''add the specified files on the next commit'''
329 names = []
329 names = []
330 q = dict(zip(pats, pats))
330 q = dict(zip(pats, pats))
331 for src, abs, rel in walk(repo, pats, opts):
331 for src, abs, rel in walk(repo, pats, opts):
332 if rel in q or abs in q:
332 if rel in q or abs in q:
333 names.append(abs)
333 names.append(abs)
334 elif repo.dirstate.state(abs) == '?':
334 elif repo.dirstate.state(abs) == '?':
335 ui.status('adding %s\n' % rel)
335 ui.status('adding %s\n' % rel)
336 names.append(abs)
336 names.append(abs)
337 repo.add(names)
337 repo.add(names)
338
338
339 def addremove(ui, repo, *pats, **opts):
339 def addremove(ui, repo, *pats, **opts):
340 """add all new files, delete all missing files"""
340 """add all new files, delete all missing files"""
341 q = dict(zip(pats, pats))
341 q = dict(zip(pats, pats))
342 cwd = repo.getcwd()
342 cwd = repo.getcwd()
343 n = (cwd and len(cwd) + 1) or 0
343 n = (cwd and len(cwd) + 1) or 0
344 c, a, d, u = repo.changes(match = matchpats(cwd, pats, opts))
344 c, a, d, u = repo.changes(match = matchpats(cwd, pats, opts))
345 for f in u:
345 for f in u:
346 if f not in q:
346 if f not in q:
347 ui.status('adding %s\n' % f[n:])
347 ui.status('adding %s\n' % f[n:])
348 repo.add(u)
348 repo.add(u)
349 for f in d:
349 for f in d:
350 if f not in q:
350 if f not in q:
351 ui.status('removing %s\n' % f[n:])
351 ui.status('removing %s\n' % f[n:])
352 repo.remove(d)
352 repo.remove(d)
353
353
354 def annotate(ui, repo, *pats, **opts):
354 def annotate(ui, repo, *pats, **opts):
355 """show changeset information per file line"""
355 """show changeset information per file line"""
356 def getnode(rev):
356 def getnode(rev):
357 return hg.short(repo.changelog.node(rev))
357 return hg.short(repo.changelog.node(rev))
358
358
359 def getname(rev):
359 def getname(rev):
360 try:
360 try:
361 return bcache[rev]
361 return bcache[rev]
362 except KeyError:
362 except KeyError:
363 cl = repo.changelog.read(repo.changelog.node(rev))
363 cl = repo.changelog.read(repo.changelog.node(rev))
364 name = cl[1]
364 name = cl[1]
365 f = name.find('@')
365 f = name.find('@')
366 if f >= 0:
366 if f >= 0:
367 name = name[:f]
367 name = name[:f]
368 f = name.find('<')
368 f = name.find('<')
369 if f >= 0:
369 if f >= 0:
370 name = name[f+1:]
370 name = name[f+1:]
371 bcache[rev] = name
371 bcache[rev] = name
372 return name
372 return name
373
373
374 if not pats:
374 if not pats:
375 raise Abort('at least one file name or pattern required')
375 raise Abort('at least one file name or pattern required')
376
376
377 bcache = {}
377 bcache = {}
378 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
378 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
379 if not opts['user'] and not opts['changeset']:
379 if not opts['user'] and not opts['changeset']:
380 opts['number'] = 1
380 opts['number'] = 1
381
381
382 if opts['rev']:
382 if opts['rev']:
383 node = repo.changelog.lookup(opts['rev'])
383 node = repo.changelog.lookup(opts['rev'])
384 else:
384 else:
385 node = repo.dirstate.parents()[0]
385 node = repo.dirstate.parents()[0]
386 change = repo.changelog.read(node)
386 change = repo.changelog.read(node)
387 mmap = repo.manifest.read(change[0])
387 mmap = repo.manifest.read(change[0])
388 for src, abs, rel in walk(repo, pats, opts):
388 for src, abs, rel in walk(repo, pats, opts):
389 if abs not in mmap:
389 if abs not in mmap:
390 ui.warn("warning: %s is not in the repository!\n" % rel)
390 ui.warn("warning: %s is not in the repository!\n" % rel)
391 continue
391 continue
392
392
393 lines = repo.file(abs).annotate(mmap[abs])
393 lines = repo.file(abs).annotate(mmap[abs])
394 pieces = []
394 pieces = []
395
395
396 for o, f in opmap:
396 for o, f in opmap:
397 if opts[o]:
397 if opts[o]:
398 l = [f(n) for n, dummy in lines]
398 l = [f(n) for n, dummy in lines]
399 if l:
399 if l:
400 m = max(map(len, l))
400 m = max(map(len, l))
401 pieces.append(["%*s" % (m, x) for x in l])
401 pieces.append(["%*s" % (m, x) for x in l])
402
402
403 if pieces:
403 if pieces:
404 for p, l in zip(zip(*pieces), lines):
404 for p, l in zip(zip(*pieces), lines):
405 ui.write("%s: %s" % (" ".join(p), l[1]))
405 ui.write("%s: %s" % (" ".join(p), l[1]))
406
406
407 def cat(ui, repo, file1, rev=None, **opts):
407 def cat(ui, repo, file1, rev=None, **opts):
408 """output the latest or given revision of a file"""
408 """output the latest or given revision of a file"""
409 r = repo.file(relpath(repo, [file1])[0])
409 r = repo.file(relpath(repo, [file1])[0])
410 if rev:
410 if rev:
411 n = r.lookup(rev)
411 n = r.lookup(rev)
412 else:
412 else:
413 n = r.tip()
413 n = r.tip()
414 fp = make_file(repo, r, opts['output'], node=n)
414 fp = make_file(repo, r, opts['output'], node=n)
415 fp.write(r.read(n))
415 fp.write(r.read(n))
416
416
417 def clone(ui, source, dest=None, **opts):
417 def clone(ui, source, dest=None, **opts):
418 """make a copy of an existing repository"""
418 """make a copy of an existing repository"""
419 if dest is None:
419 if dest is None:
420 dest = os.path.basename(os.path.normpath(source))
420 dest = os.path.basename(os.path.normpath(source))
421
421
422 if os.path.exists(dest):
422 if os.path.exists(dest):
423 ui.warn("abort: destination '%s' already exists\n" % dest)
423 ui.warn("abort: destination '%s' already exists\n" % dest)
424 return 1
424 return 1
425
425
426 class Dircleanup:
426 class Dircleanup:
427 def __init__(self, dir_):
427 def __init__(self, dir_):
428 self.rmtree = shutil.rmtree
428 self.rmtree = shutil.rmtree
429 self.dir_ = dir_
429 self.dir_ = dir_
430 os.mkdir(dir_)
430 os.mkdir(dir_)
431 def close(self):
431 def close(self):
432 self.dir_ = None
432 self.dir_ = None
433 def __del__(self):
433 def __del__(self):
434 if self.dir_:
434 if self.dir_:
435 self.rmtree(self.dir_, True)
435 self.rmtree(self.dir_, True)
436
436
437 d = Dircleanup(dest)
437 d = Dircleanup(dest)
438 abspath = source
438 abspath = source
439 source = ui.expandpath(source)
439 source = ui.expandpath(source)
440 other = hg.repository(ui, source)
440 other = hg.repository(ui, source)
441
441
442 if other.dev() != -1:
442 if other.dev() != -1:
443 abspath = os.path.abspath(source)
443 abspath = os.path.abspath(source)
444 copyfile = (os.stat(dest).st_dev == other.dev()
444 copyfile = (os.stat(dest).st_dev == other.dev()
445 and getattr(os, 'link', None) or shutil.copy2)
445 and getattr(os, 'link', None) or shutil.copy2)
446 if copyfile is not shutil.copy2:
446 if copyfile is not shutil.copy2:
447 ui.note("cloning by hardlink\n")
447 ui.note("cloning by hardlink\n")
448 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
448 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
449 copyfile)
449 copyfile)
450 try:
450 try:
451 os.unlink(os.path.join(dest, ".hg", "dirstate"))
451 os.unlink(os.path.join(dest, ".hg", "dirstate"))
452 except OSError:
452 except OSError:
453 pass
453 pass
454
454
455 repo = hg.repository(ui, dest)
455 repo = hg.repository(ui, dest)
456
456
457 else:
457 else:
458 repo = hg.repository(ui, dest, create=1)
458 repo = hg.repository(ui, dest, create=1)
459 repo.pull(other)
459 repo.pull(other)
460
460
461 f = repo.opener("hgrc", "w")
461 f = repo.opener("hgrc", "w")
462 f.write("[paths]\n")
462 f.write("[paths]\n")
463 f.write("default = %s\n" % abspath)
463 f.write("default = %s\n" % abspath)
464
464
465 if not opts['noupdate']:
465 if not opts['noupdate']:
466 update(ui, repo)
466 update(ui, repo)
467
467
468 d.close()
468 d.close()
469
469
470 def commit(ui, repo, *files, **opts):
470 def commit(ui, repo, *files, **opts):
471 """commit the specified files or all outstanding changes"""
471 """commit the specified files or all outstanding changes"""
472 if opts['text']:
472 if opts['text']:
473 ui.warn("Warning: -t and --text is deprecated,"
473 ui.warn("Warning: -t and --text is deprecated,"
474 " please use -m or --message instead.\n")
474 " please use -m or --message instead.\n")
475 message = opts['message'] or opts['text']
475 message = opts['message'] or opts['text']
476 logfile = opts['logfile']
476 logfile = opts['logfile']
477 if not message and logfile:
477 if not message and logfile:
478 try:
478 try:
479 message = open(logfile).read()
479 message = open(logfile).read()
480 except IOError, why:
480 except IOError, why:
481 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
481 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
482
482
483 if opts['addremove']:
483 if opts['addremove']:
484 addremove(ui, repo, *files)
484 addremove(ui, repo, *files)
485 repo.commit(relpath(repo, files), message, opts['user'], opts['date'])
485 repo.commit(relpath(repo, files), message, opts['user'], opts['date'])
486
486
487 def copy(ui, repo, source, dest):
487 def copy(ui, repo, source, dest):
488 """mark a file as copied or renamed for the next commit"""
488 """mark a file as copied or renamed for the next commit"""
489 return repo.copy(*relpath(repo, (source, dest)))
489 return repo.copy(*relpath(repo, (source, dest)))
490
490
491 def debugcheckstate(ui, repo):
491 def debugcheckstate(ui, repo):
492 """validate the correctness of the current dirstate"""
492 """validate the correctness of the current dirstate"""
493 parent1, parent2 = repo.dirstate.parents()
493 parent1, parent2 = repo.dirstate.parents()
494 repo.dirstate.read()
494 repo.dirstate.read()
495 dc = repo.dirstate.map
495 dc = repo.dirstate.map
496 keys = dc.keys()
496 keys = dc.keys()
497 keys.sort()
497 keys.sort()
498 m1n = repo.changelog.read(parent1)[0]
498 m1n = repo.changelog.read(parent1)[0]
499 m2n = repo.changelog.read(parent2)[0]
499 m2n = repo.changelog.read(parent2)[0]
500 m1 = repo.manifest.read(m1n)
500 m1 = repo.manifest.read(m1n)
501 m2 = repo.manifest.read(m2n)
501 m2 = repo.manifest.read(m2n)
502 errors = 0
502 errors = 0
503 for f in dc:
503 for f in dc:
504 state = repo.dirstate.state(f)
504 state = repo.dirstate.state(f)
505 if state in "nr" and f not in m1:
505 if state in "nr" and f not in m1:
506 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
506 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
507 errors += 1
507 errors += 1
508 if state in "a" and f in m1:
508 if state in "a" and f in m1:
509 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
509 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
510 errors += 1
510 errors += 1
511 if state in "m" and f not in m1 and f not in m2:
511 if state in "m" and f not in m1 and f not in m2:
512 ui.warn("%s in state %s, but not in either manifest\n" %
512 ui.warn("%s in state %s, but not in either manifest\n" %
513 (f, state))
513 (f, state))
514 errors += 1
514 errors += 1
515 for f in m1:
515 for f in m1:
516 state = repo.dirstate.state(f)
516 state = repo.dirstate.state(f)
517 if state not in "nrm":
517 if state not in "nrm":
518 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
518 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
519 errors += 1
519 errors += 1
520 if errors:
520 if errors:
521 raise Abort(".hg/dirstate inconsistent with current parent's manifest")
521 raise Abort(".hg/dirstate inconsistent with current parent's manifest")
522
522
523 def debugstate(ui, repo):
523 def debugstate(ui, repo):
524 """show the contents of the current dirstate"""
524 """show the contents of the current dirstate"""
525 repo.dirstate.read()
525 repo.dirstate.read()
526 dc = repo.dirstate.map
526 dc = repo.dirstate.map
527 keys = dc.keys()
527 keys = dc.keys()
528 keys.sort()
528 keys.sort()
529 for file_ in keys:
529 for file_ in keys:
530 ui.write("%c %s\n" % (dc[file_][0], file_))
530 ui.write("%c %s\n" % (dc[file_][0], file_))
531
531
532 def debugindex(ui, file_):
532 def debugindex(ui, file_):
533 """dump the contents of an index file"""
533 """dump the contents of an index file"""
534 r = hg.revlog(hg.opener(""), file_, "")
534 r = hg.revlog(hg.opener(""), file_, "")
535 ui.write(" rev offset length base linkrev" +
535 ui.write(" rev offset length base linkrev" +
536 " p1 p2 nodeid\n")
536 " p1 p2 nodeid\n")
537 for i in range(r.count()):
537 for i in range(r.count()):
538 e = r.index[i]
538 e = r.index[i]
539 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
539 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
540 i, e[0], e[1], e[2], e[3],
540 i, e[0], e[1], e[2], e[3],
541 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
541 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
542
542
543 def debugindexdot(ui, file_):
543 def debugindexdot(ui, file_):
544 """dump an index DAG as a .dot file"""
544 """dump an index DAG as a .dot file"""
545 r = hg.revlog(hg.opener(""), file_, "")
545 r = hg.revlog(hg.opener(""), file_, "")
546 ui.write("digraph G {\n")
546 ui.write("digraph G {\n")
547 for i in range(r.count()):
547 for i in range(r.count()):
548 e = r.index[i]
548 e = r.index[i]
549 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
549 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
550 if e[5] != hg.nullid:
550 if e[5] != hg.nullid:
551 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
551 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
552 ui.write("}\n")
552 ui.write("}\n")
553
553
554 def diff(ui, repo, *pats, **opts):
554 def diff(ui, repo, *pats, **opts):
555 """diff working directory (or selected files)"""
555 """diff working directory (or selected files)"""
556 revs = []
556 revs = []
557 if opts['rev']:
557 if opts['rev']:
558 revs = map(lambda x: repo.lookup(x), opts['rev'])
558 revs = map(lambda x: repo.lookup(x), opts['rev'])
559
559
560 if len(revs) > 2:
560 if len(revs) > 2:
561 raise Abort("too many revisions to diff")
561 raise Abort("too many revisions to diff")
562
562
563 files = []
563 files = []
564 for src, abs, rel in walk(repo, pats, opts):
564 for src, abs, rel in walk(repo, pats, opts):
565 files.append(abs)
565 files.append(abs)
566 dodiff(sys.stdout, ui, repo, files, *revs)
566 dodiff(sys.stdout, ui, repo, files, *revs)
567
567
568 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
568 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
569 node = repo.lookup(changeset)
569 node = repo.lookup(changeset)
570 prev, other = repo.changelog.parents(node)
570 prev, other = repo.changelog.parents(node)
571 change = repo.changelog.read(node)
571 change = repo.changelog.read(node)
572
572
573 fp = make_file(repo, repo.changelog, opts['output'],
573 fp = make_file(repo, repo.changelog, opts['output'],
574 node=node, total=total, seqno=seqno,
574 node=node, total=total, seqno=seqno,
575 revwidth=revwidth)
575 revwidth=revwidth)
576 if fp != sys.stdout:
576 if fp != sys.stdout:
577 ui.note("%s\n" % fp.name)
577 ui.note("%s\n" % fp.name)
578
578
579 fp.write("# HG changeset patch\n")
579 fp.write("# HG changeset patch\n")
580 fp.write("# User %s\n" % change[1])
580 fp.write("# User %s\n" % change[1])
581 fp.write("# Node ID %s\n" % hg.hex(node))
581 fp.write("# Node ID %s\n" % hg.hex(node))
582 fp.write("# Parent %s\n" % hg.hex(prev))
582 fp.write("# Parent %s\n" % hg.hex(prev))
583 if other != hg.nullid:
583 if other != hg.nullid:
584 fp.write("# Parent %s\n" % hg.hex(other))
584 fp.write("# Parent %s\n" % hg.hex(other))
585 fp.write(change[4].rstrip())
585 fp.write(change[4].rstrip())
586 fp.write("\n\n")
586 fp.write("\n\n")
587
587
588 dodiff(fp, ui, repo, None, prev, node)
588 dodiff(fp, ui, repo, None, prev, node)
589 if fp != sys.stdout: fp.close()
589 if fp != sys.stdout: fp.close()
590
590
591 def export(ui, repo, *changesets, **opts):
591 def export(ui, repo, *changesets, **opts):
592 """dump the header and diffs for one or more changesets"""
592 """dump the header and diffs for one or more changesets"""
593 if not changesets:
593 if not changesets:
594 raise Abort("export requires at least one changeset")
594 raise Abort("export requires at least one changeset")
595 seqno = 0
595 seqno = 0
596 revs = list(revrange(ui, repo, changesets))
596 revs = list(revrange(ui, repo, changesets))
597 total = len(revs)
597 total = len(revs)
598 revwidth = max(len(revs[0]), len(revs[-1]))
598 revwidth = max(len(revs[0]), len(revs[-1]))
599 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
599 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
600 for cset in revs:
600 for cset in revs:
601 seqno += 1
601 seqno += 1
602 doexport(ui, repo, cset, seqno, total, revwidth, opts)
602 doexport(ui, repo, cset, seqno, total, revwidth, opts)
603
603
604 def forget(ui, repo, file1, *files):
604 def forget(ui, repo, file1, *files):
605 """don't add the specified files on the next commit"""
605 """don't add the specified files on the next commit"""
606 repo.forget(relpath(repo, (file1,) + files))
606 repo.forget(relpath(repo, (file1,) + files))
607
607
608 def heads(ui, repo):
608 def heads(ui, repo):
609 """show current repository heads"""
609 """show current repository heads"""
610 for n in repo.changelog.heads():
610 for n in repo.changelog.heads():
611 show_changeset(ui, repo, changenode=n)
611 show_changeset(ui, repo, changenode=n)
612
612
613 def identify(ui, repo):
613 def identify(ui, repo):
614 """print information about the working copy"""
614 """print information about the working copy"""
615 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
615 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
616 if not parents:
616 if not parents:
617 ui.write("unknown\n")
617 ui.write("unknown\n")
618 return
618 return
619
619
620 hexfunc = ui.verbose and hg.hex or hg.short
620 hexfunc = ui.verbose and hg.hex or hg.short
621 (c, a, d, u) = repo.changes()
621 (c, a, d, u) = repo.changes()
622 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
622 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
623 (c or a or d) and "+" or "")]
623 (c or a or d) and "+" or "")]
624
624
625 if not ui.quiet:
625 if not ui.quiet:
626 # multiple tags for a single parent separated by '/'
626 # multiple tags for a single parent separated by '/'
627 parenttags = ['/'.join(tags)
627 parenttags = ['/'.join(tags)
628 for tags in map(repo.nodetags, parents) if tags]
628 for tags in map(repo.nodetags, parents) if tags]
629 # tags for multiple parents separated by ' + '
629 # tags for multiple parents separated by ' + '
630 if parenttags:
630 if parenttags:
631 output.append(' + '.join(parenttags))
631 output.append(' + '.join(parenttags))
632
632
633 ui.write("%s\n" % ' '.join(output))
633 ui.write("%s\n" % ' '.join(output))
634
634
635 def import_(ui, repo, patch1, *patches, **opts):
635 def import_(ui, repo, patch1, *patches, **opts):
636 """import an ordered set of patches"""
636 """import an ordered set of patches"""
637 try:
637 try:
638 import psyco
638 import psyco
639 psyco.full()
639 psyco.full()
640 except ImportError:
640 except ImportError:
641 pass
641 pass
642
642
643 patches = (patch1,) + patches
643 patches = (patch1,) + patches
644
644
645 d = opts["base"]
645 d = opts["base"]
646 strip = opts["strip"]
646 strip = opts["strip"]
647
647
648 for patch in patches:
648 for patch in patches:
649 ui.status("applying %s\n" % patch)
649 ui.status("applying %s\n" % patch)
650 pf = os.path.join(d, patch)
650 pf = os.path.join(d, patch)
651
651
652 message = []
652 message = []
653 user = None
653 user = None
654 hgpatch = False
654 hgpatch = False
655 for line in file(pf):
655 for line in file(pf):
656 line = line.rstrip()
656 line = line.rstrip()
657 if line.startswith("--- ") or line.startswith("diff -r"):
657 if line.startswith("--- ") or line.startswith("diff -r"):
658 break
658 break
659 elif hgpatch:
659 elif hgpatch:
660 # parse values when importing the result of an hg export
660 # parse values when importing the result of an hg export
661 if line.startswith("# User "):
661 if line.startswith("# User "):
662 user = line[7:]
662 user = line[7:]
663 ui.debug('User: %s\n' % user)
663 ui.debug('User: %s\n' % user)
664 elif not line.startswith("# ") and line:
664 elif not line.startswith("# ") and line:
665 message.append(line)
665 message.append(line)
666 hgpatch = False
666 hgpatch = False
667 elif line == '# HG changeset patch':
667 elif line == '# HG changeset patch':
668 hgpatch = True
668 hgpatch = True
669 else:
669 else:
670 message.append(line)
670 message.append(line)
671
671
672 # make sure message isn't empty
672 # make sure message isn't empty
673 if not message:
673 if not message:
674 message = "imported patch %s\n" % patch
674 message = "imported patch %s\n" % patch
675 else:
675 else:
676 message = "%s\n" % '\n'.join(message)
676 message = "%s\n" % '\n'.join(message)
677 ui.debug('message:\n%s\n' % message)
677 ui.debug('message:\n%s\n' % message)
678
678
679 f = os.popen("patch -p%d < %s" % (strip, pf))
679 f = os.popen("patch -p%d < %s" % (strip, pf))
680 files = []
680 files = []
681 for l in f.read().splitlines():
681 for l in f.read().splitlines():
682 l.rstrip('\r\n');
682 l.rstrip('\r\n');
683 ui.status("%s\n" % l)
683 ui.status("%s\n" % l)
684 if l.startswith('patching file '):
684 if l.startswith('patching file '):
685 pf = l[14:]
685 pf = l[14:]
686 if pf not in files:
686 if pf not in files:
687 files.append(pf)
687 files.append(pf)
688 patcherr = f.close()
688 patcherr = f.close()
689 if patcherr:
689 if patcherr:
690 raise Abort("patch failed")
690 raise Abort("patch failed")
691
691
692 if len(files) > 0:
692 if len(files) > 0:
693 addremove(ui, repo, *files)
693 addremove(ui, repo, *files)
694 repo.commit(files, message, user)
694 repo.commit(files, message, user)
695
695
696 def init(ui, source=None):
696 def init(ui, source=None):
697 """create a new repository in the current directory"""
697 """create a new repository in the current directory"""
698
698
699 if source:
699 if source:
700 raise Abort("no longer supported: use \"hg clone\" instead")
700 raise Abort("no longer supported: use \"hg clone\" instead")
701 hg.repository(ui, ".", create=1)
701 hg.repository(ui, ".", create=1)
702
702
703 def locate(ui, repo, *pats, **opts):
703 def locate(ui, repo, *pats, **opts):
704 """locate files matching specific patterns"""
704 """locate files matching specific patterns"""
705 end = '\n'
705 end = '\n'
706 if opts['print0']: end = '\0'
706 if opts['print0']: end = '\0'
707
707
708 for src, abs, rel in walk(repo, pats, opts, '(?:.*/|)'):
708 for src, abs, rel in walk(repo, pats, opts, '(?:.*/|)'):
709 if repo.dirstate.state(abs) == '?': continue
709 if repo.dirstate.state(abs) == '?': continue
710 if opts['fullpath']:
710 if opts['fullpath']:
711 ui.write(os.path.join(repo.root, abs), end)
711 ui.write(os.path.join(repo.root, abs), end)
712 else:
712 else:
713 ui.write(rel, end)
713 ui.write(rel, end)
714
714
715 def log(ui, repo, f=None, **opts):
715 def log(ui, repo, f=None, **opts):
716 """show the revision history of the repository or a single file"""
716 """show the revision history of the repository or a single file"""
717 if f:
717 if f:
718 files = relpath(repo, [f])
718 files = relpath(repo, [f])
719 filelog = repo.file(files[0])
719 filelog = repo.file(files[0])
720 log = filelog
720 log = filelog
721 lookup = filelog.lookup
721 lookup = filelog.lookup
722 else:
722 else:
723 files = None
723 files = None
724 filelog = None
724 filelog = None
725 log = repo.changelog
725 log = repo.changelog
726 lookup = repo.lookup
726 lookup = repo.lookup
727 revlist = []
727 revlist = []
728 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
728 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
729 while revs:
729 while revs:
730 if len(revs) == 1:
730 if len(revs) == 1:
731 revlist.append(revs.pop(0))
731 revlist.append(revs.pop(0))
732 else:
732 else:
733 a = revs.pop(0)
733 a = revs.pop(0)
734 b = revs.pop(0)
734 b = revs.pop(0)
735 off = a > b and -1 or 1
735 off = a > b and -1 or 1
736 revlist.extend(range(a, b + off, off))
736 revlist.extend(range(a, b + off, off))
737
737
738 for i in revlist or range(log.count() - 1, -1, -1):
738 for i in revlist or range(log.count() - 1, -1, -1):
739 show_changeset(ui, repo, filelog=filelog, rev=i)
739 show_changeset(ui, repo, filelog=filelog, rev=i)
740 if opts['patch']:
740 if opts['patch']:
741 if filelog:
741 if filelog:
742 filenode = filelog.node(i)
742 filenode = filelog.node(i)
743 i = filelog.linkrev(filenode)
743 i = filelog.linkrev(filenode)
744 changenode = repo.changelog.node(i)
744 changenode = repo.changelog.node(i)
745 prev, other = repo.changelog.parents(changenode)
745 prev, other = repo.changelog.parents(changenode)
746 dodiff(sys.stdout, ui, repo, files, prev, changenode)
746 dodiff(sys.stdout, ui, repo, files, prev, changenode)
747 ui.write("\n\n")
747 ui.write("\n\n")
748
748
749 def manifest(ui, repo, rev=None):
749 def manifest(ui, repo, rev=None):
750 """output the latest or given revision of the project manifest"""
750 """output the latest or given revision of the project manifest"""
751 if rev:
751 if rev:
752 try:
752 try:
753 # assume all revision numbers are for changesets
753 # assume all revision numbers are for changesets
754 n = repo.lookup(rev)
754 n = repo.lookup(rev)
755 change = repo.changelog.read(n)
755 change = repo.changelog.read(n)
756 n = change[0]
756 n = change[0]
757 except hg.RepoError:
757 except hg.RepoError:
758 n = repo.manifest.lookup(rev)
758 n = repo.manifest.lookup(rev)
759 else:
759 else:
760 n = repo.manifest.tip()
760 n = repo.manifest.tip()
761 m = repo.manifest.read(n)
761 m = repo.manifest.read(n)
762 mf = repo.manifest.readflags(n)
762 mf = repo.manifest.readflags(n)
763 files = m.keys()
763 files = m.keys()
764 files.sort()
764 files.sort()
765
765
766 for f in files:
766 for f in files:
767 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
767 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
768
768
769 def parents(ui, repo, rev=None):
769 def parents(ui, repo, rev=None):
770 """show the parents of the working dir or revision"""
770 """show the parents of the working dir or revision"""
771 if rev:
771 if rev:
772 p = repo.changelog.parents(repo.lookup(rev))
772 p = repo.changelog.parents(repo.lookup(rev))
773 else:
773 else:
774 p = repo.dirstate.parents()
774 p = repo.dirstate.parents()
775
775
776 for n in p:
776 for n in p:
777 if n != hg.nullid:
777 if n != hg.nullid:
778 show_changeset(ui, repo, changenode=n)
778 show_changeset(ui, repo, changenode=n)
779
779
780 def paths(ui, repo, search = None):
780 def paths(ui, repo, search = None):
781 """show path or list of available paths"""
781 """show path or list of available paths"""
782 if search:
782 if search:
783 for name, path in ui.configitems("paths"):
783 for name, path in ui.configitems("paths"):
784 if name == search:
784 if name == search:
785 ui.write("%s\n" % path)
785 ui.write("%s\n" % path)
786 return
786 return
787 ui.warn("not found!\n")
787 ui.warn("not found!\n")
788 return 1
788 return 1
789 else:
789 else:
790 for name, path in ui.configitems("paths"):
790 for name, path in ui.configitems("paths"):
791 ui.write("%s = %s\n" % (name, path))
791 ui.write("%s = %s\n" % (name, path))
792
792
793 def pull(ui, repo, source="default", **opts):
793 def pull(ui, repo, source="default", **opts):
794 """pull changes from the specified source"""
794 """pull changes from the specified source"""
795 source = ui.expandpath(source)
795 source = ui.expandpath(source)
796 ui.status('pulling from %s\n' % (source))
796 ui.status('pulling from %s\n' % (source))
797
797
798 other = hg.repository(ui, source)
798 other = hg.repository(ui, source)
799 r = repo.pull(other)
799 r = repo.pull(other)
800 if not r:
800 if not r:
801 if opts['update']:
801 if opts['update']:
802 return update(ui, repo)
802 return update(ui, repo)
803 else:
803 else:
804 ui.status("(run 'hg update' to get a working copy)\n")
804 ui.status("(run 'hg update' to get a working copy)\n")
805
805
806 return r
806 return r
807
807
808 def push(ui, repo, dest="default-push"):
808 def push(ui, repo, dest="default-push"):
809 """push changes to the specified destination"""
809 """push changes to the specified destination"""
810 dest = ui.expandpath(dest)
810 dest = ui.expandpath(dest)
811 ui.status('pushing to %s\n' % (dest))
811 ui.status('pushing to %s\n' % (dest))
812
812
813 other = hg.repository(ui, dest)
813 other = hg.repository(ui, dest)
814 r = repo.push(other)
814 r = repo.push(other)
815 return r
815 return r
816
816
817 def rawcommit(ui, repo, *flist, **rc):
817 def rawcommit(ui, repo, *flist, **rc):
818 "raw commit interface"
818 "raw commit interface"
819 if rc['text']:
819 if rc['text']:
820 ui.warn("Warning: -t and --text is deprecated,"
820 ui.warn("Warning: -t and --text is deprecated,"
821 " please use -m or --message instead.\n")
821 " please use -m or --message instead.\n")
822 message = rc['message'] or rc['text']
822 message = rc['message'] or rc['text']
823 if not message and rc['logfile']:
823 if not message and rc['logfile']:
824 try:
824 try:
825 message = open(rc['logfile']).read()
825 message = open(rc['logfile']).read()
826 except IOError:
826 except IOError:
827 pass
827 pass
828 if not message and not rc['logfile']:
828 if not message and not rc['logfile']:
829 ui.warn("abort: missing commit message\n")
829 ui.warn("abort: missing commit message\n")
830 return 1
830 return 1
831
831
832 files = relpath(repo, list(flist))
832 files = relpath(repo, list(flist))
833 if rc['files']:
833 if rc['files']:
834 files += open(rc['files']).read().splitlines()
834 files += open(rc['files']).read().splitlines()
835
835
836 rc['parent'] = map(repo.lookup, rc['parent'])
836 rc['parent'] = map(repo.lookup, rc['parent'])
837
837
838 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
838 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
839
839
840 def recover(ui, repo):
840 def recover(ui, repo):
841 """roll back an interrupted transaction"""
841 """roll back an interrupted transaction"""
842 repo.recover()
842 repo.recover()
843
843
844 def remove(ui, repo, file1, *files):
844 def remove(ui, repo, file1, *files):
845 """remove the specified files on the next commit"""
845 """remove the specified files on the next commit"""
846 repo.remove(relpath(repo, (file1,) + files))
846 repo.remove(relpath(repo, (file1,) + files))
847
847
848 def revert(ui, repo, *names, **opts):
848 def revert(ui, repo, *names, **opts):
849 """revert modified files or dirs back to their unmodified states"""
849 """revert modified files or dirs back to their unmodified states"""
850 node = opts['rev'] and repo.lookup(opts['rev']) or \
850 node = opts['rev'] and repo.lookup(opts['rev']) or \
851 repo.dirstate.parents()[0]
851 repo.dirstate.parents()[0]
852 root = os.path.realpath(repo.root)
852 root = os.path.realpath(repo.root)
853
853
854 def trimpath(p):
854 def trimpath(p):
855 p = os.path.realpath(p)
855 p = os.path.realpath(p)
856 if p.startswith(root):
856 if p.startswith(root):
857 rest = p[len(root):]
857 rest = p[len(root):]
858 if not rest:
858 if not rest:
859 return rest
859 return rest
860 if p.startswith(os.sep):
860 if p.startswith(os.sep):
861 return rest[1:]
861 return rest[1:]
862 return p
862 return p
863
863
864 relnames = map(trimpath, names or [os.getcwd()])
864 relnames = map(trimpath, names or [os.getcwd()])
865 chosen = {}
865 chosen = {}
866
866
867 def choose(name):
867 def choose(name):
868 def body(name):
868 def body(name):
869 for r in relnames:
869 for r in relnames:
870 if not name.startswith(r):
870 if not name.startswith(r):
871 continue
871 continue
872 rest = name[len(r):]
872 rest = name[len(r):]
873 if not rest:
873 if not rest:
874 return r, True
874 return r, True
875 depth = rest.count(os.sep)
875 depth = rest.count(os.sep)
876 if not r:
876 if not r:
877 if depth == 0 or not opts['nonrecursive']:
877 if depth == 0 or not opts['nonrecursive']:
878 return r, True
878 return r, True
879 elif rest[0] == os.sep:
879 elif rest[0] == os.sep:
880 if depth == 1 or not opts['nonrecursive']:
880 if depth == 1 or not opts['nonrecursive']:
881 return r, True
881 return r, True
882 return None, False
882 return None, False
883 relname, ret = body(name)
883 relname, ret = body(name)
884 if ret:
884 if ret:
885 chosen[relname] = 1
885 chosen[relname] = 1
886 return ret
886 return ret
887
887
888 r = repo.update(node, False, True, choose, False)
888 r = repo.update(node, False, True, choose, False)
889 for n in relnames:
889 for n in relnames:
890 if n not in chosen:
890 if n not in chosen:
891 ui.warn('error: no matches for %s\n' % n)
891 ui.warn('error: no matches for %s\n' % n)
892 r = 1
892 r = 1
893 sys.stdout.flush()
893 sys.stdout.flush()
894 return r
894 return r
895
895
896 def root(ui, repo):
896 def root(ui, repo):
897 """print the root (top) of the current working dir"""
897 """print the root (top) of the current working dir"""
898 ui.write(repo.root + "\n")
898 ui.write(repo.root + "\n")
899
899
900 def serve(ui, repo, **opts):
900 def serve(ui, repo, **opts):
901 """export the repository via HTTP"""
901 """export the repository via HTTP"""
902
902
903 if opts["stdio"]:
903 if opts["stdio"]:
904 fin, fout = sys.stdin, sys.stdout
904 fin, fout = sys.stdin, sys.stdout
905 sys.stdout = sys.stderr
905 sys.stdout = sys.stderr
906
906
907 def getarg():
907 def getarg():
908 argline = fin.readline()[:-1]
908 argline = fin.readline()[:-1]
909 arg, l = argline.split()
909 arg, l = argline.split()
910 val = fin.read(int(l))
910 val = fin.read(int(l))
911 return arg, val
911 return arg, val
912 def respond(v):
912 def respond(v):
913 fout.write("%d\n" % len(v))
913 fout.write("%d\n" % len(v))
914 fout.write(v)
914 fout.write(v)
915 fout.flush()
915 fout.flush()
916
916
917 lock = None
917 lock = None
918
918
919 while 1:
919 while 1:
920 cmd = fin.readline()[:-1]
920 cmd = fin.readline()[:-1]
921 if cmd == '':
921 if cmd == '':
922 return
922 return
923 if cmd == "heads":
923 if cmd == "heads":
924 h = repo.heads()
924 h = repo.heads()
925 respond(" ".join(map(hg.hex, h)) + "\n")
925 respond(" ".join(map(hg.hex, h)) + "\n")
926 if cmd == "lock":
926 if cmd == "lock":
927 lock = repo.lock()
927 lock = repo.lock()
928 respond("")
928 respond("")
929 if cmd == "unlock":
929 if cmd == "unlock":
930 if lock:
930 if lock:
931 lock.release()
931 lock.release()
932 lock = None
932 lock = None
933 respond("")
933 respond("")
934 elif cmd == "branches":
934 elif cmd == "branches":
935 arg, nodes = getarg()
935 arg, nodes = getarg()
936 nodes = map(hg.bin, nodes.split(" "))
936 nodes = map(hg.bin, nodes.split(" "))
937 r = []
937 r = []
938 for b in repo.branches(nodes):
938 for b in repo.branches(nodes):
939 r.append(" ".join(map(hg.hex, b)) + "\n")
939 r.append(" ".join(map(hg.hex, b)) + "\n")
940 respond("".join(r))
940 respond("".join(r))
941 elif cmd == "between":
941 elif cmd == "between":
942 arg, pairs = getarg()
942 arg, pairs = getarg()
943 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
943 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
944 r = []
944 r = []
945 for b in repo.between(pairs):
945 for b in repo.between(pairs):
946 r.append(" ".join(map(hg.hex, b)) + "\n")
946 r.append(" ".join(map(hg.hex, b)) + "\n")
947 respond("".join(r))
947 respond("".join(r))
948 elif cmd == "changegroup":
948 elif cmd == "changegroup":
949 nodes = []
949 nodes = []
950 arg, roots = getarg()
950 arg, roots = getarg()
951 nodes = map(hg.bin, roots.split(" "))
951 nodes = map(hg.bin, roots.split(" "))
952
952
953 cg = repo.changegroup(nodes)
953 cg = repo.changegroup(nodes)
954 while 1:
954 while 1:
955 d = cg.read(4096)
955 d = cg.read(4096)
956 if not d:
956 if not d:
957 break
957 break
958 fout.write(d)
958 fout.write(d)
959
959
960 fout.flush()
960 fout.flush()
961
961
962 elif cmd == "addchangegroup":
962 elif cmd == "addchangegroup":
963 if not lock:
963 if not lock:
964 respond("not locked")
964 respond("not locked")
965 continue
965 continue
966 respond("")
966 respond("")
967
967
968 r = repo.addchangegroup(fin)
968 r = repo.addchangegroup(fin)
969 respond("")
969 respond("")
970
970
971 def openlog(opt, default):
971 def openlog(opt, default):
972 if opts[opt] and opts[opt] != '-':
972 if opts[opt] and opts[opt] != '-':
973 return open(opts[opt], 'w')
973 return open(opts[opt], 'w')
974 else:
974 else:
975 return default
975 return default
976
976
977 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
977 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
978 opts["address"], opts["port"],
978 opts["address"], opts["port"],
979 openlog('accesslog', sys.stdout),
979 openlog('accesslog', sys.stdout),
980 openlog('errorlog', sys.stderr))
980 openlog('errorlog', sys.stderr))
981 if ui.verbose:
981 if ui.verbose:
982 addr, port = httpd.socket.getsockname()
982 addr, port = httpd.socket.getsockname()
983 if addr == '0.0.0.0':
983 if addr == '0.0.0.0':
984 addr = socket.gethostname()
984 addr = socket.gethostname()
985 else:
985 else:
986 try:
986 try:
987 addr = socket.gethostbyaddr(addr)[0]
987 addr = socket.gethostbyaddr(addr)[0]
988 except socket.error:
988 except socket.error:
989 pass
989 pass
990 if port != 80:
990 if port != 80:
991 ui.status('listening at http://%s:%d/\n' % (addr, port))
991 ui.status('listening at http://%s:%d/\n' % (addr, port))
992 else:
992 else:
993 ui.status('listening at http://%s/\n' % addr)
993 ui.status('listening at http://%s/\n' % addr)
994 httpd.serve_forever()
994 httpd.serve_forever()
995
995
996 def status(ui, repo, *pats, **opts):
996 def status(ui, repo, *pats, **opts):
997 '''show changed files in the working directory
997 '''show changed files in the working directory
998
998
999 M = modified
999 M = modified
1000 A = added
1000 A = added
1001 R = removed
1001 R = removed
1002 ? = not tracked'''
1002 ? = not tracked'''
1003
1003
1004 (c, a, d, u) = repo.changes(match = matchpats(repo.getcwd(), pats, opts))
1004 (c, a, d, u) = repo.changes(match = matchpats(repo.getcwd(), pats, opts))
1005 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
1005 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
1006
1006
1007 for f in c:
1007 for f in c:
1008 ui.write("M ", f, "\n")
1008 ui.write("M ", f, "\n")
1009 for f in a:
1009 for f in a:
1010 ui.write("A ", f, "\n")
1010 ui.write("A ", f, "\n")
1011 for f in d:
1011 for f in d:
1012 ui.write("R ", f, "\n")
1012 ui.write("R ", f, "\n")
1013 for f in u:
1013 for f in u:
1014 ui.write("? ", f, "\n")
1014 ui.write("? ", f, "\n")
1015
1015
1016 def tag(ui, repo, name, rev=None, **opts):
1016 def tag(ui, repo, name, rev=None, **opts):
1017 """add a tag for the current tip or a given revision"""
1017 """add a tag for the current tip or a given revision"""
1018 if opts['text']:
1018 if opts['text']:
1019 ui.warn("Warning: -t and --text is deprecated,"
1019 ui.warn("Warning: -t and --text is deprecated,"
1020 " please use -m or --message instead.\n")
1020 " please use -m or --message instead.\n")
1021 if name == "tip":
1021 if name == "tip":
1022 ui.warn("abort: 'tip' is a reserved name!\n")
1022 ui.warn("abort: 'tip' is a reserved name!\n")
1023 return -1
1023 return -1
1024 if rev:
1024 if rev:
1025 r = hg.hex(repo.lookup(rev))
1025 r = hg.hex(repo.lookup(rev))
1026 else:
1026 else:
1027 r = hg.hex(repo.changelog.tip())
1027 r = hg.hex(repo.changelog.tip())
1028
1028
1029 if name.find(revrangesep) >= 0:
1029 if name.find(revrangesep) >= 0:
1030 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1030 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1031 return -1
1031 return -1
1032
1032
1033 if opts['local']:
1033 if opts['local']:
1034 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1034 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1035 return
1035 return
1036
1036
1037 (c, a, d, u) = repo.changes()
1037 (c, a, d, u) = repo.changes()
1038 for x in (c, a, d, u):
1038 for x in (c, a, d, u):
1039 if ".hgtags" in x:
1039 if ".hgtags" in x:
1040 ui.warn("abort: working copy of .hgtags is changed!\n")
1040 ui.warn("abort: working copy of .hgtags is changed!\n")
1041 ui.status("(please commit .hgtags manually)\n")
1041 ui.status("(please commit .hgtags manually)\n")
1042 return -1
1042 return -1
1043
1043
1044 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1044 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1045 if repo.dirstate.state(".hgtags") == '?':
1045 if repo.dirstate.state(".hgtags") == '?':
1046 repo.add([".hgtags"])
1046 repo.add([".hgtags"])
1047
1047
1048 message = (opts['message'] or opts['text'] or
1048 message = (opts['message'] or opts['text'] or
1049 "Added tag %s for changeset %s" % (name, r))
1049 "Added tag %s for changeset %s" % (name, r))
1050 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1050 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1051
1051
1052 def tags(ui, repo):
1052 def tags(ui, repo):
1053 """list repository tags"""
1053 """list repository tags"""
1054
1054
1055 l = repo.tagslist()
1055 l = repo.tagslist()
1056 l.reverse()
1056 l.reverse()
1057 for t, n in l:
1057 for t, n in l:
1058 try:
1058 try:
1059 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1059 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1060 except KeyError:
1060 except KeyError:
1061 r = " ?:?"
1061 r = " ?:?"
1062 ui.write("%-30s %s\n" % (t, r))
1062 ui.write("%-30s %s\n" % (t, r))
1063
1063
1064 def tip(ui, repo):
1064 def tip(ui, repo):
1065 """show the tip revision"""
1065 """show the tip revision"""
1066 n = repo.changelog.tip()
1066 n = repo.changelog.tip()
1067 show_changeset(ui, repo, changenode=n)
1067 show_changeset(ui, repo, changenode=n)
1068
1068
1069 def undo(ui, repo):
1069 def undo(ui, repo):
1070 """undo the last commit or pull
1070 """undo the last commit or pull
1071
1071
1072 Roll back the last pull or commit transaction on the
1072 Roll back the last pull or commit transaction on the
1073 repository, restoring the project to its earlier state.
1073 repository, restoring the project to its earlier state.
1074
1074
1075 This command should be used with care. There is only one level of
1075 This command should be used with care. There is only one level of
1076 undo and there is no redo.
1076 undo and there is no redo.
1077
1077
1078 This command is not intended for use on public repositories. Once
1078 This command is not intended for use on public repositories. Once
1079 a change is visible for pull by other users, undoing it locally is
1079 a change is visible for pull by other users, undoing it locally is
1080 ineffective.
1080 ineffective.
1081 """
1081 """
1082 repo.undo()
1082 repo.undo()
1083
1083
1084 def update(ui, repo, node=None, merge=False, clean=False):
1084 def update(ui, repo, node=None, merge=False, clean=False):
1085 '''update or merge working directory
1085 '''update or merge working directory
1086
1086
1087 If there are no outstanding changes in the working directory and
1087 If there are no outstanding changes in the working directory and
1088 there is a linear relationship between the current version and the
1088 there is a linear relationship between the current version and the
1089 requested version, the result is the requested version.
1089 requested version, the result is the requested version.
1090
1090
1091 Otherwise the result is a merge between the contents of the
1091 Otherwise the result is a merge between the contents of the
1092 current working directory and the requested version. Files that
1092 current working directory and the requested version. Files that
1093 changed between either parent are marked as changed for the next
1093 changed between either parent are marked as changed for the next
1094 commit and a commit must be performed before any further updates
1094 commit and a commit must be performed before any further updates
1095 are allowed.
1095 are allowed.
1096 '''
1096 '''
1097 node = node and repo.lookup(node) or repo.changelog.tip()
1097 node = node and repo.lookup(node) or repo.changelog.tip()
1098 return repo.update(node, allow=merge, force=clean)
1098 return repo.update(node, allow=merge, force=clean)
1099
1099
1100 def verify(ui, repo):
1100 def verify(ui, repo):
1101 """verify the integrity of the repository"""
1101 """verify the integrity of the repository"""
1102 return repo.verify()
1102 return repo.verify()
1103
1103
1104 # Command options and aliases are listed here, alphabetically
1104 # Command options and aliases are listed here, alphabetically
1105
1105
1106 table = {
1106 table = {
1107 "^add": (add,
1107 "^add": (add,
1108 [('I', 'include', [], 'include path in search'),
1108 [('I', 'include', [], 'include path in search'),
1109 ('X', 'exclude', [], 'exclude path from search')],
1109 ('X', 'exclude', [], 'exclude path from search')],
1110 "hg add [FILE]..."),
1110 "hg add [FILE]..."),
1111 "addremove": (addremove,
1111 "addremove": (addremove,
1112 [('I', 'include', [], 'include path in search'),
1112 [('I', 'include', [], 'include path in search'),
1113 ('X', 'exclude', [], 'exclude path from search')],
1113 ('X', 'exclude', [], 'exclude path from search')],
1114 "hg addremove [OPTION]... [FILE]..."),
1114 "hg addremove [OPTION]... [FILE]..."),
1115 "^annotate":
1115 "^annotate":
1116 (annotate,
1116 (annotate,
1117 [('r', 'rev', '', 'revision'),
1117 [('r', 'rev', '', 'revision'),
1118 ('u', 'user', None, 'show user'),
1118 ('u', 'user', None, 'show user'),
1119 ('n', 'number', None, 'show revision number'),
1119 ('n', 'number', None, 'show revision number'),
1120 ('c', 'changeset', None, 'show changeset'),
1120 ('c', 'changeset', None, 'show changeset'),
1121 ('I', 'include', [], 'include path in search'),
1121 ('I', 'include', [], 'include path in search'),
1122 ('X', 'exclude', [], 'exclude path from search')],
1122 ('X', 'exclude', [], 'exclude path from search')],
1123 'hg annotate [-r REV] [-u] [-n] [-c] FILE...'),
1123 'hg annotate [-r REV] [-u] [-n] [-c] FILE...'),
1124 "cat":
1124 "cat":
1125 (cat,
1125 (cat,
1126 [('o', 'output', "", 'output to file')],
1126 [('o', 'output', "", 'output to file')],
1127 'hg cat [-o OUTFILE] FILE [REV]'),
1127 'hg cat [-o OUTFILE] FILE [REV]'),
1128 "^clone":
1128 "^clone":
1129 (clone,
1129 (clone,
1130 [('U', 'noupdate', None, 'skip update after cloning')],
1130 [('U', 'noupdate', None, 'skip update after cloning')],
1131 'hg clone [-U] SOURCE [DEST]'),
1131 'hg clone [-U] SOURCE [DEST]'),
1132 "^commit|ci":
1132 "^commit|ci":
1133 (commit,
1133 (commit,
1134 [('A', 'addremove', None, 'run add/remove during commit'),
1134 [('A', 'addremove', None, 'run add/remove during commit'),
1135 ('m', 'message', "", 'commit message'),
1135 ('m', 'message', "", 'commit message'),
1136 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1136 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1137 ('l', 'logfile', "", 'commit message file'),
1137 ('l', 'logfile', "", 'commit message file'),
1138 ('d', 'date', "", 'date code'),
1138 ('d', 'date', "", 'date code'),
1139 ('u', 'user', "", 'user')],
1139 ('u', 'user', "", 'user')],
1140 'hg commit [OPTION]... [FILE]...'),
1140 'hg commit [OPTION]... [FILE]...'),
1141 "copy": (copy, [], 'hg copy SOURCE DEST'),
1141 "copy": (copy, [], 'hg copy SOURCE DEST'),
1142 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1142 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1143 "debugstate": (debugstate, [], 'debugstate'),
1143 "debugstate": (debugstate, [], 'debugstate'),
1144 "debugindex": (debugindex, [], 'debugindex FILE'),
1144 "debugindex": (debugindex, [], 'debugindex FILE'),
1145 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1145 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1146 "^diff":
1146 "^diff":
1147 (diff,
1147 (diff,
1148 [('r', 'rev', [], 'revision'),
1148 [('r', 'rev', [], 'revision'),
1149 ('I', 'include', [], 'include path in search'),
1149 ('I', 'include', [], 'include path in search'),
1150 ('X', 'exclude', [], 'exclude path from search')],
1150 ('X', 'exclude', [], 'exclude path from search')],
1151 'hg diff [-r REV1 [-r REV2]] [FILE]...'),
1151 'hg diff [-r REV1 [-r REV2]] [FILE]...'),
1152 "^export":
1152 "^export":
1153 (export,
1153 (export,
1154 [('o', 'output', "", 'output to file')],
1154 [('o', 'output', "", 'output to file')],
1155 "hg export [-o OUTFILE] REV..."),
1155 "hg export [-o OUTFILE] REV..."),
1156 "forget": (forget, [], "hg forget FILE..."),
1156 "forget": (forget, [], "hg forget FILE..."),
1157 "heads": (heads, [], 'hg heads'),
1157 "heads": (heads, [], 'hg heads'),
1158 "help": (help_, [], 'hg help [COMMAND]'),
1158 "help": (help_, [], 'hg help [COMMAND]'),
1159 "identify|id": (identify, [], 'hg identify'),
1159 "identify|id": (identify, [], 'hg identify'),
1160 "import|patch":
1160 "import|patch":
1161 (import_,
1161 (import_,
1162 [('p', 'strip', 1, 'path strip'),
1162 [('p', 'strip', 1, 'path strip'),
1163 ('b', 'base', "", 'base path')],
1163 ('b', 'base', "", 'base path')],
1164 "hg import [-p NUM] [-b BASE] PATCH..."),
1164 "hg import [-p NUM] [-b BASE] PATCH..."),
1165 "^init": (init, [], 'hg init'),
1165 "^init": (init, [], 'hg init'),
1166 "locate":
1166 "locate":
1167 (locate,
1167 (locate,
1168 [('r', 'rev', '', 'revision'),
1168 [('r', 'rev', '', 'revision'),
1169 ('0', 'print0', None, 'end records with NUL'),
1169 ('0', 'print0', None, 'end records with NUL'),
1170 ('f', 'fullpath', None, 'print complete paths'),
1170 ('f', 'fullpath', None, 'print complete paths'),
1171 ('I', 'include', [], 'include path in search'),
1171 ('I', 'include', [], 'include path in search'),
1172 ('X', 'exclude', [], 'exclude path from search')],
1172 ('X', 'exclude', [], 'exclude path from search')],
1173 'hg locate [-r REV] [-f] [-0] [PATTERN]...'),
1173 'hg locate [-r REV] [-f] [-0] [PATTERN]...'),
1174 "^log|history":
1174 "^log|history":
1175 (log,
1175 (log,
1176 [('r', 'rev', [], 'revision'),
1176 [('r', 'rev', [], 'revision'),
1177 ('p', 'patch', None, 'show patch')],
1177 ('p', 'patch', None, 'show patch')],
1178 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1178 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1179 "manifest": (manifest, [], 'hg manifest [REV]'),
1179 "manifest": (manifest, [], 'hg manifest [REV]'),
1180 "parents": (parents, [], 'hg parents [REV]'),
1180 "parents": (parents, [], 'hg parents [REV]'),
1181 "paths": (paths, [], 'hg paths [name]'),
1181 "paths": (paths, [], 'hg paths [name]'),
1182 "^pull":
1182 "^pull":
1183 (pull,
1183 (pull,
1184 [('u', 'update', None, 'update working directory')],
1184 [('u', 'update', None, 'update working directory')],
1185 'hg pull [-u] [SOURCE]'),
1185 'hg pull [-u] [SOURCE]'),
1186 "^push": (push, [], 'hg push [DEST]'),
1186 "^push": (push, [], 'hg push [DEST]'),
1187 "rawcommit":
1187 "rawcommit":
1188 (rawcommit,
1188 (rawcommit,
1189 [('p', 'parent', [], 'parent'),
1189 [('p', 'parent', [], 'parent'),
1190 ('d', 'date', "", 'date code'),
1190 ('d', 'date', "", 'date code'),
1191 ('u', 'user', "", 'user'),
1191 ('u', 'user', "", 'user'),
1192 ('F', 'files', "", 'file list'),
1192 ('F', 'files', "", 'file list'),
1193 ('m', 'message', "", 'commit message'),
1193 ('m', 'message', "", 'commit message'),
1194 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1194 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1195 ('l', 'logfile', "", 'commit message file')],
1195 ('l', 'logfile', "", 'commit message file')],
1196 'hg rawcommit [OPTION]... [FILE]...'),
1196 'hg rawcommit [OPTION]... [FILE]...'),
1197 "recover": (recover, [], "hg recover"),
1197 "recover": (recover, [], "hg recover"),
1198 "^remove|rm": (remove, [], "hg remove FILE..."),
1198 "^remove|rm": (remove, [], "hg remove FILE..."),
1199 "^revert":
1199 "^revert":
1200 (revert,
1200 (revert,
1201 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1201 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1202 ("r", "rev", "", "revision")],
1202 ("r", "rev", "", "revision")],
1203 "hg revert [-n] [-r REV] [NAME]..."),
1203 "hg revert [-n] [-r REV] [NAME]..."),
1204 "root": (root, [], "hg root"),
1204 "root": (root, [], "hg root"),
1205 "^serve":
1205 "^serve":
1206 (serve,
1206 (serve,
1207 [('A', 'accesslog', '', 'access log file'),
1207 [('A', 'accesslog', '', 'access log file'),
1208 ('E', 'errorlog', '', 'error log file'),
1208 ('E', 'errorlog', '', 'error log file'),
1209 ('p', 'port', 8000, 'listen port'),
1209 ('p', 'port', 8000, 'listen port'),
1210 ('a', 'address', '', 'interface address'),
1210 ('a', 'address', '', 'interface address'),
1211 ('n', 'name', os.getcwd(), 'repository name'),
1211 ('n', 'name', os.getcwd(), 'repository name'),
1212 ('', 'stdio', None, 'for remote clients'),
1212 ('', 'stdio', None, 'for remote clients'),
1213 ('t', 'templates', "", 'template map')],
1213 ('t', 'templates', "", 'template map')],
1214 "hg serve [OPTION]..."),
1214 "hg serve [OPTION]..."),
1215 "^status": (status,
1215 "^status": (status,
1216 [('I', 'include', [], 'include path in search'),
1216 [('I', 'include', [], 'include path in search'),
1217 ('X', 'exclude', [], 'exclude path from search')],
1217 ('X', 'exclude', [], 'exclude path from search')],
1218 'hg status [FILE]...'),
1218 'hg status [FILE]...'),
1219 "tag":
1219 "tag":
1220 (tag,
1220 (tag,
1221 [('l', 'local', None, 'make the tag local'),
1221 [('l', 'local', None, 'make the tag local'),
1222 ('m', 'message', "", 'commit message'),
1222 ('m', 'message', "", 'commit message'),
1223 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1223 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1224 ('d', 'date', "", 'date code'),
1224 ('d', 'date', "", 'date code'),
1225 ('u', 'user', "", 'user')],
1225 ('u', 'user', "", 'user')],
1226 'hg tag [OPTION]... NAME [REV]'),
1226 'hg tag [OPTION]... NAME [REV]'),
1227 "tags": (tags, [], 'hg tags'),
1227 "tags": (tags, [], 'hg tags'),
1228 "tip": (tip, [], 'hg tip'),
1228 "tip": (tip, [], 'hg tip'),
1229 "undo": (undo, [], 'hg undo'),
1229 "undo": (undo, [], 'hg undo'),
1230 "^update|up|checkout|co":
1230 "^update|up|checkout|co":
1231 (update,
1231 (update,
1232 [('m', 'merge', None, 'allow merging of conflicts'),
1232 [('m', 'merge', None, 'allow merging of conflicts'),
1233 ('C', 'clean', None, 'overwrite locally modified files')],
1233 ('C', 'clean', None, 'overwrite locally modified files')],
1234 'hg update [-m] [-C] [REV]'),
1234 'hg update [-m] [-C] [REV]'),
1235 "verify": (verify, [], 'hg verify'),
1235 "verify": (verify, [], 'hg verify'),
1236 "version": (show_version, [], 'hg version'),
1236 "version": (show_version, [], 'hg version'),
1237 }
1237 }
1238
1238
1239 globalopts = [('v', 'verbose', None, 'verbose'),
1239 globalopts = [('v', 'verbose', None, 'verbose'),
1240 ('', 'debug', None, 'debug'),
1240 ('', 'debug', None, 'debug'),
1241 ('q', 'quiet', None, 'quiet'),
1241 ('q', 'quiet', None, 'quiet'),
1242 ('', 'profile', None, 'profile'),
1242 ('', 'profile', None, 'profile'),
1243 ('R', 'repository', "", 'repository root directory'),
1243 ('R', 'repository', "", 'repository root directory'),
1244 ('', 'traceback', None, 'print traceback on exception'),
1244 ('', 'traceback', None, 'print traceback on exception'),
1245 ('y', 'noninteractive', None, 'run non-interactively'),
1245 ('y', 'noninteractive', None, 'run non-interactively'),
1246 ('', 'version', None, 'output version information and exit'),
1246 ('', 'version', None, 'output version information and exit'),
1247 ('', 'time', None, 'time how long the command takes'),
1247 ]
1248 ]
1248
1249
1249 norepo = "clone init version help debugindex debugindexdot"
1250 norepo = "clone init version help debugindex debugindexdot"
1250
1251
1251 def find(cmd):
1252 def find(cmd):
1252 for e in table.keys():
1253 for e in table.keys():
1253 if re.match("(%s)$" % e, cmd):
1254 if re.match("(%s)$" % e, cmd):
1254 return table[e]
1255 return table[e]
1255
1256
1256 raise UnknownCommand(cmd)
1257 raise UnknownCommand(cmd)
1257
1258
1258 class SignalInterrupt(Exception):
1259 class SignalInterrupt(Exception):
1259 """Exception raised on SIGTERM and SIGHUP."""
1260 """Exception raised on SIGTERM and SIGHUP."""
1260
1261
1261 def catchterm(*args):
1262 def catchterm(*args):
1262 raise SignalInterrupt
1263 raise SignalInterrupt
1263
1264
1264 def run():
1265 def run():
1265 sys.exit(dispatch(sys.argv[1:]))
1266 sys.exit(dispatch(sys.argv[1:]))
1266
1267
1267 class ParseError(Exception):
1268 class ParseError(Exception):
1268 """Exception raised on errors in parsing the command line."""
1269 """Exception raised on errors in parsing the command line."""
1269
1270
1270 def parse(args):
1271 def parse(args):
1271 options = {}
1272 options = {}
1272 cmdoptions = {}
1273 cmdoptions = {}
1273
1274
1274 try:
1275 try:
1275 args = fancyopts.fancyopts(args, globalopts, options)
1276 args = fancyopts.fancyopts(args, globalopts, options)
1276 except fancyopts.getopt.GetoptError, inst:
1277 except fancyopts.getopt.GetoptError, inst:
1277 raise ParseError(None, inst)
1278 raise ParseError(None, inst)
1278
1279
1279 if options["version"]:
1280 if options["version"]:
1280 return ("version", show_version, [], options, cmdoptions)
1281 return ("version", show_version, [], options, cmdoptions)
1281 elif not args:
1282 elif not args:
1282 return ("help", help_, [], options, cmdoptions)
1283 return ("help", help_, [], options, cmdoptions)
1283 else:
1284 else:
1284 cmd, args = args[0], args[1:]
1285 cmd, args = args[0], args[1:]
1285
1286
1286 i = find(cmd)
1287 i = find(cmd)
1287
1288
1288 # combine global options into local
1289 # combine global options into local
1289 c = list(i[1])
1290 c = list(i[1])
1290 for o in globalopts:
1291 for o in globalopts:
1291 c.append((o[0], o[1], options[o[1]], o[3]))
1292 c.append((o[0], o[1], options[o[1]], o[3]))
1292
1293
1293 try:
1294 try:
1294 args = fancyopts.fancyopts(args, c, cmdoptions)
1295 args = fancyopts.fancyopts(args, c, cmdoptions)
1295 except fancyopts.getopt.GetoptError, inst:
1296 except fancyopts.getopt.GetoptError, inst:
1296 raise ParseError(cmd, inst)
1297 raise ParseError(cmd, inst)
1297
1298
1298 # separate global options back out
1299 # separate global options back out
1299 for o in globalopts:
1300 for o in globalopts:
1300 n = o[1]
1301 n = o[1]
1301 options[n] = cmdoptions[n]
1302 options[n] = cmdoptions[n]
1302 del cmdoptions[n]
1303 del cmdoptions[n]
1303
1304
1304 return (cmd, i[0], args, options, cmdoptions)
1305 return (cmd, i[0], args, options, cmdoptions)
1305
1306
1306 def dispatch(args):
1307 def dispatch(args):
1307 signal.signal(signal.SIGTERM, catchterm)
1308 signal.signal(signal.SIGTERM, catchterm)
1308 try:
1309 try:
1309 signal.signal(signal.SIGHUP, catchterm)
1310 signal.signal(signal.SIGHUP, catchterm)
1310 except AttributeError:
1311 except AttributeError:
1311 pass
1312 pass
1312
1313
1313 try:
1314 try:
1314 cmd, func, args, options, cmdoptions = parse(args)
1315 cmd, func, args, options, cmdoptions = parse(args)
1315 except ParseError, inst:
1316 except ParseError, inst:
1316 u = ui.ui()
1317 u = ui.ui()
1317 if inst.args[0]:
1318 if inst.args[0]:
1318 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1319 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1319 help_(u, inst.args[0])
1320 help_(u, inst.args[0])
1320 else:
1321 else:
1321 u.warn("hg: %s\n" % inst.args[1])
1322 u.warn("hg: %s\n" % inst.args[1])
1322 help_(u)
1323 help_(u)
1323 sys.exit(-1)
1324 sys.exit(-1)
1324 except UnknownCommand, inst:
1325 except UnknownCommand, inst:
1325 u = ui.ui()
1326 u = ui.ui()
1326 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1327 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1327 help_(u)
1328 help_(u)
1328 sys.exit(1)
1329 sys.exit(1)
1329
1330
1331 if options["time"]:
1332 def get_times():
1333 t = os.times()
1334 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1335 t = (t[0], t[1], t[2], t[3], time.clock())
1336 return t
1337 s = get_times()
1338 def print_time():
1339 t = get_times()
1340 u = ui.ui()
1341 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1342 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1343 atexit.register(print_time)
1344
1330 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1345 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1331 not options["noninteractive"])
1346 not options["noninteractive"])
1332
1347
1333 try:
1348 try:
1334 try:
1349 try:
1335 if cmd not in norepo.split():
1350 if cmd not in norepo.split():
1336 path = options["repository"] or ""
1351 path = options["repository"] or ""
1337 repo = hg.repository(ui=u, path=path)
1352 repo = hg.repository(ui=u, path=path)
1338 d = lambda: func(u, repo, *args, **cmdoptions)
1353 d = lambda: func(u, repo, *args, **cmdoptions)
1339 else:
1354 else:
1340 d = lambda: func(u, *args, **cmdoptions)
1355 d = lambda: func(u, *args, **cmdoptions)
1341
1356
1342 if options['profile']:
1357 if options['profile']:
1343 import hotshot, hotshot.stats
1358 import hotshot, hotshot.stats
1344 prof = hotshot.Profile("hg.prof")
1359 prof = hotshot.Profile("hg.prof")
1345 r = prof.runcall(d)
1360 r = prof.runcall(d)
1346 prof.close()
1361 prof.close()
1347 stats = hotshot.stats.load("hg.prof")
1362 stats = hotshot.stats.load("hg.prof")
1348 stats.strip_dirs()
1363 stats.strip_dirs()
1349 stats.sort_stats('time', 'calls')
1364 stats.sort_stats('time', 'calls')
1350 stats.print_stats(40)
1365 stats.print_stats(40)
1351 return r
1366 return r
1352 else:
1367 else:
1353 return d()
1368 return d()
1354 except:
1369 except:
1355 if options['traceback']:
1370 if options['traceback']:
1356 traceback.print_exc()
1371 traceback.print_exc()
1357 raise
1372 raise
1358 except util.CommandError, inst:
1373 except util.CommandError, inst:
1359 u.warn("abort: %s\n" % inst.args)
1374 u.warn("abort: %s\n" % inst.args)
1360 except hg.RepoError, inst:
1375 except hg.RepoError, inst:
1361 u.warn("abort: ", inst, "!\n")
1376 u.warn("abort: ", inst, "!\n")
1362 except SignalInterrupt:
1377 except SignalInterrupt:
1363 u.warn("killed!\n")
1378 u.warn("killed!\n")
1364 except KeyboardInterrupt:
1379 except KeyboardInterrupt:
1365 u.warn("interrupted!\n")
1380 u.warn("interrupted!\n")
1366 except IOError, inst:
1381 except IOError, inst:
1367 if hasattr(inst, "code"):
1382 if hasattr(inst, "code"):
1368 u.warn("abort: %s\n" % inst)
1383 u.warn("abort: %s\n" % inst)
1369 elif hasattr(inst, "reason"):
1384 elif hasattr(inst, "reason"):
1370 u.warn("abort: error: %s\n" % inst.reason[1])
1385 u.warn("abort: error: %s\n" % inst.reason[1])
1371 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1386 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1372 if u.debugflag: u.warn("broken pipe\n")
1387 if u.debugflag: u.warn("broken pipe\n")
1373 else:
1388 else:
1374 raise
1389 raise
1375 except OSError, inst:
1390 except OSError, inst:
1376 if hasattr(inst, "filename"):
1391 if hasattr(inst, "filename"):
1377 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1392 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1378 else:
1393 else:
1379 u.warn("abort: %s\n" % inst.strerror)
1394 u.warn("abort: %s\n" % inst.strerror)
1380 except Abort, inst:
1395 except Abort, inst:
1381 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1396 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1382 sys.exit(1)
1397 sys.exit(1)
1383 except TypeError, inst:
1398 except TypeError, inst:
1384 # was this an argument error?
1399 # was this an argument error?
1385 tb = traceback.extract_tb(sys.exc_info()[2])
1400 tb = traceback.extract_tb(sys.exc_info()[2])
1386 if len(tb) > 2: # no
1401 if len(tb) > 2: # no
1387 raise
1402 raise
1388 u.debug(inst, "\n")
1403 u.debug(inst, "\n")
1389 u.warn("%s: invalid arguments\n" % cmd)
1404 u.warn("%s: invalid arguments\n" % cmd)
1390 help_(u, cmd)
1405 help_(u, cmd)
1391
1406
1392 sys.exit(-1)
1407 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now