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