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