##// END OF EJS Templates
Unbreak dirstate debug commands...
mpm@selenic.com -
r555:39a1cfb0 default
parent child Browse files
Show More
@@ -1,942 +1,944 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 import os, re, sys, signal
8 import os, re, sys, signal
9 import fancyopts, ui, hg, util
9 import fancyopts, ui, hg, util
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "mdiff time hgweb traceback random signal errno version")
11 demandload(globals(), "mdiff time hgweb traceback random signal errno version")
12
12
13 class UnknownCommand(Exception): pass
13 class UnknownCommand(Exception): pass
14
14
15 def filterfiles(filters, files):
15 def filterfiles(filters, files):
16 l = [ x for x in files if x in filters ]
16 l = [ x for x in files if x in filters ]
17
17
18 for t in filters:
18 for t in filters:
19 if t and t[-1] != "/": t += "/"
19 if t and t[-1] != "/": t += "/"
20 l += [ x for x in files if x.startswith(t) ]
20 l += [ x for x in files if x.startswith(t) ]
21 return l
21 return l
22
22
23 def relfilter(repo, files):
23 def relfilter(repo, files):
24 if os.getcwd() != repo.root:
24 if os.getcwd() != repo.root:
25 p = os.getcwd()[len(repo.root) + 1: ]
25 p = os.getcwd()[len(repo.root) + 1: ]
26 return filterfiles([util.pconvert(p)], files)
26 return filterfiles([util.pconvert(p)], files)
27 return files
27 return files
28
28
29 def relpath(repo, args):
29 def relpath(repo, args):
30 if os.getcwd() != repo.root:
30 if os.getcwd() != repo.root:
31 p = os.getcwd()[len(repo.root) + 1: ]
31 p = os.getcwd()[len(repo.root) + 1: ]
32 return [ util.pconvert(os.path.normpath(os.path.join(p, x)))
32 return [ util.pconvert(os.path.normpath(os.path.join(p, x)))
33 for x in args ]
33 for x in args ]
34 return args
34 return args
35
35
36 def dodiff(ui, repo, files = None, node1 = None, node2 = None):
36 def dodiff(ui, repo, files = None, node1 = None, node2 = None):
37 def date(c):
37 def date(c):
38 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
38 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
39
39
40 (c, a, d, u) = repo.changes(None, node1, files)
40 (c, a, d, u) = repo.changes(None, node1, files)
41 if files:
41 if files:
42 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
42 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
43
43
44 if not c and not a and not d:
44 if not c and not a and not d:
45 return
45 return
46
46
47 if node2:
47 if node2:
48 change = repo.changelog.read(node2)
48 change = repo.changelog.read(node2)
49 mmap2 = repo.manifest.read(change[0])
49 mmap2 = repo.manifest.read(change[0])
50 def read(f): return repo.file(f).read(mmap2[f])
50 def read(f): return repo.file(f).read(mmap2[f])
51 date2 = date(change)
51 date2 = date(change)
52 else:
52 else:
53 date2 = time.asctime()
53 date2 = time.asctime()
54 if not node1:
54 if not node1:
55 node1 = repo.dirstate.parents()[0]
55 node1 = repo.dirstate.parents()[0]
56 def read(f): return repo.wfile(f).read()
56 def read(f): return repo.wfile(f).read()
57
57
58 if ui.quiet:
58 if ui.quiet:
59 r = None
59 r = None
60 else:
60 else:
61 hexfunc = ui.verbose and hg.hex or hg.short
61 hexfunc = ui.verbose and hg.hex or hg.short
62 r = [hexfunc(node) for node in [node1, node2] if node]
62 r = [hexfunc(node) for node in [node1, node2] if node]
63
63
64 change = repo.changelog.read(node1)
64 change = repo.changelog.read(node1)
65 mmap = repo.manifest.read(change[0])
65 mmap = repo.manifest.read(change[0])
66 date1 = date(change)
66 date1 = date(change)
67
67
68 for f in c:
68 for f in c:
69 to = None
69 to = None
70 if f in mmap:
70 if f in mmap:
71 to = repo.file(f).read(mmap[f])
71 to = repo.file(f).read(mmap[f])
72 tn = read(f)
72 tn = read(f)
73 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
73 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
74 for f in a:
74 for f in a:
75 to = None
75 to = None
76 tn = read(f)
76 tn = read(f)
77 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
77 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
78 for f in d:
78 for f in d:
79 to = repo.file(f).read(mmap[f])
79 to = repo.file(f).read(mmap[f])
80 tn = None
80 tn = None
81 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
81 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
82
82
83 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
83 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
84 """show a single changeset or file revision"""
84 """show a single changeset or file revision"""
85 changelog = repo.changelog
85 changelog = repo.changelog
86 if filelog:
86 if filelog:
87 log = filelog
87 log = filelog
88 filerev = rev
88 filerev = rev
89 node = filenode = filelog.node(filerev)
89 node = filenode = filelog.node(filerev)
90 changerev = filelog.linkrev(filenode)
90 changerev = filelog.linkrev(filenode)
91 changenode = changenode or changelog.node(changerev)
91 changenode = changenode or changelog.node(changerev)
92 else:
92 else:
93 log = changelog
93 log = changelog
94 changerev = rev
94 changerev = rev
95 if changenode is None:
95 if changenode is None:
96 changenode = changelog.node(changerev)
96 changenode = changelog.node(changerev)
97 elif not changerev:
97 elif not changerev:
98 rev = changerev = changelog.rev(changenode)
98 rev = changerev = changelog.rev(changenode)
99 node = changenode
99 node = changenode
100
100
101 if ui.quiet:
101 if ui.quiet:
102 ui.write("%d:%s\n" % (rev, hg.hex(node)))
102 ui.write("%d:%s\n" % (rev, hg.hex(node)))
103 return
103 return
104
104
105 changes = changelog.read(changenode)
105 changes = changelog.read(changenode)
106
106
107 parents = [(log.rev(parent), hg.hex(parent))
107 parents = [(log.rev(parent), hg.hex(parent))
108 for parent in log.parents(node)
108 for parent in log.parents(node)
109 if ui.debugflag or parent != hg.nullid]
109 if ui.debugflag or parent != hg.nullid]
110 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
110 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
111 parents = []
111 parents = []
112
112
113 if filelog:
113 if filelog:
114 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
114 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
115 for parent in parents:
115 for parent in parents:
116 ui.write("parent: %d:%s\n" % parent)
116 ui.write("parent: %d:%s\n" % parent)
117 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
117 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
118 else:
118 else:
119 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
119 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
120 for tag in repo.nodetags(changenode):
120 for tag in repo.nodetags(changenode):
121 ui.status("tag: %s\n" % tag)
121 ui.status("tag: %s\n" % tag)
122 for parent in parents:
122 for parent in parents:
123 ui.write("parent: %d:%s\n" % parent)
123 ui.write("parent: %d:%s\n" % parent)
124 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
124 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
125 hg.hex(changes[0])))
125 hg.hex(changes[0])))
126 ui.status("user: %s\n" % changes[1])
126 ui.status("user: %s\n" % changes[1])
127 ui.status("date: %s\n" % time.asctime(
127 ui.status("date: %s\n" % time.asctime(
128 time.localtime(float(changes[2].split(' ')[0]))))
128 time.localtime(float(changes[2].split(' ')[0]))))
129 if ui.debugflag:
129 if ui.debugflag:
130 files = repo.changes(changelog.parents(changenode)[0], changenode)
130 files = repo.changes(changelog.parents(changenode)[0], changenode)
131 for key, value in zip(["files:", "files+:", "files-:"], files):
131 for key, value in zip(["files:", "files+:", "files-:"], files):
132 if value:
132 if value:
133 ui.note("%-12s %s\n" % (key, " ".join(value)))
133 ui.note("%-12s %s\n" % (key, " ".join(value)))
134 else:
134 else:
135 ui.note("files: %s\n" % " ".join(changes[3]))
135 ui.note("files: %s\n" % " ".join(changes[3]))
136 description = changes[4].strip()
136 description = changes[4].strip()
137 if description:
137 if description:
138 if ui.verbose:
138 if ui.verbose:
139 ui.status("description:\n")
139 ui.status("description:\n")
140 ui.status(description)
140 ui.status(description)
141 ui.status("\n\n")
141 ui.status("\n\n")
142 else:
142 else:
143 ui.status("summary: %s\n" % description.splitlines()[0])
143 ui.status("summary: %s\n" % description.splitlines()[0])
144 ui.status("\n")
144 ui.status("\n")
145
145
146 def show_version(ui):
146 def show_version(ui):
147 """output version and copyright information"""
147 """output version and copyright information"""
148 ui.write("Mercurial version %s\n" % version.get_version())
148 ui.write("Mercurial version %s\n" % version.get_version())
149 ui.status(
149 ui.status(
150 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
150 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
151 "This is free software; see the source for copying conditions. "
151 "This is free software; see the source for copying conditions. "
152 "There is NO\nwarranty; "
152 "There is NO\nwarranty; "
153 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
153 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
154 )
154 )
155
155
156 def help(ui, cmd=None):
156 def help(ui, cmd=None):
157 '''show help for a given command or all commands'''
157 '''show help for a given command or all commands'''
158 if cmd:
158 if cmd:
159 try:
159 try:
160 i = find(cmd)
160 i = find(cmd)
161 ui.write("%s\n\n" % i[2])
161 ui.write("%s\n\n" % i[2])
162
162
163 if i[1]:
163 if i[1]:
164 for s, l, d, c in i[1]:
164 for s, l, d, c in i[1]:
165 opt=' '
165 opt=' '
166 if s: opt = opt + '-' + s + ' '
166 if s: opt = opt + '-' + s + ' '
167 if l: opt = opt + '--' + l + ' '
167 if l: opt = opt + '--' + l + ' '
168 if d: opt = opt + '(' + str(d) + ')'
168 if d: opt = opt + '(' + str(d) + ')'
169 ui.write(opt, "\n")
169 ui.write(opt, "\n")
170 if c: ui.write(' %s\n' % c)
170 if c: ui.write(' %s\n' % c)
171 ui.write("\n")
171 ui.write("\n")
172
172
173 ui.write(i[0].__doc__, "\n")
173 ui.write(i[0].__doc__, "\n")
174 except UnknownCommand:
174 except UnknownCommand:
175 ui.warn("hg: unknown command %s\n" % cmd)
175 ui.warn("hg: unknown command %s\n" % cmd)
176 sys.exit(0)
176 sys.exit(0)
177 else:
177 else:
178 if not ui.quiet:
178 if not ui.quiet:
179 show_version(ui)
179 show_version(ui)
180 ui.write('\n')
180 ui.write('\n')
181 ui.write('hg commands:\n\n')
181 ui.write('hg commands:\n\n')
182
182
183 h = {}
183 h = {}
184 for c, e in table.items():
184 for c, e in table.items():
185 f = c.split("|")[0]
185 f = c.split("|")[0]
186 if f.startswith("debug"):
186 if f.startswith("debug"):
187 continue
187 continue
188 d = ""
188 d = ""
189 if e[0].__doc__:
189 if e[0].__doc__:
190 d = e[0].__doc__.splitlines(0)[0].rstrip()
190 d = e[0].__doc__.splitlines(0)[0].rstrip()
191 h[f] = d
191 h[f] = d
192
192
193 fns = h.keys()
193 fns = h.keys()
194 fns.sort()
194 fns.sort()
195 m = max(map(len, fns))
195 m = max(map(len, fns))
196 for f in fns:
196 for f in fns:
197 ui.write(' %-*s %s\n' % (m, f, h[f]))
197 ui.write(' %-*s %s\n' % (m, f, h[f]))
198
198
199 # Commands start here, listed alphabetically
199 # Commands start here, listed alphabetically
200
200
201 def add(ui, repo, file, *files):
201 def add(ui, repo, file, *files):
202 '''add the specified files on the next commit'''
202 '''add the specified files on the next commit'''
203 repo.add(relpath(repo, (file,) + files))
203 repo.add(relpath(repo, (file,) + files))
204
204
205 def addremove(ui, repo, *files):
205 def addremove(ui, repo, *files):
206 """add all new files, delete all missing files"""
206 """add all new files, delete all missing files"""
207 if files:
207 if files:
208 files = relpath(repo, files)
208 files = relpath(repo, files)
209 d = []
209 d = []
210 u = []
210 u = []
211 for f in files:
211 for f in files:
212 p = repo.wjoin(f)
212 p = repo.wjoin(f)
213 s = repo.dirstate.state(f)
213 s = repo.dirstate.state(f)
214 isfile = os.path.isfile(p)
214 isfile = os.path.isfile(p)
215 if s != 'r' and not isfile:
215 if s != 'r' and not isfile:
216 d.append(f)
216 d.append(f)
217 elif s not in 'nmai' and isfile:
217 elif s not in 'nmai' and isfile:
218 u.append(f)
218 u.append(f)
219 else:
219 else:
220 (c, a, d, u) = repo.changes(None, None)
220 (c, a, d, u) = repo.changes(None, None)
221 repo.add(u)
221 repo.add(u)
222 repo.remove(d)
222 repo.remove(d)
223
223
224 def annotate(u, repo, file, *files, **ops):
224 def annotate(u, repo, file, *files, **ops):
225 """show changeset information per file line"""
225 """show changeset information per file line"""
226 def getnode(rev):
226 def getnode(rev):
227 return hg.short(repo.changelog.node(rev))
227 return hg.short(repo.changelog.node(rev))
228
228
229 def getname(rev):
229 def getname(rev):
230 try:
230 try:
231 return bcache[rev]
231 return bcache[rev]
232 except KeyError:
232 except KeyError:
233 cl = repo.changelog.read(repo.changelog.node(rev))
233 cl = repo.changelog.read(repo.changelog.node(rev))
234 name = cl[1]
234 name = cl[1]
235 f = name.find('@')
235 f = name.find('@')
236 if f >= 0:
236 if f >= 0:
237 name = name[:f]
237 name = name[:f]
238 f = name.find('<')
238 f = name.find('<')
239 if f >= 0:
239 if f >= 0:
240 name = name[f+1:]
240 name = name[f+1:]
241 bcache[rev] = name
241 bcache[rev] = name
242 return name
242 return name
243
243
244 bcache = {}
244 bcache = {}
245 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
245 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
246 if not ops['user'] and not ops['changeset']:
246 if not ops['user'] and not ops['changeset']:
247 ops['number'] = 1
247 ops['number'] = 1
248
248
249 node = repo.dirstate.parents()[0]
249 node = repo.dirstate.parents()[0]
250 if ops['revision']:
250 if ops['revision']:
251 node = repo.changelog.lookup(ops['revision'])
251 node = repo.changelog.lookup(ops['revision'])
252 change = repo.changelog.read(node)
252 change = repo.changelog.read(node)
253 mmap = repo.manifest.read(change[0])
253 mmap = repo.manifest.read(change[0])
254 for f in relpath(repo, (file,) + files):
254 for f in relpath(repo, (file,) + files):
255 lines = repo.file(f).annotate(mmap[f])
255 lines = repo.file(f).annotate(mmap[f])
256 pieces = []
256 pieces = []
257
257
258 for o, f in opmap:
258 for o, f in opmap:
259 if ops[o]:
259 if ops[o]:
260 l = [ f(n) for n,t in lines ]
260 l = [ f(n) for n,t in lines ]
261 m = max(map(len, l))
261 m = max(map(len, l))
262 pieces.append([ "%*s" % (m, x) for x in l])
262 pieces.append([ "%*s" % (m, x) for x in l])
263
263
264 for p,l in zip(zip(*pieces), lines):
264 for p,l in zip(zip(*pieces), lines):
265 u.write(" ".join(p) + ": " + l[1])
265 u.write(" ".join(p) + ": " + l[1])
266
266
267 def cat(ui, repo, file, rev = []):
267 def cat(ui, repo, file, rev = []):
268 """output the latest or given revision of a file"""
268 """output the latest or given revision of a file"""
269 r = repo.file(relpath(repo, [file])[0])
269 r = repo.file(relpath(repo, [file])[0])
270 n = r.tip()
270 n = r.tip()
271 if rev: n = r.lookup(rev)
271 if rev: n = r.lookup(rev)
272 sys.stdout.write(r.read(n))
272 sys.stdout.write(r.read(n))
273
273
274 def clone(ui, source, dest = None, **opts):
274 def clone(ui, source, dest = None, **opts):
275 """make a copy of an existing repository"""
275 """make a copy of an existing repository"""
276 source = ui.expandpath(source)
276 source = ui.expandpath(source)
277
277
278 if dest is None:
278 if dest is None:
279 dest = os.path.basename(os.path.normpath(source))
279 dest = os.path.basename(os.path.normpath(source))
280
280
281 if os.path.exists(dest):
281 if os.path.exists(dest):
282 ui.warn("abort: destination '%s' already exists\n" % dest)
282 ui.warn("abort: destination '%s' already exists\n" % dest)
283 return 1
283 return 1
284
284
285 class dircleanup:
285 class dircleanup:
286 def __init__(self, dir):
286 def __init__(self, dir):
287 self.dir = dir
287 self.dir = dir
288 os.mkdir(dir)
288 os.mkdir(dir)
289 def close(self):
289 def close(self):
290 self.dir = None
290 self.dir = None
291 def __del__(self):
291 def __del__(self):
292 if self.dir:
292 if self.dir:
293 import shutil
293 import shutil
294 shutil.rmtree(self.dir, True)
294 shutil.rmtree(self.dir, True)
295
295
296 d = dircleanup(dest)
296 d = dircleanup(dest)
297
297
298 link = 0
298 link = 0
299 if not (source.startswith("http://") or
299 if not (source.startswith("http://") or
300 source.startswith("hg://") or
300 source.startswith("hg://") or
301 source.startswith("old-http://")):
301 source.startswith("old-http://")):
302 d1 = os.stat(dest).st_dev
302 d1 = os.stat(dest).st_dev
303 d2 = os.stat(source).st_dev
303 d2 = os.stat(source).st_dev
304 if d1 == d2: link = 1
304 if d1 == d2: link = 1
305
305
306 if link:
306 if link:
307 ui.note("copying by hardlink\n")
307 ui.note("copying by hardlink\n")
308 util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
308 util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
309 try:
309 try:
310 os.remove(os.path.join(dest, ".hg", "dirstate"))
310 os.remove(os.path.join(dest, ".hg", "dirstate"))
311 except: pass
311 except: pass
312
312
313 repo = hg.repository(ui, dest)
313 repo = hg.repository(ui, dest)
314
314
315 else:
315 else:
316 repo = hg.repository(ui, dest, create=1)
316 repo = hg.repository(ui, dest, create=1)
317 other = hg.repository(ui, source)
317 other = hg.repository(ui, source)
318 fetch = repo.findincoming(other)
318 fetch = repo.findincoming(other)
319 if fetch:
319 if fetch:
320 cg = other.changegroup(fetch)
320 cg = other.changegroup(fetch)
321 repo.addchangegroup(cg)
321 repo.addchangegroup(cg)
322
322
323 f = repo.opener("hgrc", "w")
323 f = repo.opener("hgrc", "w")
324 f.write("[paths]\n")
324 f.write("[paths]\n")
325 f.write("default = %s\n" % source)
325 f.write("default = %s\n" % source)
326
326
327 if not opts['noupdate']:
327 if not opts['noupdate']:
328 update(ui, repo)
328 update(ui, repo)
329
329
330 d.close()
330 d.close()
331
331
332 def commit(ui, repo, *files, **opts):
332 def commit(ui, repo, *files, **opts):
333 """commit the specified files or all outstanding changes"""
333 """commit the specified files or all outstanding changes"""
334 text = opts['text']
334 text = opts['text']
335 if not text and opts['logfile']:
335 if not text and opts['logfile']:
336 try: text = open(opts['logfile']).read()
336 try: text = open(opts['logfile']).read()
337 except IOError: pass
337 except IOError: pass
338
338
339 if opts['addremove']:
339 if opts['addremove']:
340 addremove(ui, repo, *files)
340 addremove(ui, repo, *files)
341 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
341 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
342
342
343 def copy(ui, repo, source, dest):
343 def copy(ui, repo, source, dest):
344 """mark a file as copied or renamed for the next commit"""
344 """mark a file as copied or renamed for the next commit"""
345 return repo.copy(*relpath(repo, (source, dest)))
345 return repo.copy(*relpath(repo, (source, dest)))
346
346
347 def debugcheckdirstate(ui, repo):
347 def debugcheckdirstate(ui, repo):
348 parent1, parent2 = repo.dirstate.parents()
348 parent1, parent2 = repo.dirstate.parents()
349 dc = repo.dirstate.dup()
349 repo.dirstate.read()
350 dc = repo.dirstate.map
350 keys = dc.keys()
351 keys = dc.keys()
351 keys.sort()
352 keys.sort()
352 m1n = repo.changelog.read(parent1)[0]
353 m1n = repo.changelog.read(parent1)[0]
353 m2n = repo.changelog.read(parent2)[0]
354 m2n = repo.changelog.read(parent2)[0]
354 m1 = repo.manifest.read(m1n)
355 m1 = repo.manifest.read(m1n)
355 m2 = repo.manifest.read(m2n)
356 m2 = repo.manifest.read(m2n)
356 errors = 0
357 errors = 0
357 for f in dc:
358 for f in dc:
358 state = repo.dirstate.state(f)
359 state = repo.dirstate.state(f)
359 if state in "nr" and f not in m1:
360 if state in "nr" and f not in m1:
360 print "%s in state %s, but not listed in manifest1" % (f, state)
361 print "%s in state %s, but not listed in manifest1" % (f, state)
361 errors += 1
362 errors += 1
362 if state in "a" and f in m1:
363 if state in "a" and f in m1:
363 print "%s in state %s, but also listed in manifest1" % (f, state)
364 print "%s in state %s, but also listed in manifest1" % (f, state)
364 errors += 1
365 errors += 1
365 if state in "m" and f not in m1 and f not in m2:
366 if state in "m" and f not in m1 and f not in m2:
366 print "%s in state %s, but not listed in either manifest" % \
367 print "%s in state %s, but not listed in either manifest" % \
367 (f, state)
368 (f, state)
368 errors += 1
369 errors += 1
369 for f in m1:
370 for f in m1:
370 state = repo.dirstate.state(f)
371 state = repo.dirstate.state(f)
371 if state not in "nrm":
372 if state not in "nrm":
372 print "%s in manifest1, but listed as state %s" % (f, state)
373 print "%s in manifest1, but listed as state %s" % (f, state)
373 errors += 1
374 errors += 1
374 if errors:
375 if errors:
375 print ".hg/dirstate inconsistent with current parent's manifest"
376 print ".hg/dirstate inconsistent with current parent's manifest"
376 sys.exit(1)
377 sys.exit(1)
377
378
378 def debugdumpdirstate(ui, repo):
379 def debugdumpdirstate(ui, repo):
379 dc = repo.dirstate.dup()
380 repo.dirstate.read()
381 dc = repo.dirstate.map
380 keys = dc.keys()
382 keys = dc.keys()
381 keys.sort()
383 keys.sort()
382 for file in keys:
384 for file in keys:
383 print "%s => %c" % (file, dc[file][0])
385 print "%s => %c" % (file, dc[file][0])
384
386
385 def debugindex(ui, file):
387 def debugindex(ui, file):
386 r = hg.revlog(hg.opener(""), file, "")
388 r = hg.revlog(hg.opener(""), file, "")
387 print " rev offset length base linkrev"+\
389 print " rev offset length base linkrev"+\
388 " p1 p2 nodeid"
390 " p1 p2 nodeid"
389 for i in range(r.count()):
391 for i in range(r.count()):
390 e = r.index[i]
392 e = r.index[i]
391 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
393 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
392 i, e[0], e[1], e[2], e[3],
394 i, e[0], e[1], e[2], e[3],
393 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
395 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
394
396
395 def debugindexdot(ui, file):
397 def debugindexdot(ui, file):
396 r = hg.revlog(hg.opener(""), file, "")
398 r = hg.revlog(hg.opener(""), file, "")
397 print "digraph G {"
399 print "digraph G {"
398 for i in range(r.count()):
400 for i in range(r.count()):
399 e = r.index[i]
401 e = r.index[i]
400 print "\t%d -> %d" % (r.rev(e[4]), i)
402 print "\t%d -> %d" % (r.rev(e[4]), i)
401 if e[5] != hg.nullid:
403 if e[5] != hg.nullid:
402 print "\t%d -> %d" % (r.rev(e[5]), i)
404 print "\t%d -> %d" % (r.rev(e[5]), i)
403 print "}"
405 print "}"
404
406
405 def diff(ui, repo, *files, **opts):
407 def diff(ui, repo, *files, **opts):
406 """diff working directory (or selected files)"""
408 """diff working directory (or selected files)"""
407 revs = []
409 revs = []
408 if opts['rev']:
410 if opts['rev']:
409 revs = map(lambda x: repo.lookup(x), opts['rev'])
411 revs = map(lambda x: repo.lookup(x), opts['rev'])
410
412
411 if len(revs) > 2:
413 if len(revs) > 2:
412 ui.warn("too many revisions to diff\n")
414 ui.warn("too many revisions to diff\n")
413 sys.exit(1)
415 sys.exit(1)
414
416
415 if files:
417 if files:
416 files = relpath(repo, files)
418 files = relpath(repo, files)
417 else:
419 else:
418 files = relpath(repo, [""])
420 files = relpath(repo, [""])
419
421
420 dodiff(ui, repo, files, *revs)
422 dodiff(ui, repo, files, *revs)
421
423
422 def export(ui, repo, changeset):
424 def export(ui, repo, changeset):
423 """dump the changeset header and diffs for a revision"""
425 """dump the changeset header and diffs for a revision"""
424 node = repo.lookup(changeset)
426 node = repo.lookup(changeset)
425 prev, other = repo.changelog.parents(node)
427 prev, other = repo.changelog.parents(node)
426 change = repo.changelog.read(node)
428 change = repo.changelog.read(node)
427 print "# HG changeset patch"
429 print "# HG changeset patch"
428 print "# User %s" % change[1]
430 print "# User %s" % change[1]
429 print "# Node ID %s" % hg.hex(node)
431 print "# Node ID %s" % hg.hex(node)
430 print "# Parent %s" % hg.hex(prev)
432 print "# Parent %s" % hg.hex(prev)
431 print
433 print
432 if other != hg.nullid:
434 if other != hg.nullid:
433 print "# Parent %s" % hg.hex(other)
435 print "# Parent %s" % hg.hex(other)
434 print change[4].rstrip()
436 print change[4].rstrip()
435 print
437 print
436
438
437 dodiff(ui, repo, None, prev, node)
439 dodiff(ui, repo, None, prev, node)
438
440
439 def forget(ui, repo, file, *files):
441 def forget(ui, repo, file, *files):
440 """don't add the specified files on the next commit"""
442 """don't add the specified files on the next commit"""
441 repo.forget(relpath(repo, (file,) + files))
443 repo.forget(relpath(repo, (file,) + files))
442
444
443 def heads(ui, repo):
445 def heads(ui, repo):
444 """show current repository heads"""
446 """show current repository heads"""
445 for n in repo.changelog.heads():
447 for n in repo.changelog.heads():
446 show_changeset(ui, repo, changenode=n)
448 show_changeset(ui, repo, changenode=n)
447
449
448 def identify(ui, repo):
450 def identify(ui, repo):
449 """print information about the working copy"""
451 """print information about the working copy"""
450 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
452 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
451 if not parents:
453 if not parents:
452 ui.write("unknown\n")
454 ui.write("unknown\n")
453 return
455 return
454
456
455 hexfunc = ui.verbose and hg.hex or hg.short
457 hexfunc = ui.verbose and hg.hex or hg.short
456 (c, a, d, u) = repo.changes(None, None)
458 (c, a, d, u) = repo.changes(None, None)
457 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
459 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
458 (c or a or d) and "+" or "")]
460 (c or a or d) and "+" or "")]
459
461
460 if not ui.quiet:
462 if not ui.quiet:
461 # multiple tags for a single parent separated by '/'
463 # multiple tags for a single parent separated by '/'
462 parenttags = ['/'.join(tags)
464 parenttags = ['/'.join(tags)
463 for tags in map(repo.nodetags, parents) if tags]
465 for tags in map(repo.nodetags, parents) if tags]
464 # tags for multiple parents separated by ' + '
466 # tags for multiple parents separated by ' + '
465 output.append(' + '.join(parenttags))
467 output.append(' + '.join(parenttags))
466
468
467 ui.write("%s\n" % ' '.join(output))
469 ui.write("%s\n" % ' '.join(output))
468
470
469 def import_(ui, repo, patch1, *patches, **opts):
471 def import_(ui, repo, patch1, *patches, **opts):
470 """import an ordered set of patches"""
472 """import an ordered set of patches"""
471 try:
473 try:
472 import psyco
474 import psyco
473 psyco.full()
475 psyco.full()
474 except:
476 except:
475 pass
477 pass
476
478
477 patches = (patch1,) + patches
479 patches = (patch1,) + patches
478
480
479 d = opts["base"]
481 d = opts["base"]
480 strip = opts["strip"]
482 strip = opts["strip"]
481
483
482 for patch in patches:
484 for patch in patches:
483 ui.status("applying %s\n" % patch)
485 ui.status("applying %s\n" % patch)
484 pf = os.path.join(d, patch)
486 pf = os.path.join(d, patch)
485
487
486 text = ""
488 text = ""
487 for l in file(pf):
489 for l in file(pf):
488 if l[:4] == "--- ": break
490 if l[:4] == "--- ": break
489 text += l
491 text += l
490
492
491 # make sure text isn't empty
493 # make sure text isn't empty
492 if not text: text = "imported patch %s\n" % patch
494 if not text: text = "imported patch %s\n" % patch
493
495
494 f = os.popen("patch -p%d < %s" % (strip, pf))
496 f = os.popen("patch -p%d < %s" % (strip, pf))
495 files = []
497 files = []
496 for l in f.read().splitlines():
498 for l in f.read().splitlines():
497 l.rstrip('\r\n');
499 l.rstrip('\r\n');
498 ui.status("%s\n" % l)
500 ui.status("%s\n" % l)
499 if l[:14] == 'patching file ':
501 if l[:14] == 'patching file ':
500 pf = l[14:]
502 pf = l[14:]
501 if pf not in files:
503 if pf not in files:
502 files.append(pf)
504 files.append(pf)
503 patcherr = f.close()
505 patcherr = f.close()
504 if patcherr:
506 if patcherr:
505 sys.stderr.write("patch failed")
507 sys.stderr.write("patch failed")
506 sys.exit(1)
508 sys.exit(1)
507
509
508 if len(files) > 0:
510 if len(files) > 0:
509 addremove(ui, repo, *files)
511 addremove(ui, repo, *files)
510 repo.commit(files, text)
512 repo.commit(files, text)
511
513
512 def init(ui, source=None):
514 def init(ui, source=None):
513 """create a new repository in the current directory"""
515 """create a new repository in the current directory"""
514
516
515 if source:
517 if source:
516 ui.warn("no longer supported: use \"hg clone\" instead\n")
518 ui.warn("no longer supported: use \"hg clone\" instead\n")
517 sys.exit(1)
519 sys.exit(1)
518 repo = hg.repository(ui, ".", create=1)
520 repo = hg.repository(ui, ".", create=1)
519
521
520 def log(ui, repo, f=None, **opts):
522 def log(ui, repo, f=None, **opts):
521 """show the revision history of the repository or a single file"""
523 """show the revision history of the repository or a single file"""
522 if f:
524 if f:
523 filelog = repo.file(relpath(repo, [f])[0])
525 filelog = repo.file(relpath(repo, [f])[0])
524 log = filelog
526 log = filelog
525 lookup = filelog.lookup
527 lookup = filelog.lookup
526 else:
528 else:
527 filelog = None
529 filelog = None
528 log = repo.changelog
530 log = repo.changelog
529 lookup = repo.lookup
531 lookup = repo.lookup
530 revlist = []
532 revlist = []
531 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
533 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
532 while revs:
534 while revs:
533 if len(revs) == 1:
535 if len(revs) == 1:
534 revlist.append(revs.pop(0))
536 revlist.append(revs.pop(0))
535 else:
537 else:
536 a = revs.pop(0)
538 a = revs.pop(0)
537 b = revs.pop(0)
539 b = revs.pop(0)
538 off = a > b and -1 or 1
540 off = a > b and -1 or 1
539 revlist.extend(range(a, b + off, off))
541 revlist.extend(range(a, b + off, off))
540 for i in revlist or range(log.count() - 1, -1, -1):
542 for i in revlist or range(log.count() - 1, -1, -1):
541 show_changeset(ui, repo, filelog=filelog, rev=i)
543 show_changeset(ui, repo, filelog=filelog, rev=i)
542
544
543 def manifest(ui, repo, rev = []):
545 def manifest(ui, repo, rev = []):
544 """output the latest or given revision of the project manifest"""
546 """output the latest or given revision of the project manifest"""
545 n = repo.manifest.tip()
547 n = repo.manifest.tip()
546 if rev:
548 if rev:
547 n = repo.manifest.lookup(rev)
549 n = repo.manifest.lookup(rev)
548 m = repo.manifest.read(n)
550 m = repo.manifest.read(n)
549 mf = repo.manifest.readflags(n)
551 mf = repo.manifest.readflags(n)
550 files = m.keys()
552 files = m.keys()
551 files.sort()
553 files.sort()
552
554
553 for f in files:
555 for f in files:
554 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
556 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
555
557
556 def parents(ui, repo, node = None):
558 def parents(ui, repo, node = None):
557 '''show the parents of the current working dir'''
559 '''show the parents of the current working dir'''
558 if node:
560 if node:
559 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
561 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
560 else:
562 else:
561 p = repo.dirstate.parents()
563 p = repo.dirstate.parents()
562
564
563 for n in p:
565 for n in p:
564 if n != hg.nullid:
566 if n != hg.nullid:
565 show_changeset(ui, repo, changenode=n)
567 show_changeset(ui, repo, changenode=n)
566
568
567 def pull(ui, repo, source="default", **opts):
569 def pull(ui, repo, source="default", **opts):
568 """pull changes from the specified source"""
570 """pull changes from the specified source"""
569 source = ui.expandpath(source)
571 source = ui.expandpath(source)
570
572
571 ui.status('pulling from %s\n' % (source))
573 ui.status('pulling from %s\n' % (source))
572
574
573 other = hg.repository(ui, source)
575 other = hg.repository(ui, source)
574 fetch = repo.findincoming(other)
576 fetch = repo.findincoming(other)
575 if not fetch:
577 if not fetch:
576 ui.status("no changes found\n")
578 ui.status("no changes found\n")
577 return
579 return
578
580
579 cg = other.changegroup(fetch)
581 cg = other.changegroup(fetch)
580 r = repo.addchangegroup(cg)
582 r = repo.addchangegroup(cg)
581 if cg and not r:
583 if cg and not r:
582 if opts['update']:
584 if opts['update']:
583 return update(ui, repo)
585 return update(ui, repo)
584 else:
586 else:
585 ui.status("(run 'hg update' to get a working copy)\n")
587 ui.status("(run 'hg update' to get a working copy)\n")
586
588
587 return r
589 return r
588
590
589 def push(ui, repo, dest="default-push"):
591 def push(ui, repo, dest="default-push"):
590 """push changes to the specified destination"""
592 """push changes to the specified destination"""
591 dest = ui.expandpath(dest)
593 dest = ui.expandpath(dest)
592
594
593 if not dest.startswith("ssh://"):
595 if not dest.startswith("ssh://"):
594 ui.warn("abort: can only push to ssh:// destinations currently\n")
596 ui.warn("abort: can only push to ssh:// destinations currently\n")
595 return 1
597 return 1
596
598
597 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
599 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
598 if not m:
600 if not m:
599 ui.warn("abort: couldn't parse destination %s\n" % dest)
601 ui.warn("abort: couldn't parse destination %s\n" % dest)
600 return 1
602 return 1
601
603
602 user, host, port, path = map(m.group, (2, 3, 5, 7))
604 user, host, port, path = map(m.group, (2, 3, 5, 7))
603 uhost = user and ("%s@%s" % (user, host)) or host
605 uhost = user and ("%s@%s" % (user, host)) or host
604 port = port and (" -p %s") % port or ""
606 port = port and (" -p %s") % port or ""
605 path = path or ""
607 path = path or ""
606
608
607 sport = random.randrange(30000, 60000)
609 sport = random.randrange(30000, 60000)
608 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
610 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
609 cmd = cmd % (uhost, port, sport+1, sport, path, sport+1)
611 cmd = cmd % (uhost, port, sport+1, sport, path, sport+1)
610
612
611 child = os.fork()
613 child = os.fork()
612 if not child:
614 if not child:
613 sys.stdout = file("/dev/null", "w")
615 sys.stdout = file("/dev/null", "w")
614 sys.stderr = sys.stdout
616 sys.stderr = sys.stdout
615 hgweb.server(repo.root, "pull", "", "localhost", sport)
617 hgweb.server(repo.root, "pull", "", "localhost", sport)
616 else:
618 else:
617 ui.status("connecting to %s\n" % host)
619 ui.status("connecting to %s\n" % host)
618 r = os.system(cmd)
620 r = os.system(cmd)
619 os.kill(child, signal.SIGTERM)
621 os.kill(child, signal.SIGTERM)
620 return r
622 return r
621
623
622 def rawcommit(ui, repo, *flist, **rc):
624 def rawcommit(ui, repo, *flist, **rc):
623 "raw commit interface"
625 "raw commit interface"
624
626
625 text = rc['text']
627 text = rc['text']
626 if not text and rc['logfile']:
628 if not text and rc['logfile']:
627 try: text = open(rc['logfile']).read()
629 try: text = open(rc['logfile']).read()
628 except IOError: pass
630 except IOError: pass
629 if not text and not rc['logfile']:
631 if not text and not rc['logfile']:
630 print "missing commit text"
632 print "missing commit text"
631 return 1
633 return 1
632
634
633 files = relpath(repo, list(flist))
635 files = relpath(repo, list(flist))
634 if rc['files']:
636 if rc['files']:
635 files += open(rc['files']).read().splitlines()
637 files += open(rc['files']).read().splitlines()
636
638
637 rc['parent'] = map(repo.lookup, rc['parent'])
639 rc['parent'] = map(repo.lookup, rc['parent'])
638
640
639 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
641 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
640
642
641 def recover(ui, repo):
643 def recover(ui, repo):
642 """roll back an interrupted transaction"""
644 """roll back an interrupted transaction"""
643 repo.recover()
645 repo.recover()
644
646
645 def remove(ui, repo, file, *files):
647 def remove(ui, repo, file, *files):
646 """remove the specified files on the next commit"""
648 """remove the specified files on the next commit"""
647 repo.remove(relpath(repo, (file,) + files))
649 repo.remove(relpath(repo, (file,) + files))
648
650
649 def root(ui, repo):
651 def root(ui, repo):
650 """print the root (top) of the current working dir"""
652 """print the root (top) of the current working dir"""
651 ui.write(repo.root + "\n")
653 ui.write(repo.root + "\n")
652
654
653 def serve(ui, repo, **opts):
655 def serve(ui, repo, **opts):
654 """export the repository via HTTP"""
656 """export the repository via HTTP"""
655 hgweb.server(repo.root, opts["name"], opts["templates"],
657 hgweb.server(repo.root, opts["name"], opts["templates"],
656 opts["address"], opts["port"])
658 opts["address"], opts["port"])
657
659
658 def status(ui, repo):
660 def status(ui, repo):
659 '''show changed files in the working directory
661 '''show changed files in the working directory
660
662
661 C = changed
663 C = changed
662 A = added
664 A = added
663 R = removed
665 R = removed
664 ? = not tracked'''
666 ? = not tracked'''
665
667
666 (c, a, d, u) = repo.changes(None, None)
668 (c, a, d, u) = repo.changes(None, None)
667 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
669 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
668
670
669 for f in c: print "C", f
671 for f in c: print "C", f
670 for f in a: print "A", f
672 for f in a: print "A", f
671 for f in d: print "R", f
673 for f in d: print "R", f
672 for f in u: print "?", f
674 for f in u: print "?", f
673
675
674 def tag(ui, repo, name, rev = None, **opts):
676 def tag(ui, repo, name, rev = None, **opts):
675 """add a tag for the current tip or a given revision"""
677 """add a tag for the current tip or a given revision"""
676
678
677 if name == "tip":
679 if name == "tip":
678 ui.warn("abort: 'tip' is a reserved name!\n")
680 ui.warn("abort: 'tip' is a reserved name!\n")
679 return -1
681 return -1
680
682
681 (c, a, d, u) = repo.changes(None, None)
683 (c, a, d, u) = repo.changes(None, None)
682 for x in (c, a, d, u):
684 for x in (c, a, d, u):
683 if ".hgtags" in x:
685 if ".hgtags" in x:
684 ui.warn("abort: working copy of .hgtags is changed!\n")
686 ui.warn("abort: working copy of .hgtags is changed!\n")
685 ui.status("(please commit .hgtags manually)\n")
687 ui.status("(please commit .hgtags manually)\n")
686 return -1
688 return -1
687
689
688 if rev:
690 if rev:
689 r = hg.hex(repo.lookup(rev))
691 r = hg.hex(repo.lookup(rev))
690 else:
692 else:
691 r = hg.hex(repo.changelog.tip())
693 r = hg.hex(repo.changelog.tip())
692
694
693 add = 0
695 add = 0
694 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
696 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
695 repo.wfile(".hgtags", "a").write("%s %s\n" % (r, name))
697 repo.wfile(".hgtags", "a").write("%s %s\n" % (r, name))
696 if add: repo.add([".hgtags"])
698 if add: repo.add([".hgtags"])
697
699
698 if not opts['text']:
700 if not opts['text']:
699 opts['text'] = "Added tag %s for changeset %s" % (name, r)
701 opts['text'] = "Added tag %s for changeset %s" % (name, r)
700
702
701 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
703 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
702
704
703 def tags(ui, repo):
705 def tags(ui, repo):
704 """list repository tags"""
706 """list repository tags"""
705
707
706 l = repo.tagslist()
708 l = repo.tagslist()
707 l.reverse()
709 l.reverse()
708 for t, n in l:
710 for t, n in l:
709 try:
711 try:
710 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
712 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
711 except KeyError:
713 except KeyError:
712 r = " ?:?"
714 r = " ?:?"
713 ui.write("%-30s %s\n" % (t, r))
715 ui.write("%-30s %s\n" % (t, r))
714
716
715 def tip(ui, repo):
717 def tip(ui, repo):
716 """show the tip revision"""
718 """show the tip revision"""
717 n = repo.changelog.tip()
719 n = repo.changelog.tip()
718 show_changeset(ui, repo, changenode=n)
720 show_changeset(ui, repo, changenode=n)
719
721
720 def undo(ui, repo):
722 def undo(ui, repo):
721 """undo the last transaction"""
723 """undo the last transaction"""
722 repo.undo()
724 repo.undo()
723
725
724 def update(ui, repo, node=None, merge=False, clean=False):
726 def update(ui, repo, node=None, merge=False, clean=False):
725 '''update or merge working directory
727 '''update or merge working directory
726
728
727 If there are no outstanding changes in the working directory and
729 If there are no outstanding changes in the working directory and
728 there is a linear relationship between the current version and the
730 there is a linear relationship between the current version and the
729 requested version, the result is the requested version.
731 requested version, the result is the requested version.
730
732
731 Otherwise the result is a merge between the contents of the
733 Otherwise the result is a merge between the contents of the
732 current working directory and the requested version. Files that
734 current working directory and the requested version. Files that
733 changed between either parent are marked as changed for the next
735 changed between either parent are marked as changed for the next
734 commit and a commit must be performed before any further updates
736 commit and a commit must be performed before any further updates
735 are allowed.
737 are allowed.
736 '''
738 '''
737 node = node and repo.lookup(node) or repo.changelog.tip()
739 node = node and repo.lookup(node) or repo.changelog.tip()
738 return repo.update(node, allow=merge, force=clean)
740 return repo.update(node, allow=merge, force=clean)
739
741
740 def verify(ui, repo):
742 def verify(ui, repo):
741 """verify the integrity of the repository"""
743 """verify the integrity of the repository"""
742 return repo.verify()
744 return repo.verify()
743
745
744 # Command options and aliases are listed here, alphabetically
746 # Command options and aliases are listed here, alphabetically
745
747
746 table = {
748 table = {
747 "add": (add, [], "hg add [files]"),
749 "add": (add, [], "hg add [files]"),
748 "addremove": (addremove, [], "hg addremove [files]"),
750 "addremove": (addremove, [], "hg addremove [files]"),
749 "annotate": (annotate,
751 "annotate": (annotate,
750 [('r', 'revision', '', 'revision'),
752 [('r', 'revision', '', 'revision'),
751 ('u', 'user', None, 'show user'),
753 ('u', 'user', None, 'show user'),
752 ('n', 'number', None, 'show revision number'),
754 ('n', 'number', None, 'show revision number'),
753 ('c', 'changeset', None, 'show changeset')],
755 ('c', 'changeset', None, 'show changeset')],
754 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
756 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
755 "cat": (cat, [], 'hg cat <file> [rev]'),
757 "cat": (cat, [], 'hg cat <file> [rev]'),
756 "clone": (clone, [('U', 'noupdate', None, 'skip update after cloning')],
758 "clone": (clone, [('U', 'noupdate', None, 'skip update after cloning')],
757 'hg clone [options] <source> [dest]'),
759 'hg clone [options] <source> [dest]'),
758 "commit|ci": (commit,
760 "commit|ci": (commit,
759 [('t', 'text', "", 'commit text'),
761 [('t', 'text', "", 'commit text'),
760 ('A', 'addremove', None, 'run add/remove during commit'),
762 ('A', 'addremove', None, 'run add/remove during commit'),
761 ('l', 'logfile', "", 'commit text file'),
763 ('l', 'logfile', "", 'commit text file'),
762 ('d', 'date', "", 'data'),
764 ('d', 'date', "", 'data'),
763 ('u', 'user', "", 'user')],
765 ('u', 'user', "", 'user')],
764 'hg commit [files]'),
766 'hg commit [files]'),
765 "copy": (copy, [], 'hg copy <source> <dest>'),
767 "copy": (copy, [], 'hg copy <source> <dest>'),
766 "debugcheckdirstate": (debugcheckdirstate, [], 'debugcheckdirstate'),
768 "debugcheckdirstate": (debugcheckdirstate, [], 'debugcheckdirstate'),
767 "debugdumpdirstate": (debugdumpdirstate, [], 'debugdumpdirstate'),
769 "debugdumpdirstate": (debugdumpdirstate, [], 'debugdumpdirstate'),
768 "debugindex": (debugindex, [], 'debugindex <file>'),
770 "debugindex": (debugindex, [], 'debugindex <file>'),
769 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
771 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
770 "diff": (diff, [('r', 'rev', [], 'revision')],
772 "diff": (diff, [('r', 'rev', [], 'revision')],
771 'hg diff [-r A] [-r B] [files]'),
773 'hg diff [-r A] [-r B] [files]'),
772 "export": (export, [], "hg export <changeset>"),
774 "export": (export, [], "hg export <changeset>"),
773 "forget": (forget, [], "hg forget [files]"),
775 "forget": (forget, [], "hg forget [files]"),
774 "heads": (heads, [], 'hg heads'),
776 "heads": (heads, [], 'hg heads'),
775 "help": (help, [], 'hg help [command]'),
777 "help": (help, [], 'hg help [command]'),
776 "identify|id": (identify, [], 'hg identify'),
778 "identify|id": (identify, [], 'hg identify'),
777 "import|patch": (import_,
779 "import|patch": (import_,
778 [('p', 'strip', 1, 'path strip'),
780 [('p', 'strip', 1, 'path strip'),
779 ('b', 'base', "", 'base path')],
781 ('b', 'base', "", 'base path')],
780 "hg import [options] <patches>"),
782 "hg import [options] <patches>"),
781 "init": (init, [], 'hg init'),
783 "init": (init, [], 'hg init'),
782 "log|history": (log,
784 "log|history": (log,
783 [('r', 'rev', [], 'revision')],
785 [('r', 'rev', [], 'revision')],
784 'hg log [-r A] [-r B] [file]'),
786 'hg log [-r A] [-r B] [file]'),
785 "manifest": (manifest, [], 'hg manifest [rev]'),
787 "manifest": (manifest, [], 'hg manifest [rev]'),
786 "parents": (parents, [], 'hg parents [node]'),
788 "parents": (parents, [], 'hg parents [node]'),
787 "pull": (pull,
789 "pull": (pull,
788 [('u', 'update', None, 'update working directory')],
790 [('u', 'update', None, 'update working directory')],
789 'hg pull [options] [source]'),
791 'hg pull [options] [source]'),
790 "push": (push, [], 'hg push <destination>'),
792 "push": (push, [], 'hg push <destination>'),
791 "rawcommit": (rawcommit,
793 "rawcommit": (rawcommit,
792 [('p', 'parent', [], 'parent'),
794 [('p', 'parent', [], 'parent'),
793 ('d', 'date', "", 'data'),
795 ('d', 'date', "", 'data'),
794 ('u', 'user', "", 'user'),
796 ('u', 'user', "", 'user'),
795 ('F', 'files', "", 'file list'),
797 ('F', 'files', "", 'file list'),
796 ('t', 'text', "", 'commit text'),
798 ('t', 'text', "", 'commit text'),
797 ('l', 'logfile', "", 'commit text file')],
799 ('l', 'logfile', "", 'commit text file')],
798 'hg rawcommit [options] [files]'),
800 'hg rawcommit [options] [files]'),
799 "recover": (recover, [], "hg recover"),
801 "recover": (recover, [], "hg recover"),
800 "remove|rm": (remove, [], "hg remove [files]"),
802 "remove|rm": (remove, [], "hg remove [files]"),
801 "root": (root, [], "hg root"),
803 "root": (root, [], "hg root"),
802 "serve": (serve, [('p', 'port', 8000, 'listen port'),
804 "serve": (serve, [('p', 'port', 8000, 'listen port'),
803 ('a', 'address', '', 'interface address'),
805 ('a', 'address', '', 'interface address'),
804 ('n', 'name', os.getcwd(), 'repository name'),
806 ('n', 'name', os.getcwd(), 'repository name'),
805 ('t', 'templates', "", 'template map')],
807 ('t', 'templates', "", 'template map')],
806 "hg serve [options]"),
808 "hg serve [options]"),
807 "status": (status, [], 'hg status'),
809 "status": (status, [], 'hg status'),
808 "tag": (tag, [('t', 'text', "", 'commit text'),
810 "tag": (tag, [('t', 'text', "", 'commit text'),
809 ('d', 'date', "", 'date'),
811 ('d', 'date', "", 'date'),
810 ('u', 'user', "", 'user')],
812 ('u', 'user', "", 'user')],
811 'hg tag [options] <name> [rev]'),
813 'hg tag [options] <name> [rev]'),
812 "tags": (tags, [], 'hg tags'),
814 "tags": (tags, [], 'hg tags'),
813 "tip": (tip, [], 'hg tip'),
815 "tip": (tip, [], 'hg tip'),
814 "undo": (undo, [], 'hg undo'),
816 "undo": (undo, [], 'hg undo'),
815 "update|up|checkout|co":
817 "update|up|checkout|co":
816 (update,
818 (update,
817 [('m', 'merge', None, 'allow merging of conflicts'),
819 [('m', 'merge', None, 'allow merging of conflicts'),
818 ('C', 'clean', None, 'overwrite locally modified files')],
820 ('C', 'clean', None, 'overwrite locally modified files')],
819 'hg update [options] [node]'),
821 'hg update [options] [node]'),
820 "verify": (verify, [], 'hg verify'),
822 "verify": (verify, [], 'hg verify'),
821 "version": (show_version, [], 'hg version'),
823 "version": (show_version, [], 'hg version'),
822 }
824 }
823
825
824 norepo = "clone init version help debugindex debugindexdot"
826 norepo = "clone init version help debugindex debugindexdot"
825
827
826 def find(cmd):
828 def find(cmd):
827 for e in table.keys():
829 for e in table.keys():
828 if re.match("(%s)$" % e, cmd):
830 if re.match("(%s)$" % e, cmd):
829 return table[e]
831 return table[e]
830
832
831 raise UnknownCommand(cmd)
833 raise UnknownCommand(cmd)
832
834
833 class SignalInterrupt(Exception): pass
835 class SignalInterrupt(Exception): pass
834
836
835 def catchterm(*args):
837 def catchterm(*args):
836 raise SignalInterrupt
838 raise SignalInterrupt
837
839
838 def run():
840 def run():
839 sys.exit(dispatch(sys.argv[1:]))
841 sys.exit(dispatch(sys.argv[1:]))
840
842
841 def dispatch(args):
843 def dispatch(args):
842 options = {}
844 options = {}
843 opts = [('v', 'verbose', None, 'verbose'),
845 opts = [('v', 'verbose', None, 'verbose'),
844 ('d', 'debug', None, 'debug'),
846 ('d', 'debug', None, 'debug'),
845 ('q', 'quiet', None, 'quiet'),
847 ('q', 'quiet', None, 'quiet'),
846 ('p', 'profile', None, 'profile'),
848 ('p', 'profile', None, 'profile'),
847 ('R', 'repository', "", 'repository root directory'),
849 ('R', 'repository', "", 'repository root directory'),
848 ('', 'traceback', None, 'print traceback on exception'),
850 ('', 'traceback', None, 'print traceback on exception'),
849 ('y', 'noninteractive', None, 'run non-interactively'),
851 ('y', 'noninteractive', None, 'run non-interactively'),
850 ('', 'version', None, 'output version information and exit'),
852 ('', 'version', None, 'output version information and exit'),
851 ]
853 ]
852
854
853 args = fancyopts.fancyopts(args, opts, options,
855 args = fancyopts.fancyopts(args, opts, options,
854 'hg [options] <command> [options] [files]')
856 'hg [options] <command> [options] [files]')
855
857
856 if not args:
858 if not args:
857 cmd = "help"
859 cmd = "help"
858 else:
860 else:
859 cmd, args = args[0], args[1:]
861 cmd, args = args[0], args[1:]
860
862
861 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
863 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
862 not options["noninteractive"])
864 not options["noninteractive"])
863
865
864 if options["version"]:
866 if options["version"]:
865 show_version(u)
867 show_version(u)
866 sys.exit(0)
868 sys.exit(0)
867
869
868 try:
870 try:
869 i = find(cmd)
871 i = find(cmd)
870 except UnknownCommand:
872 except UnknownCommand:
871 u.warn("hg: unknown command '%s'\n" % cmd)
873 u.warn("hg: unknown command '%s'\n" % cmd)
872 help(u)
874 help(u)
873 sys.exit(1)
875 sys.exit(1)
874
876
875 signal.signal(signal.SIGTERM, catchterm)
877 signal.signal(signal.SIGTERM, catchterm)
876
878
877 cmdoptions = {}
879 cmdoptions = {}
878 try:
880 try:
879 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
881 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
880 except fancyopts.getopt.GetoptError, inst:
882 except fancyopts.getopt.GetoptError, inst:
881 u.warn("hg %s: %s\n" % (cmd, inst))
883 u.warn("hg %s: %s\n" % (cmd, inst))
882 help(u, cmd)
884 help(u, cmd)
883 sys.exit(-1)
885 sys.exit(-1)
884
886
885 try:
887 try:
886 try:
888 try:
887 if cmd not in norepo.split():
889 if cmd not in norepo.split():
888 path = options["repository"] or ""
890 path = options["repository"] or ""
889 repo = hg.repository(ui=u, path=path)
891 repo = hg.repository(ui=u, path=path)
890 d = lambda: i[0](u, repo, *args, **cmdoptions)
892 d = lambda: i[0](u, repo, *args, **cmdoptions)
891 else:
893 else:
892 d = lambda: i[0](u, *args, **cmdoptions)
894 d = lambda: i[0](u, *args, **cmdoptions)
893
895
894 if options['profile']:
896 if options['profile']:
895 import hotshot, hotshot.stats
897 import hotshot, hotshot.stats
896 prof = hotshot.Profile("hg.prof")
898 prof = hotshot.Profile("hg.prof")
897 r = prof.runcall(d)
899 r = prof.runcall(d)
898 prof.close()
900 prof.close()
899 stats = hotshot.stats.load("hg.prof")
901 stats = hotshot.stats.load("hg.prof")
900 stats.strip_dirs()
902 stats.strip_dirs()
901 stats.sort_stats('time', 'calls')
903 stats.sort_stats('time', 'calls')
902 stats.print_stats(40)
904 stats.print_stats(40)
903 return r
905 return r
904 else:
906 else:
905 return d()
907 return d()
906 except:
908 except:
907 if options['traceback']:
909 if options['traceback']:
908 traceback.print_exc()
910 traceback.print_exc()
909 raise
911 raise
910 except util.CommandError, inst:
912 except util.CommandError, inst:
911 u.warn("abort: %s\n" % inst.args)
913 u.warn("abort: %s\n" % inst.args)
912 except hg.RepoError, inst:
914 except hg.RepoError, inst:
913 u.warn("abort: ", inst, "!\n")
915 u.warn("abort: ", inst, "!\n")
914 except SignalInterrupt:
916 except SignalInterrupt:
915 u.warn("killed!\n")
917 u.warn("killed!\n")
916 except KeyboardInterrupt:
918 except KeyboardInterrupt:
917 u.warn("interrupted!\n")
919 u.warn("interrupted!\n")
918 except IOError, inst:
920 except IOError, inst:
919 if hasattr(inst, "code"):
921 if hasattr(inst, "code"):
920 u.warn("abort: %s\n" % inst)
922 u.warn("abort: %s\n" % inst)
921 elif hasattr(inst, "reason"):
923 elif hasattr(inst, "reason"):
922 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
924 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
923 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
925 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
924 u.warn("broken pipe\n")
926 u.warn("broken pipe\n")
925 else:
927 else:
926 raise
928 raise
927 except OSError, inst:
929 except OSError, inst:
928 if hasattr(inst, "filename"):
930 if hasattr(inst, "filename"):
929 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
931 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
930 else:
932 else:
931 u.warn("abort: %s\n" % inst.strerror)
933 u.warn("abort: %s\n" % inst.strerror)
932 except TypeError, inst:
934 except TypeError, inst:
933 # was this an argument error?
935 # was this an argument error?
934 tb = traceback.extract_tb(sys.exc_info()[2])
936 tb = traceback.extract_tb(sys.exc_info()[2])
935 if len(tb) > 2: # no
937 if len(tb) > 2: # no
936 raise
938 raise
937 u.debug(inst, "\n")
939 u.debug(inst, "\n")
938 u.warn("%s: invalid arguments\n" % i[0].__name__)
940 u.warn("%s: invalid arguments\n" % i[0].__name__)
939 help(u, cmd)
941 help(u, cmd)
940
942
941 sys.exit(-1)
943 sys.exit(-1)
942
944
General Comments 0
You need to be logged in to leave comments. Login now