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