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