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