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