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