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