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