##// END OF EJS Templates
Change canonpath to not know about repo objects...
mpm@selenic.com -
r1081:8b7d6348 default
parent child Browse files
Show More
@@ -1,1870 +1,1870 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 imp")
9 demandload(globals(), "os re sys signal shutil imp")
10 demandload(globals(), "fancyopts ui hg util lock")
10 demandload(globals(), "fancyopts ui hg util lock")
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 sets")
12 demandload(globals(), "errno socket version struct atexit sets")
13
13
14 class UnknownCommand(Exception):
14 class UnknownCommand(Exception):
15 """Exception raised if command is not in the command table."""
15 """Exception raised if command is not in the command table."""
16
16
17 def filterfiles(filters, files):
17 def filterfiles(filters, files):
18 l = [x for x in files if x in filters]
18 l = [x for x in files if x in filters]
19
19
20 for t in filters:
20 for t in filters:
21 if t and t[-1] != "/":
21 if t and t[-1] != "/":
22 t += "/"
22 t += "/"
23 l += [x for x in files if x.startswith(t)]
23 l += [x for x in files if x.startswith(t)]
24 return l
24 return l
25
25
26 def relpath(repo, args):
26 def relpath(repo, args):
27 cwd = repo.getcwd()
27 cwd = repo.getcwd()
28 if cwd:
28 if cwd:
29 return [util.normpath(os.path.join(cwd, x)) for x in args]
29 return [util.normpath(os.path.join(cwd, x)) for x in args]
30 return args
30 return args
31
31
32 def matchpats(repo, cwd, pats=[], opts={}, head=''):
32 def matchpats(repo, cwd, pats=[], opts={}, head=''):
33 return util.matcher(repo, cwd, pats or ['.'], opts.get('include'),
33 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
34 opts.get('exclude'), head)
34 opts.get('exclude'), head)
35
35
36 def makewalk(repo, pats, opts, head=''):
36 def makewalk(repo, pats, opts, head=''):
37 cwd = repo.getcwd()
37 cwd = repo.getcwd()
38 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
38 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
39 exact = dict(zip(files, files))
39 exact = dict(zip(files, files))
40 def walk():
40 def walk():
41 for src, fn in repo.walk(files=files, match=matchfn):
41 for src, fn in repo.walk(files=files, match=matchfn):
42 yield src, fn, util.pathto(cwd, fn), fn in exact
42 yield src, fn, util.pathto(cwd, fn), fn in exact
43 return files, matchfn, walk()
43 return files, matchfn, walk()
44
44
45 def walk(repo, pats, opts, head=''):
45 def walk(repo, pats, opts, head=''):
46 files, matchfn, results = makewalk(repo, pats, opts, head)
46 files, matchfn, results = makewalk(repo, pats, opts, head)
47 for r in results:
47 for r in results:
48 yield r
48 yield r
49
49
50 def walkchangerevs(ui, repo, cwd, pats, opts):
50 def walkchangerevs(ui, repo, cwd, pats, opts):
51 # This code most commonly needs to iterate backwards over the
51 # This code most commonly needs to iterate backwards over the
52 # history it is interested in. Doing so has awful
52 # history it is interested in. Doing so has awful
53 # (quadratic-looking) performance, so we use iterators in a
53 # (quadratic-looking) performance, so we use iterators in a
54 # "windowed" way. Walk forwards through a window of revisions,
54 # "windowed" way. Walk forwards through a window of revisions,
55 # yielding them in the desired order, and walk the windows
55 # yielding them in the desired order, and walk the windows
56 # themselves backwards.
56 # themselves backwards.
57 cwd = repo.getcwd()
57 cwd = repo.getcwd()
58 if not pats and cwd:
58 if not pats and cwd:
59 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
59 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
60 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
60 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
61 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
61 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
62 pats, opts)
62 pats, opts)
63 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
63 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
64 wanted = {}
64 wanted = {}
65 slowpath = anypats
65 slowpath = anypats
66 window = 300
66 window = 300
67 fncache = {}
67 fncache = {}
68 if not slowpath and not files:
68 if not slowpath and not files:
69 # No files, no patterns. Display all revs.
69 # No files, no patterns. Display all revs.
70 wanted = dict(zip(revs, revs))
70 wanted = dict(zip(revs, revs))
71 if not slowpath:
71 if not slowpath:
72 # Only files, no patterns. Check the history of each file.
72 # Only files, no patterns. Check the history of each file.
73 def filerevgen(filelog):
73 def filerevgen(filelog):
74 for i in xrange(filelog.count() - 1, -1, -window):
74 for i in xrange(filelog.count() - 1, -1, -window):
75 revs = []
75 revs = []
76 for j in xrange(max(0, i - window), i + 1):
76 for j in xrange(max(0, i - window), i + 1):
77 revs.append(filelog.linkrev(filelog.node(j)))
77 revs.append(filelog.linkrev(filelog.node(j)))
78 revs.reverse()
78 revs.reverse()
79 for rev in revs:
79 for rev in revs:
80 yield rev
80 yield rev
81
81
82 minrev, maxrev = min(revs), max(revs)
82 minrev, maxrev = min(revs), max(revs)
83 for file in files:
83 for file in files:
84 filelog = repo.file(file)
84 filelog = repo.file(file)
85 # A zero count may be a directory or deleted file, so
85 # A zero count may be a directory or deleted file, so
86 # try to find matching entries on the slow path.
86 # try to find matching entries on the slow path.
87 if filelog.count() == 0:
87 if filelog.count() == 0:
88 slowpath = True
88 slowpath = True
89 break
89 break
90 for rev in filerevgen(filelog):
90 for rev in filerevgen(filelog):
91 if rev <= maxrev:
91 if rev <= maxrev:
92 if rev < minrev:
92 if rev < minrev:
93 break
93 break
94 fncache.setdefault(rev, [])
94 fncache.setdefault(rev, [])
95 fncache[rev].append(file)
95 fncache[rev].append(file)
96 wanted[rev] = 1
96 wanted[rev] = 1
97 if slowpath:
97 if slowpath:
98 # The slow path checks files modified in every changeset.
98 # The slow path checks files modified in every changeset.
99 def changerevgen():
99 def changerevgen():
100 for i in xrange(repo.changelog.count() - 1, -1, -window):
100 for i in xrange(repo.changelog.count() - 1, -1, -window):
101 for j in xrange(max(0, i - window), i + 1):
101 for j in xrange(max(0, i - window), i + 1):
102 yield j, repo.changelog.read(repo.lookup(str(j)))[3]
102 yield j, repo.changelog.read(repo.lookup(str(j)))[3]
103
103
104 for rev, changefiles in changerevgen():
104 for rev, changefiles in changerevgen():
105 matches = filter(matchfn, changefiles)
105 matches = filter(matchfn, changefiles)
106 if matches:
106 if matches:
107 fncache[rev] = matches
107 fncache[rev] = matches
108 wanted[rev] = 1
108 wanted[rev] = 1
109
109
110 for i in xrange(0, len(revs), window):
110 for i in xrange(0, len(revs), window):
111 yield 'window', revs[0] < revs[-1], revs[-1]
111 yield 'window', revs[0] < revs[-1], revs[-1]
112 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
112 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
113 if rev in wanted]
113 if rev in wanted]
114 srevs = list(nrevs)
114 srevs = list(nrevs)
115 srevs.sort()
115 srevs.sort()
116 for rev in srevs:
116 for rev in srevs:
117 fns = fncache.get(rev)
117 fns = fncache.get(rev)
118 if not fns:
118 if not fns:
119 fns = repo.changelog.read(repo.lookup(str(rev)))[3]
119 fns = repo.changelog.read(repo.lookup(str(rev)))[3]
120 fns = filter(matchfn, fns)
120 fns = filter(matchfn, fns)
121 yield 'add', rev, fns
121 yield 'add', rev, fns
122 for rev in nrevs:
122 for rev in nrevs:
123 yield 'iter', rev, None
123 yield 'iter', rev, None
124
124
125 revrangesep = ':'
125 revrangesep = ':'
126
126
127 def revrange(ui, repo, revs, revlog=None):
127 def revrange(ui, repo, revs, revlog=None):
128 """Yield revision as strings from a list of revision specifications."""
128 """Yield revision as strings from a list of revision specifications."""
129 if revlog is None:
129 if revlog is None:
130 revlog = repo.changelog
130 revlog = repo.changelog
131 revcount = revlog.count()
131 revcount = revlog.count()
132 def fix(val, defval):
132 def fix(val, defval):
133 if not val:
133 if not val:
134 return defval
134 return defval
135 try:
135 try:
136 num = int(val)
136 num = int(val)
137 if str(num) != val:
137 if str(num) != val:
138 raise ValueError
138 raise ValueError
139 if num < 0:
139 if num < 0:
140 num += revcount
140 num += revcount
141 if not (0 <= num < revcount):
141 if not (0 <= num < revcount):
142 raise ValueError
142 raise ValueError
143 except ValueError:
143 except ValueError:
144 try:
144 try:
145 num = repo.changelog.rev(repo.lookup(val))
145 num = repo.changelog.rev(repo.lookup(val))
146 except KeyError:
146 except KeyError:
147 try:
147 try:
148 num = revlog.rev(revlog.lookup(val))
148 num = revlog.rev(revlog.lookup(val))
149 except KeyError:
149 except KeyError:
150 raise util.Abort('invalid revision identifier %s', val)
150 raise util.Abort('invalid revision identifier %s', val)
151 return num
151 return num
152 for spec in revs:
152 for spec in revs:
153 if spec.find(revrangesep) >= 0:
153 if spec.find(revrangesep) >= 0:
154 start, end = spec.split(revrangesep, 1)
154 start, end = spec.split(revrangesep, 1)
155 start = fix(start, 0)
155 start = fix(start, 0)
156 end = fix(end, revcount - 1)
156 end = fix(end, revcount - 1)
157 step = start > end and -1 or 1
157 step = start > end and -1 or 1
158 for rev in xrange(start, end+step, step):
158 for rev in xrange(start, end+step, step):
159 yield str(rev)
159 yield str(rev)
160 else:
160 else:
161 yield str(fix(spec, None))
161 yield str(fix(spec, None))
162
162
163 def make_filename(repo, r, pat, node=None,
163 def make_filename(repo, r, pat, node=None,
164 total=None, seqno=None, revwidth=None):
164 total=None, seqno=None, revwidth=None):
165 node_expander = {
165 node_expander = {
166 'H': lambda: hg.hex(node),
166 'H': lambda: hg.hex(node),
167 'R': lambda: str(r.rev(node)),
167 'R': lambda: str(r.rev(node)),
168 'h': lambda: hg.short(node),
168 'h': lambda: hg.short(node),
169 }
169 }
170 expander = {
170 expander = {
171 '%': lambda: '%',
171 '%': lambda: '%',
172 'b': lambda: os.path.basename(repo.root),
172 'b': lambda: os.path.basename(repo.root),
173 }
173 }
174
174
175 try:
175 try:
176 if node:
176 if node:
177 expander.update(node_expander)
177 expander.update(node_expander)
178 if node and revwidth is not None:
178 if node and revwidth is not None:
179 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
179 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
180 if total is not None:
180 if total is not None:
181 expander['N'] = lambda: str(total)
181 expander['N'] = lambda: str(total)
182 if seqno is not None:
182 if seqno is not None:
183 expander['n'] = lambda: str(seqno)
183 expander['n'] = lambda: str(seqno)
184 if total is not None and seqno is not None:
184 if total is not None and seqno is not None:
185 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
185 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
186
186
187 newname = []
187 newname = []
188 patlen = len(pat)
188 patlen = len(pat)
189 i = 0
189 i = 0
190 while i < patlen:
190 while i < patlen:
191 c = pat[i]
191 c = pat[i]
192 if c == '%':
192 if c == '%':
193 i += 1
193 i += 1
194 c = pat[i]
194 c = pat[i]
195 c = expander[c]()
195 c = expander[c]()
196 newname.append(c)
196 newname.append(c)
197 i += 1
197 i += 1
198 return ''.join(newname)
198 return ''.join(newname)
199 except KeyError, inst:
199 except KeyError, inst:
200 raise util.Abort("invalid format spec '%%%s' in output file name",
200 raise util.Abort("invalid format spec '%%%s' in output file name",
201 inst.args[0])
201 inst.args[0])
202
202
203 def make_file(repo, r, pat, node=None,
203 def make_file(repo, r, pat, node=None,
204 total=None, seqno=None, revwidth=None, mode='wb'):
204 total=None, seqno=None, revwidth=None, mode='wb'):
205 if not pat or pat == '-':
205 if not pat or pat == '-':
206 return 'w' in mode and sys.stdout or sys.stdin
206 return 'w' in mode and sys.stdout or sys.stdin
207 if hasattr(pat, 'write') and 'w' in mode:
207 if hasattr(pat, 'write') and 'w' in mode:
208 return pat
208 return pat
209 if hasattr(pat, 'read') and 'r' in mode:
209 if hasattr(pat, 'read') and 'r' in mode:
210 return pat
210 return pat
211 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
211 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
212 mode)
212 mode)
213
213
214 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
214 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
215 changes=None, text=False):
215 changes=None, text=False):
216 def date(c):
216 def date(c):
217 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
217 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
218
218
219 if not changes:
219 if not changes:
220 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
220 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
221 else:
221 else:
222 (c, a, d, u) = changes
222 (c, a, d, u) = changes
223 if files:
223 if files:
224 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
224 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
225
225
226 if not c and not a and not d:
226 if not c and not a and not d:
227 return
227 return
228
228
229 if node2:
229 if node2:
230 change = repo.changelog.read(node2)
230 change = repo.changelog.read(node2)
231 mmap2 = repo.manifest.read(change[0])
231 mmap2 = repo.manifest.read(change[0])
232 date2 = date(change)
232 date2 = date(change)
233 def read(f):
233 def read(f):
234 return repo.file(f).read(mmap2[f])
234 return repo.file(f).read(mmap2[f])
235 else:
235 else:
236 date2 = time.asctime()
236 date2 = time.asctime()
237 if not node1:
237 if not node1:
238 node1 = repo.dirstate.parents()[0]
238 node1 = repo.dirstate.parents()[0]
239 def read(f):
239 def read(f):
240 return repo.wfile(f).read()
240 return repo.wfile(f).read()
241
241
242 if ui.quiet:
242 if ui.quiet:
243 r = None
243 r = None
244 else:
244 else:
245 hexfunc = ui.verbose and hg.hex or hg.short
245 hexfunc = ui.verbose and hg.hex or hg.short
246 r = [hexfunc(node) for node in [node1, node2] if node]
246 r = [hexfunc(node) for node in [node1, node2] if node]
247
247
248 change = repo.changelog.read(node1)
248 change = repo.changelog.read(node1)
249 mmap = repo.manifest.read(change[0])
249 mmap = repo.manifest.read(change[0])
250 date1 = date(change)
250 date1 = date(change)
251
251
252 for f in c:
252 for f in c:
253 to = None
253 to = None
254 if f in mmap:
254 if f in mmap:
255 to = repo.file(f).read(mmap[f])
255 to = repo.file(f).read(mmap[f])
256 tn = read(f)
256 tn = read(f)
257 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
257 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
258 for f in a:
258 for f in a:
259 to = None
259 to = None
260 tn = read(f)
260 tn = read(f)
261 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
261 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
262 for f in d:
262 for f in d:
263 to = repo.file(f).read(mmap[f])
263 to = repo.file(f).read(mmap[f])
264 tn = None
264 tn = None
265 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
265 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
266
266
267 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
267 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
268 """show a single changeset or file revision"""
268 """show a single changeset or file revision"""
269 log = repo.changelog
269 log = repo.changelog
270 if changenode is None:
270 if changenode is None:
271 changenode = log.node(rev)
271 changenode = log.node(rev)
272 elif not rev:
272 elif not rev:
273 rev = log.rev(changenode)
273 rev = log.rev(changenode)
274
274
275 if ui.quiet:
275 if ui.quiet:
276 ui.write("%d:%s\n" % (rev, hg.short(changenode)))
276 ui.write("%d:%s\n" % (rev, hg.short(changenode)))
277 return
277 return
278
278
279 changes = log.read(changenode)
279 changes = log.read(changenode)
280
280
281 t, tz = changes[2].split(' ')
281 t, tz = changes[2].split(' ')
282 # a conversion tool was sticking non-integer offsets into repos
282 # a conversion tool was sticking non-integer offsets into repos
283 try:
283 try:
284 tz = int(tz)
284 tz = int(tz)
285 except ValueError:
285 except ValueError:
286 tz = 0
286 tz = 0
287 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
287 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
288
288
289 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
289 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
290 for p in log.parents(changenode)
290 for p in log.parents(changenode)
291 if ui.debugflag or p != hg.nullid]
291 if ui.debugflag or p != hg.nullid]
292 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
292 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
293 parents = []
293 parents = []
294
294
295 if ui.verbose:
295 if ui.verbose:
296 ui.write("changeset: %d:%s\n" % (rev, hg.hex(changenode)))
296 ui.write("changeset: %d:%s\n" % (rev, hg.hex(changenode)))
297 else:
297 else:
298 ui.write("changeset: %d:%s\n" % (rev, hg.short(changenode)))
298 ui.write("changeset: %d:%s\n" % (rev, hg.short(changenode)))
299
299
300 for tag in repo.nodetags(changenode):
300 for tag in repo.nodetags(changenode):
301 ui.status("tag: %s\n" % tag)
301 ui.status("tag: %s\n" % tag)
302 for parent in parents:
302 for parent in parents:
303 ui.write("parent: %d:%s\n" % parent)
303 ui.write("parent: %d:%s\n" % parent)
304
304
305 if brinfo and changenode in brinfo:
305 if brinfo and changenode in brinfo:
306 br = brinfo[changenode]
306 br = brinfo[changenode]
307 ui.write("branch: %s\n" % " ".join(br))
307 ui.write("branch: %s\n" % " ".join(br))
308
308
309 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
309 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
310 hg.hex(changes[0])))
310 hg.hex(changes[0])))
311 ui.status("user: %s\n" % changes[1])
311 ui.status("user: %s\n" % changes[1])
312 ui.status("date: %s\n" % date)
312 ui.status("date: %s\n" % date)
313
313
314 if ui.debugflag:
314 if ui.debugflag:
315 files = repo.changes(log.parents(changenode)[0], changenode)
315 files = repo.changes(log.parents(changenode)[0], changenode)
316 for key, value in zip(["files:", "files+:", "files-:"], files):
316 for key, value in zip(["files:", "files+:", "files-:"], files):
317 if value:
317 if value:
318 ui.note("%-12s %s\n" % (key, " ".join(value)))
318 ui.note("%-12s %s\n" % (key, " ".join(value)))
319 else:
319 else:
320 ui.note("files: %s\n" % " ".join(changes[3]))
320 ui.note("files: %s\n" % " ".join(changes[3]))
321
321
322 description = changes[4].strip()
322 description = changes[4].strip()
323 if description:
323 if description:
324 if ui.verbose:
324 if ui.verbose:
325 ui.status("description:\n")
325 ui.status("description:\n")
326 ui.status(description)
326 ui.status(description)
327 ui.status("\n\n")
327 ui.status("\n\n")
328 else:
328 else:
329 ui.status("summary: %s\n" % description.splitlines()[0])
329 ui.status("summary: %s\n" % description.splitlines()[0])
330 ui.status("\n")
330 ui.status("\n")
331
331
332 def show_version(ui):
332 def show_version(ui):
333 """output version and copyright information"""
333 """output version and copyright information"""
334 ui.write("Mercurial Distributed SCM (version %s)\n"
334 ui.write("Mercurial Distributed SCM (version %s)\n"
335 % version.get_version())
335 % version.get_version())
336 ui.status(
336 ui.status(
337 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
337 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
338 "This is free software; see the source for copying conditions. "
338 "This is free software; see the source for copying conditions. "
339 "There is NO\nwarranty; "
339 "There is NO\nwarranty; "
340 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
340 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
341 )
341 )
342
342
343 def help_(ui, cmd=None, with_version=False):
343 def help_(ui, cmd=None, with_version=False):
344 """show help for a given command or all commands"""
344 """show help for a given command or all commands"""
345 option_lists = []
345 option_lists = []
346 if cmd and cmd != 'shortlist':
346 if cmd and cmd != 'shortlist':
347 if with_version:
347 if with_version:
348 show_version(ui)
348 show_version(ui)
349 ui.write('\n')
349 ui.write('\n')
350 key, i = find(cmd)
350 key, i = find(cmd)
351 # synopsis
351 # synopsis
352 ui.write("%s\n\n" % i[2])
352 ui.write("%s\n\n" % i[2])
353
353
354 # description
354 # description
355 doc = i[0].__doc__
355 doc = i[0].__doc__
356 if ui.quiet:
356 if ui.quiet:
357 doc = doc.splitlines(0)[0]
357 doc = doc.splitlines(0)[0]
358 ui.write("%s\n" % doc.rstrip())
358 ui.write("%s\n" % doc.rstrip())
359
359
360 if not ui.quiet:
360 if not ui.quiet:
361 # aliases
361 # aliases
362 aliases = ', '.join(key.split('|')[1:])
362 aliases = ', '.join(key.split('|')[1:])
363 if aliases:
363 if aliases:
364 ui.write("\naliases: %s\n" % aliases)
364 ui.write("\naliases: %s\n" % aliases)
365
365
366 # options
366 # options
367 if i[1]:
367 if i[1]:
368 option_lists.append(("options", i[1]))
368 option_lists.append(("options", i[1]))
369
369
370 else:
370 else:
371 # program name
371 # program name
372 if ui.verbose or with_version:
372 if ui.verbose or with_version:
373 show_version(ui)
373 show_version(ui)
374 else:
374 else:
375 ui.status("Mercurial Distributed SCM\n")
375 ui.status("Mercurial Distributed SCM\n")
376 ui.status('\n')
376 ui.status('\n')
377
377
378 # list of commands
378 # list of commands
379 if cmd == "shortlist":
379 if cmd == "shortlist":
380 ui.status('basic commands (use "hg help" '
380 ui.status('basic commands (use "hg help" '
381 'for the full list or option "-v" for details):\n\n')
381 'for the full list or option "-v" for details):\n\n')
382 elif ui.verbose:
382 elif ui.verbose:
383 ui.status('list of commands:\n\n')
383 ui.status('list of commands:\n\n')
384 else:
384 else:
385 ui.status('list of commands (use "hg help -v" '
385 ui.status('list of commands (use "hg help -v" '
386 'to show aliases and global options):\n\n')
386 'to show aliases and global options):\n\n')
387
387
388 h = {}
388 h = {}
389 cmds = {}
389 cmds = {}
390 for c, e in table.items():
390 for c, e in table.items():
391 f = c.split("|")[0]
391 f = c.split("|")[0]
392 if cmd == "shortlist" and not f.startswith("^"):
392 if cmd == "shortlist" and not f.startswith("^"):
393 continue
393 continue
394 f = f.lstrip("^")
394 f = f.lstrip("^")
395 if not ui.debugflag and f.startswith("debug"):
395 if not ui.debugflag and f.startswith("debug"):
396 continue
396 continue
397 d = ""
397 d = ""
398 if e[0].__doc__:
398 if e[0].__doc__:
399 d = e[0].__doc__.splitlines(0)[0].rstrip()
399 d = e[0].__doc__.splitlines(0)[0].rstrip()
400 h[f] = d
400 h[f] = d
401 cmds[f]=c.lstrip("^")
401 cmds[f]=c.lstrip("^")
402
402
403 fns = h.keys()
403 fns = h.keys()
404 fns.sort()
404 fns.sort()
405 m = max(map(len, fns))
405 m = max(map(len, fns))
406 for f in fns:
406 for f in fns:
407 if ui.verbose:
407 if ui.verbose:
408 commands = cmds[f].replace("|",", ")
408 commands = cmds[f].replace("|",", ")
409 ui.write(" %s:\n %s\n"%(commands,h[f]))
409 ui.write(" %s:\n %s\n"%(commands,h[f]))
410 else:
410 else:
411 ui.write(' %-*s %s\n' % (m, f, h[f]))
411 ui.write(' %-*s %s\n' % (m, f, h[f]))
412
412
413 # global options
413 # global options
414 if ui.verbose:
414 if ui.verbose:
415 option_lists.append(("global options", globalopts))
415 option_lists.append(("global options", globalopts))
416
416
417 # list all option lists
417 # list all option lists
418 opt_output = []
418 opt_output = []
419 for title, options in option_lists:
419 for title, options in option_lists:
420 opt_output.append(("\n%s:\n" % title, None))
420 opt_output.append(("\n%s:\n" % title, None))
421 for shortopt, longopt, default, desc in options:
421 for shortopt, longopt, default, desc in options:
422 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
422 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
423 longopt and " --%s" % longopt),
423 longopt and " --%s" % longopt),
424 "%s%s" % (desc,
424 "%s%s" % (desc,
425 default and " (default: %s)" % default
425 default and " (default: %s)" % default
426 or "")))
426 or "")))
427
427
428 if opt_output:
428 if opt_output:
429 opts_len = max([len(line[0]) for line in opt_output if line[1]])
429 opts_len = max([len(line[0]) for line in opt_output if line[1]])
430 for first, second in opt_output:
430 for first, second in opt_output:
431 if second:
431 if second:
432 ui.write(" %-*s %s\n" % (opts_len, first, second))
432 ui.write(" %-*s %s\n" % (opts_len, first, second))
433 else:
433 else:
434 ui.write("%s\n" % first)
434 ui.write("%s\n" % first)
435
435
436 # Commands start here, listed alphabetically
436 # Commands start here, listed alphabetically
437
437
438 def add(ui, repo, *pats, **opts):
438 def add(ui, repo, *pats, **opts):
439 '''add the specified files on the next commit'''
439 '''add the specified files on the next commit'''
440 names = []
440 names = []
441 for src, abs, rel, exact in walk(repo, pats, opts):
441 for src, abs, rel, exact in walk(repo, pats, opts):
442 if exact:
442 if exact:
443 names.append(abs)
443 names.append(abs)
444 elif repo.dirstate.state(abs) == '?':
444 elif repo.dirstate.state(abs) == '?':
445 ui.status('adding %s\n' % rel)
445 ui.status('adding %s\n' % rel)
446 names.append(abs)
446 names.append(abs)
447 repo.add(names)
447 repo.add(names)
448
448
449 def addremove(ui, repo, *pats, **opts):
449 def addremove(ui, repo, *pats, **opts):
450 """add all new files, delete all missing files"""
450 """add all new files, delete all missing files"""
451 add, remove = [], []
451 add, remove = [], []
452 for src, abs, rel, exact in walk(repo, pats, opts):
452 for src, abs, rel, exact in walk(repo, pats, opts):
453 if src == 'f' and repo.dirstate.state(abs) == '?':
453 if src == 'f' and repo.dirstate.state(abs) == '?':
454 add.append(abs)
454 add.append(abs)
455 if not exact:
455 if not exact:
456 ui.status('adding ', rel, '\n')
456 ui.status('adding ', rel, '\n')
457 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
457 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
458 remove.append(abs)
458 remove.append(abs)
459 if not exact:
459 if not exact:
460 ui.status('removing ', rel, '\n')
460 ui.status('removing ', rel, '\n')
461 repo.add(add)
461 repo.add(add)
462 repo.remove(remove)
462 repo.remove(remove)
463
463
464 def annotate(ui, repo, *pats, **opts):
464 def annotate(ui, repo, *pats, **opts):
465 """show changeset information per file line"""
465 """show changeset information per file line"""
466 def getnode(rev):
466 def getnode(rev):
467 return hg.short(repo.changelog.node(rev))
467 return hg.short(repo.changelog.node(rev))
468
468
469 def getname(rev):
469 def getname(rev):
470 try:
470 try:
471 return bcache[rev]
471 return bcache[rev]
472 except KeyError:
472 except KeyError:
473 cl = repo.changelog.read(repo.changelog.node(rev))
473 cl = repo.changelog.read(repo.changelog.node(rev))
474 name = cl[1]
474 name = cl[1]
475 f = name.find('@')
475 f = name.find('@')
476 if f >= 0:
476 if f >= 0:
477 name = name[:f]
477 name = name[:f]
478 f = name.find('<')
478 f = name.find('<')
479 if f >= 0:
479 if f >= 0:
480 name = name[f+1:]
480 name = name[f+1:]
481 bcache[rev] = name
481 bcache[rev] = name
482 return name
482 return name
483
483
484 if not pats:
484 if not pats:
485 raise util.Abort('at least one file name or pattern required')
485 raise util.Abort('at least one file name or pattern required')
486
486
487 bcache = {}
487 bcache = {}
488 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
488 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
489 if not opts['user'] and not opts['changeset']:
489 if not opts['user'] and not opts['changeset']:
490 opts['number'] = 1
490 opts['number'] = 1
491
491
492 if opts['rev']:
492 if opts['rev']:
493 node = repo.changelog.lookup(opts['rev'])
493 node = repo.changelog.lookup(opts['rev'])
494 else:
494 else:
495 node = repo.dirstate.parents()[0]
495 node = repo.dirstate.parents()[0]
496 change = repo.changelog.read(node)
496 change = repo.changelog.read(node)
497 mmap = repo.manifest.read(change[0])
497 mmap = repo.manifest.read(change[0])
498
498
499 for src, abs, rel, exact in walk(repo, pats, opts):
499 for src, abs, rel, exact in walk(repo, pats, opts):
500 if abs not in mmap:
500 if abs not in mmap:
501 ui.warn("warning: %s is not in the repository!\n" % rel)
501 ui.warn("warning: %s is not in the repository!\n" % rel)
502 continue
502 continue
503
503
504 f = repo.file(abs)
504 f = repo.file(abs)
505 if not opts['text'] and util.binary(f.read(mmap[abs])):
505 if not opts['text'] and util.binary(f.read(mmap[abs])):
506 ui.write("%s: binary file\n" % rel)
506 ui.write("%s: binary file\n" % rel)
507 continue
507 continue
508
508
509 lines = f.annotate(mmap[abs])
509 lines = f.annotate(mmap[abs])
510 pieces = []
510 pieces = []
511
511
512 for o, f in opmap:
512 for o, f in opmap:
513 if opts[o]:
513 if opts[o]:
514 l = [f(n) for n, dummy in lines]
514 l = [f(n) for n, dummy in lines]
515 if l:
515 if l:
516 m = max(map(len, l))
516 m = max(map(len, l))
517 pieces.append(["%*s" % (m, x) for x in l])
517 pieces.append(["%*s" % (m, x) for x in l])
518
518
519 if pieces:
519 if pieces:
520 for p, l in zip(zip(*pieces), lines):
520 for p, l in zip(zip(*pieces), lines):
521 ui.write("%s: %s" % (" ".join(p), l[1]))
521 ui.write("%s: %s" % (" ".join(p), l[1]))
522
522
523 def cat(ui, repo, file1, rev=None, **opts):
523 def cat(ui, repo, file1, rev=None, **opts):
524 """output the latest or given revision of a file"""
524 """output the latest or given revision of a file"""
525 r = repo.file(relpath(repo, [file1])[0])
525 r = repo.file(relpath(repo, [file1])[0])
526 if rev:
526 if rev:
527 try:
527 try:
528 # assume all revision numbers are for changesets
528 # assume all revision numbers are for changesets
529 n = repo.lookup(rev)
529 n = repo.lookup(rev)
530 change = repo.changelog.read(n)
530 change = repo.changelog.read(n)
531 m = repo.manifest.read(change[0])
531 m = repo.manifest.read(change[0])
532 n = m[relpath(repo, [file1])[0]]
532 n = m[relpath(repo, [file1])[0]]
533 except hg.RepoError, KeyError:
533 except hg.RepoError, KeyError:
534 n = r.lookup(rev)
534 n = r.lookup(rev)
535 else:
535 else:
536 n = r.tip()
536 n = r.tip()
537 fp = make_file(repo, r, opts['output'], node=n)
537 fp = make_file(repo, r, opts['output'], node=n)
538 fp.write(r.read(n))
538 fp.write(r.read(n))
539
539
540 def clone(ui, source, dest=None, **opts):
540 def clone(ui, source, dest=None, **opts):
541 """make a copy of an existing repository"""
541 """make a copy of an existing repository"""
542 if dest is None:
542 if dest is None:
543 dest = os.path.basename(os.path.normpath(source))
543 dest = os.path.basename(os.path.normpath(source))
544
544
545 if os.path.exists(dest):
545 if os.path.exists(dest):
546 ui.warn("abort: destination '%s' already exists\n" % dest)
546 ui.warn("abort: destination '%s' already exists\n" % dest)
547 return 1
547 return 1
548
548
549 dest = os.path.realpath(dest)
549 dest = os.path.realpath(dest)
550
550
551 class Dircleanup:
551 class Dircleanup:
552 def __init__(self, dir_):
552 def __init__(self, dir_):
553 self.rmtree = shutil.rmtree
553 self.rmtree = shutil.rmtree
554 self.dir_ = dir_
554 self.dir_ = dir_
555 os.mkdir(dir_)
555 os.mkdir(dir_)
556 def close(self):
556 def close(self):
557 self.dir_ = None
557 self.dir_ = None
558 def __del__(self):
558 def __del__(self):
559 if self.dir_:
559 if self.dir_:
560 self.rmtree(self.dir_, True)
560 self.rmtree(self.dir_, True)
561
561
562 if opts['ssh']:
562 if opts['ssh']:
563 ui.setconfig("ui", "ssh", opts['ssh'])
563 ui.setconfig("ui", "ssh", opts['ssh'])
564 if opts['remotecmd']:
564 if opts['remotecmd']:
565 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
565 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
566
566
567 d = Dircleanup(dest)
567 d = Dircleanup(dest)
568 source = ui.expandpath(source)
568 source = ui.expandpath(source)
569 abspath = source
569 abspath = source
570 other = hg.repository(ui, source)
570 other = hg.repository(ui, source)
571
571
572 if other.dev() != -1:
572 if other.dev() != -1:
573 abspath = os.path.abspath(source)
573 abspath = os.path.abspath(source)
574 copyfile = (os.stat(dest).st_dev == other.dev()
574 copyfile = (os.stat(dest).st_dev == other.dev()
575 and getattr(os, 'link', None) or shutil.copy2)
575 and getattr(os, 'link', None) or shutil.copy2)
576 if copyfile is not shutil.copy2:
576 if copyfile is not shutil.copy2:
577 ui.note("cloning by hardlink\n")
577 ui.note("cloning by hardlink\n")
578 # we use a lock here because because we're not nicely ordered
578 # we use a lock here because because we're not nicely ordered
579 l = lock.lock(os.path.join(source, ".hg", "lock"))
579 l = lock.lock(os.path.join(source, ".hg", "lock"))
580
580
581 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
581 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
582 copyfile)
582 copyfile)
583 try:
583 try:
584 os.unlink(os.path.join(dest, ".hg", "dirstate"))
584 os.unlink(os.path.join(dest, ".hg", "dirstate"))
585 except OSError:
585 except OSError:
586 pass
586 pass
587
587
588 repo = hg.repository(ui, dest)
588 repo = hg.repository(ui, dest)
589
589
590 else:
590 else:
591 repo = hg.repository(ui, dest, create=1)
591 repo = hg.repository(ui, dest, create=1)
592 repo.pull(other)
592 repo.pull(other)
593
593
594 f = repo.opener("hgrc", "w")
594 f = repo.opener("hgrc", "w")
595 f.write("[paths]\n")
595 f.write("[paths]\n")
596 f.write("default = %s\n" % abspath)
596 f.write("default = %s\n" % abspath)
597
597
598 if not opts['noupdate']:
598 if not opts['noupdate']:
599 update(ui, repo)
599 update(ui, repo)
600
600
601 d.close()
601 d.close()
602
602
603 def commit(ui, repo, *pats, **opts):
603 def commit(ui, repo, *pats, **opts):
604 """commit the specified files or all outstanding changes"""
604 """commit the specified files or all outstanding changes"""
605 if opts['text']:
605 if opts['text']:
606 ui.warn("Warning: -t and --text is deprecated,"
606 ui.warn("Warning: -t and --text is deprecated,"
607 " please use -m or --message instead.\n")
607 " please use -m or --message instead.\n")
608 message = opts['message'] or opts['text']
608 message = opts['message'] or opts['text']
609 logfile = opts['logfile']
609 logfile = opts['logfile']
610 if not message and logfile:
610 if not message and logfile:
611 try:
611 try:
612 if logfile == '-':
612 if logfile == '-':
613 message = sys.stdin.read()
613 message = sys.stdin.read()
614 else:
614 else:
615 message = open(logfile).read()
615 message = open(logfile).read()
616 except IOError, why:
616 except IOError, why:
617 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
617 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
618
618
619 if opts['addremove']:
619 if opts['addremove']:
620 addremove(ui, repo, *pats, **opts)
620 addremove(ui, repo, *pats, **opts)
621 cwd = repo.getcwd()
621 cwd = repo.getcwd()
622 if not pats and cwd:
622 if not pats and cwd:
623 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
623 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
624 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
624 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
625 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
625 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
626 pats, opts)
626 pats, opts)
627 if pats:
627 if pats:
628 c, a, d, u = repo.changes(files=fns, match=match)
628 c, a, d, u = repo.changes(files=fns, match=match)
629 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
629 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
630 else:
630 else:
631 files = []
631 files = []
632 repo.commit(files, message, opts['user'], opts['date'], match)
632 repo.commit(files, message, opts['user'], opts['date'], match)
633
633
634 def copy(ui, repo, source, dest):
634 def copy(ui, repo, source, dest):
635 """mark a file as copied or renamed for the next commit"""
635 """mark a file as copied or renamed for the next commit"""
636 return repo.copy(*relpath(repo, (source, dest)))
636 return repo.copy(*relpath(repo, (source, dest)))
637
637
638 def debugcheckstate(ui, repo):
638 def debugcheckstate(ui, repo):
639 """validate the correctness of the current dirstate"""
639 """validate the correctness of the current dirstate"""
640 parent1, parent2 = repo.dirstate.parents()
640 parent1, parent2 = repo.dirstate.parents()
641 repo.dirstate.read()
641 repo.dirstate.read()
642 dc = repo.dirstate.map
642 dc = repo.dirstate.map
643 keys = dc.keys()
643 keys = dc.keys()
644 keys.sort()
644 keys.sort()
645 m1n = repo.changelog.read(parent1)[0]
645 m1n = repo.changelog.read(parent1)[0]
646 m2n = repo.changelog.read(parent2)[0]
646 m2n = repo.changelog.read(parent2)[0]
647 m1 = repo.manifest.read(m1n)
647 m1 = repo.manifest.read(m1n)
648 m2 = repo.manifest.read(m2n)
648 m2 = repo.manifest.read(m2n)
649 errors = 0
649 errors = 0
650 for f in dc:
650 for f in dc:
651 state = repo.dirstate.state(f)
651 state = repo.dirstate.state(f)
652 if state in "nr" and f not in m1:
652 if state in "nr" and f not in m1:
653 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
653 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
654 errors += 1
654 errors += 1
655 if state in "a" and f in m1:
655 if state in "a" and f in m1:
656 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
656 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
657 errors += 1
657 errors += 1
658 if state in "m" and f not in m1 and f not in m2:
658 if state in "m" and f not in m1 and f not in m2:
659 ui.warn("%s in state %s, but not in either manifest\n" %
659 ui.warn("%s in state %s, but not in either manifest\n" %
660 (f, state))
660 (f, state))
661 errors += 1
661 errors += 1
662 for f in m1:
662 for f in m1:
663 state = repo.dirstate.state(f)
663 state = repo.dirstate.state(f)
664 if state not in "nrm":
664 if state not in "nrm":
665 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
665 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
666 errors += 1
666 errors += 1
667 if errors:
667 if errors:
668 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
668 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
669
669
670 def debugconfig(ui):
670 def debugconfig(ui):
671 """show combined config settings from all hgrc files"""
671 """show combined config settings from all hgrc files"""
672 try:
672 try:
673 repo = hg.repository(ui)
673 repo = hg.repository(ui)
674 except hg.RepoError:
674 except hg.RepoError:
675 pass
675 pass
676 for section, name, value in ui.walkconfig():
676 for section, name, value in ui.walkconfig():
677 ui.write('%s.%s=%s\n' % (section, name, value))
677 ui.write('%s.%s=%s\n' % (section, name, value))
678
678
679 def debugstate(ui, repo):
679 def debugstate(ui, repo):
680 """show the contents of the current dirstate"""
680 """show the contents of the current dirstate"""
681 repo.dirstate.read()
681 repo.dirstate.read()
682 dc = repo.dirstate.map
682 dc = repo.dirstate.map
683 keys = dc.keys()
683 keys = dc.keys()
684 keys.sort()
684 keys.sort()
685 for file_ in keys:
685 for file_ in keys:
686 ui.write("%c %3o %10d %s %s\n"
686 ui.write("%c %3o %10d %s %s\n"
687 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
687 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
688 time.strftime("%x %X",
688 time.strftime("%x %X",
689 time.localtime(dc[file_][3])), file_))
689 time.localtime(dc[file_][3])), file_))
690
690
691 def debugdata(ui, file_, rev):
691 def debugdata(ui, file_, rev):
692 """dump the contents of an data file revision"""
692 """dump the contents of an data file revision"""
693 r = hg.revlog(hg.opener(""), file_[:-2] + ".i", file_)
693 r = hg.revlog(hg.opener(""), file_[:-2] + ".i", file_)
694 ui.write(r.revision(r.lookup(rev)))
694 ui.write(r.revision(r.lookup(rev)))
695
695
696 def debugindex(ui, file_):
696 def debugindex(ui, file_):
697 """dump the contents of an index file"""
697 """dump the contents of an index file"""
698 r = hg.revlog(hg.opener(""), file_, "")
698 r = hg.revlog(hg.opener(""), file_, "")
699 ui.write(" rev offset length base linkrev" +
699 ui.write(" rev offset length base linkrev" +
700 " nodeid p1 p2\n")
700 " nodeid p1 p2\n")
701 for i in range(r.count()):
701 for i in range(r.count()):
702 e = r.index[i]
702 e = r.index[i]
703 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
703 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
704 i, e[0], e[1], e[2], e[3],
704 i, e[0], e[1], e[2], e[3],
705 hg.short(e[6]), hg.short(e[4]), hg.short(e[5])))
705 hg.short(e[6]), hg.short(e[4]), hg.short(e[5])))
706
706
707 def debugindexdot(ui, file_):
707 def debugindexdot(ui, file_):
708 """dump an index DAG as a .dot file"""
708 """dump an index DAG as a .dot file"""
709 r = hg.revlog(hg.opener(""), file_, "")
709 r = hg.revlog(hg.opener(""), file_, "")
710 ui.write("digraph G {\n")
710 ui.write("digraph G {\n")
711 for i in range(r.count()):
711 for i in range(r.count()):
712 e = r.index[i]
712 e = r.index[i]
713 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
713 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
714 if e[5] != hg.nullid:
714 if e[5] != hg.nullid:
715 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
715 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
716 ui.write("}\n")
716 ui.write("}\n")
717
717
718 def debugwalk(ui, repo, *pats, **opts):
718 def debugwalk(ui, repo, *pats, **opts):
719 """show how files match on given patterns"""
719 """show how files match on given patterns"""
720 items = list(walk(repo, pats, opts))
720 items = list(walk(repo, pats, opts))
721 if not items:
721 if not items:
722 return
722 return
723 fmt = '%%s %%-%ds %%-%ds %%s\n' % (
723 fmt = '%%s %%-%ds %%-%ds %%s\n' % (
724 max([len(abs) for (src, abs, rel, exact) in items]),
724 max([len(abs) for (src, abs, rel, exact) in items]),
725 max([len(rel) for (src, abs, rel, exact) in items]))
725 max([len(rel) for (src, abs, rel, exact) in items]))
726 for src, abs, rel, exact in items:
726 for src, abs, rel, exact in items:
727 ui.write(fmt % (src, abs, rel, exact and 'exact' or ''))
727 ui.write(fmt % (src, abs, rel, exact and 'exact' or ''))
728
728
729 def diff(ui, repo, *pats, **opts):
729 def diff(ui, repo, *pats, **opts):
730 """diff working directory (or selected files)"""
730 """diff working directory (or selected files)"""
731 node1, node2 = None, None
731 node1, node2 = None, None
732 revs = [repo.lookup(x) for x in opts['rev']]
732 revs = [repo.lookup(x) for x in opts['rev']]
733
733
734 if len(revs) > 0:
734 if len(revs) > 0:
735 node1 = revs[0]
735 node1 = revs[0]
736 if len(revs) > 1:
736 if len(revs) > 1:
737 node2 = revs[1]
737 node2 = revs[1]
738 if len(revs) > 2:
738 if len(revs) > 2:
739 raise util.Abort("too many revisions to diff")
739 raise util.Abort("too many revisions to diff")
740
740
741 files = []
741 files = []
742 match = util.always
742 match = util.always
743 if pats:
743 if pats:
744 roots, match, results = makewalk(repo, pats, opts)
744 roots, match, results = makewalk(repo, pats, opts)
745 for src, abs, rel, exact in results:
745 for src, abs, rel, exact in results:
746 files.append(abs)
746 files.append(abs)
747
747
748 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
748 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
749 text=opts['text'])
749 text=opts['text'])
750
750
751 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
751 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
752 node = repo.lookup(changeset)
752 node = repo.lookup(changeset)
753 prev, other = repo.changelog.parents(node)
753 prev, other = repo.changelog.parents(node)
754 change = repo.changelog.read(node)
754 change = repo.changelog.read(node)
755
755
756 fp = make_file(repo, repo.changelog, opts['output'],
756 fp = make_file(repo, repo.changelog, opts['output'],
757 node=node, total=total, seqno=seqno,
757 node=node, total=total, seqno=seqno,
758 revwidth=revwidth)
758 revwidth=revwidth)
759 if fp != sys.stdout:
759 if fp != sys.stdout:
760 ui.note("%s\n" % fp.name)
760 ui.note("%s\n" % fp.name)
761
761
762 fp.write("# HG changeset patch\n")
762 fp.write("# HG changeset patch\n")
763 fp.write("# User %s\n" % change[1])
763 fp.write("# User %s\n" % change[1])
764 fp.write("# Node ID %s\n" % hg.hex(node))
764 fp.write("# Node ID %s\n" % hg.hex(node))
765 fp.write("# Parent %s\n" % hg.hex(prev))
765 fp.write("# Parent %s\n" % hg.hex(prev))
766 if other != hg.nullid:
766 if other != hg.nullid:
767 fp.write("# Parent %s\n" % hg.hex(other))
767 fp.write("# Parent %s\n" % hg.hex(other))
768 fp.write(change[4].rstrip())
768 fp.write(change[4].rstrip())
769 fp.write("\n\n")
769 fp.write("\n\n")
770
770
771 dodiff(fp, ui, repo, prev, node, text=opts['text'])
771 dodiff(fp, ui, repo, prev, node, text=opts['text'])
772 if fp != sys.stdout:
772 if fp != sys.stdout:
773 fp.close()
773 fp.close()
774
774
775 def export(ui, repo, *changesets, **opts):
775 def export(ui, repo, *changesets, **opts):
776 """dump the header and diffs for one or more changesets"""
776 """dump the header and diffs for one or more changesets"""
777 if not changesets:
777 if not changesets:
778 raise util.Abort("export requires at least one changeset")
778 raise util.Abort("export requires at least one changeset")
779 seqno = 0
779 seqno = 0
780 revs = list(revrange(ui, repo, changesets))
780 revs = list(revrange(ui, repo, changesets))
781 total = len(revs)
781 total = len(revs)
782 revwidth = max(map(len, revs))
782 revwidth = max(map(len, revs))
783 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
783 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
784 for cset in revs:
784 for cset in revs:
785 seqno += 1
785 seqno += 1
786 doexport(ui, repo, cset, seqno, total, revwidth, opts)
786 doexport(ui, repo, cset, seqno, total, revwidth, opts)
787
787
788 def forget(ui, repo, *pats, **opts):
788 def forget(ui, repo, *pats, **opts):
789 """don't add the specified files on the next commit"""
789 """don't add the specified files on the next commit"""
790 forget = []
790 forget = []
791 for src, abs, rel, exact in walk(repo, pats, opts):
791 for src, abs, rel, exact in walk(repo, pats, opts):
792 if repo.dirstate.state(abs) == 'a':
792 if repo.dirstate.state(abs) == 'a':
793 forget.append(abs)
793 forget.append(abs)
794 if not exact:
794 if not exact:
795 ui.status('forgetting ', rel, '\n')
795 ui.status('forgetting ', rel, '\n')
796 repo.forget(forget)
796 repo.forget(forget)
797
797
798 def grep(ui, repo, pattern=None, *pats, **opts):
798 def grep(ui, repo, pattern=None, *pats, **opts):
799 """search for a pattern in specified files and revisions"""
799 """search for a pattern in specified files and revisions"""
800 if pattern is None:
800 if pattern is None:
801 pattern = opts['regexp']
801 pattern = opts['regexp']
802 if not pattern:
802 if not pattern:
803 raise util.Abort('no pattern to search for')
803 raise util.Abort('no pattern to search for')
804 reflags = 0
804 reflags = 0
805 if opts['ignore_case']:
805 if opts['ignore_case']:
806 reflags |= re.I
806 reflags |= re.I
807 regexp = re.compile(pattern, reflags)
807 regexp = re.compile(pattern, reflags)
808 sep, end = ':', '\n'
808 sep, end = ':', '\n'
809 if opts['null'] or opts['print0']:
809 if opts['null'] or opts['print0']:
810 sep = end = '\0'
810 sep = end = '\0'
811
811
812 fcache = {}
812 fcache = {}
813 def getfile(fn):
813 def getfile(fn):
814 if fn not in fcache:
814 if fn not in fcache:
815 fcache[fn] = repo.file(fn)
815 fcache[fn] = repo.file(fn)
816 return fcache[fn]
816 return fcache[fn]
817
817
818 def matchlines(body):
818 def matchlines(body):
819 begin = 0
819 begin = 0
820 linenum = 0
820 linenum = 0
821 while True:
821 while True:
822 match = regexp.search(body, begin)
822 match = regexp.search(body, begin)
823 if not match:
823 if not match:
824 break
824 break
825 mstart, mend = match.span()
825 mstart, mend = match.span()
826 linenum += body.count('\n', begin, mstart) + 1
826 linenum += body.count('\n', begin, mstart) + 1
827 lstart = body.rfind('\n', begin, mstart) + 1 or begin
827 lstart = body.rfind('\n', begin, mstart) + 1 or begin
828 lend = body.find('\n', mend)
828 lend = body.find('\n', mend)
829 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
829 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
830 begin = lend + 1
830 begin = lend + 1
831
831
832 class linestate:
832 class linestate:
833 def __init__(self, line, linenum, colstart, colend):
833 def __init__(self, line, linenum, colstart, colend):
834 self.line = line
834 self.line = line
835 self.linenum = linenum
835 self.linenum = linenum
836 self.colstart = colstart
836 self.colstart = colstart
837 self.colend = colend
837 self.colend = colend
838 def __eq__(self, other):
838 def __eq__(self, other):
839 return self.line == other.line
839 return self.line == other.line
840 def __hash__(self):
840 def __hash__(self):
841 return hash(self.line)
841 return hash(self.line)
842
842
843 matches = {}
843 matches = {}
844 def grepbody(fn, rev, body):
844 def grepbody(fn, rev, body):
845 matches[rev].setdefault(fn, {})
845 matches[rev].setdefault(fn, {})
846 m = matches[rev][fn]
846 m = matches[rev][fn]
847 for lnum, cstart, cend, line in matchlines(body):
847 for lnum, cstart, cend, line in matchlines(body):
848 s = linestate(line, lnum, cstart, cend)
848 s = linestate(line, lnum, cstart, cend)
849 m[s] = s
849 m[s] = s
850
850
851 prev = {}
851 prev = {}
852 def display(fn, rev, states, prevstates):
852 def display(fn, rev, states, prevstates):
853 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
853 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
854 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
854 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
855 for l in diff:
855 for l in diff:
856 if incrementing:
856 if incrementing:
857 change = ((l in prevstates) and '-') or '+'
857 change = ((l in prevstates) and '-') or '+'
858 r = rev
858 r = rev
859 else:
859 else:
860 change = ((l in states) and '-') or '+'
860 change = ((l in states) and '-') or '+'
861 r = prev[fn]
861 r = prev[fn]
862 ui.write('%s:%s:%s:%s%s\n' % (fn, r, l.linenum, change, l.line))
862 ui.write('%s:%s:%s:%s%s\n' % (fn, r, l.linenum, change, l.line))
863
863
864 fstate = {}
864 fstate = {}
865 for st, rev, fns in walkchangerevs(ui, repo, repo.getcwd(), pats, opts):
865 for st, rev, fns in walkchangerevs(ui, repo, repo.getcwd(), pats, opts):
866 if st == 'window':
866 if st == 'window':
867 incrementing = rev
867 incrementing = rev
868 matches.clear()
868 matches.clear()
869 elif st == 'add':
869 elif st == 'add':
870 change = repo.changelog.read(repo.lookup(str(rev)))
870 change = repo.changelog.read(repo.lookup(str(rev)))
871 mf = repo.manifest.read(change[0])
871 mf = repo.manifest.read(change[0])
872 matches[rev] = {}
872 matches[rev] = {}
873 for fn in fns:
873 for fn in fns:
874 fstate.setdefault(fn, {})
874 fstate.setdefault(fn, {})
875 try:
875 try:
876 grepbody(fn, rev, getfile(fn).read(mf[fn]))
876 grepbody(fn, rev, getfile(fn).read(mf[fn]))
877 except KeyError:
877 except KeyError:
878 pass
878 pass
879 elif st == 'iter':
879 elif st == 'iter':
880 states = matches[rev].items()
880 states = matches[rev].items()
881 states.sort()
881 states.sort()
882 for fn, m in states:
882 for fn, m in states:
883 if incrementing or fstate[fn]:
883 if incrementing or fstate[fn]:
884 display(fn, rev, m, fstate[fn])
884 display(fn, rev, m, fstate[fn])
885 fstate[fn] = m
885 fstate[fn] = m
886 prev[fn] = rev
886 prev[fn] = rev
887
887
888 if not incrementing:
888 if not incrementing:
889 fstate = fstate.items()
889 fstate = fstate.items()
890 fstate.sort()
890 fstate.sort()
891 for fn, state in fstate:
891 for fn, state in fstate:
892 display(fn, rev, {}, state)
892 display(fn, rev, {}, state)
893
893
894 def heads(ui, repo, **opts):
894 def heads(ui, repo, **opts):
895 """show current repository heads"""
895 """show current repository heads"""
896 heads = repo.changelog.heads()
896 heads = repo.changelog.heads()
897 br = None
897 br = None
898 if opts['branches']:
898 if opts['branches']:
899 br = repo.branchlookup(heads)
899 br = repo.branchlookup(heads)
900 for n in repo.changelog.heads():
900 for n in repo.changelog.heads():
901 show_changeset(ui, repo, changenode=n, brinfo=br)
901 show_changeset(ui, repo, changenode=n, brinfo=br)
902
902
903 def identify(ui, repo):
903 def identify(ui, repo):
904 """print information about the working copy"""
904 """print information about the working copy"""
905 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
905 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
906 if not parents:
906 if not parents:
907 ui.write("unknown\n")
907 ui.write("unknown\n")
908 return
908 return
909
909
910 hexfunc = ui.verbose and hg.hex or hg.short
910 hexfunc = ui.verbose and hg.hex or hg.short
911 (c, a, d, u) = repo.changes()
911 (c, a, d, u) = repo.changes()
912 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
912 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
913 (c or a or d) and "+" or "")]
913 (c or a or d) and "+" or "")]
914
914
915 if not ui.quiet:
915 if not ui.quiet:
916 # multiple tags for a single parent separated by '/'
916 # multiple tags for a single parent separated by '/'
917 parenttags = ['/'.join(tags)
917 parenttags = ['/'.join(tags)
918 for tags in map(repo.nodetags, parents) if tags]
918 for tags in map(repo.nodetags, parents) if tags]
919 # tags for multiple parents separated by ' + '
919 # tags for multiple parents separated by ' + '
920 if parenttags:
920 if parenttags:
921 output.append(' + '.join(parenttags))
921 output.append(' + '.join(parenttags))
922
922
923 ui.write("%s\n" % ' '.join(output))
923 ui.write("%s\n" % ' '.join(output))
924
924
925 def import_(ui, repo, patch1, *patches, **opts):
925 def import_(ui, repo, patch1, *patches, **opts):
926 """import an ordered set of patches"""
926 """import an ordered set of patches"""
927 patches = (patch1,) + patches
927 patches = (patch1,) + patches
928
928
929 if not opts['force']:
929 if not opts['force']:
930 (c, a, d, u) = repo.changes()
930 (c, a, d, u) = repo.changes()
931 if c or a or d:
931 if c or a or d:
932 ui.warn("abort: outstanding uncommitted changes!\n")
932 ui.warn("abort: outstanding uncommitted changes!\n")
933 return 1
933 return 1
934
934
935 d = opts["base"]
935 d = opts["base"]
936 strip = opts["strip"]
936 strip = opts["strip"]
937
937
938 for patch in patches:
938 for patch in patches:
939 ui.status("applying %s\n" % patch)
939 ui.status("applying %s\n" % patch)
940 pf = os.path.join(d, patch)
940 pf = os.path.join(d, patch)
941
941
942 message = []
942 message = []
943 user = None
943 user = None
944 hgpatch = False
944 hgpatch = False
945 for line in file(pf):
945 for line in file(pf):
946 line = line.rstrip()
946 line = line.rstrip()
947 if line.startswith("--- ") or line.startswith("diff -r"):
947 if line.startswith("--- ") or line.startswith("diff -r"):
948 break
948 break
949 elif hgpatch:
949 elif hgpatch:
950 # parse values when importing the result of an hg export
950 # parse values when importing the result of an hg export
951 if line.startswith("# User "):
951 if line.startswith("# User "):
952 user = line[7:]
952 user = line[7:]
953 ui.debug('User: %s\n' % user)
953 ui.debug('User: %s\n' % user)
954 elif not line.startswith("# ") and line:
954 elif not line.startswith("# ") and line:
955 message.append(line)
955 message.append(line)
956 hgpatch = False
956 hgpatch = False
957 elif line == '# HG changeset patch':
957 elif line == '# HG changeset patch':
958 hgpatch = True
958 hgpatch = True
959 message = [] # We may have collected garbage
959 message = [] # We may have collected garbage
960 else:
960 else:
961 message.append(line)
961 message.append(line)
962
962
963 # make sure message isn't empty
963 # make sure message isn't empty
964 if not message:
964 if not message:
965 message = "imported patch %s\n" % patch
965 message = "imported patch %s\n" % patch
966 else:
966 else:
967 message = "%s\n" % '\n'.join(message)
967 message = "%s\n" % '\n'.join(message)
968 ui.debug('message:\n%s\n' % message)
968 ui.debug('message:\n%s\n' % message)
969
969
970 f = os.popen("patch -p%d < '%s'" % (strip, pf))
970 f = os.popen("patch -p%d < '%s'" % (strip, pf))
971 files = []
971 files = []
972 for l in f.read().splitlines():
972 for l in f.read().splitlines():
973 l.rstrip('\r\n');
973 l.rstrip('\r\n');
974 ui.status("%s\n" % l)
974 ui.status("%s\n" % l)
975 if l.startswith('patching file '):
975 if l.startswith('patching file '):
976 pf = l[14:]
976 pf = l[14:]
977 if pf not in files:
977 if pf not in files:
978 files.append(pf)
978 files.append(pf)
979 patcherr = f.close()
979 patcherr = f.close()
980 if patcherr:
980 if patcherr:
981 raise util.Abort("patch failed")
981 raise util.Abort("patch failed")
982
982
983 if len(files) > 0:
983 if len(files) > 0:
984 addremove(ui, repo, *files)
984 addremove(ui, repo, *files)
985 repo.commit(files, message, user)
985 repo.commit(files, message, user)
986
986
987 def incoming(ui, repo, source="default"):
987 def incoming(ui, repo, source="default"):
988 """show new changesets found in source"""
988 """show new changesets found in source"""
989 source = ui.expandpath(source)
989 source = ui.expandpath(source)
990 other = hg.repository(ui, source)
990 other = hg.repository(ui, source)
991 if not other.local():
991 if not other.local():
992 ui.warn("abort: incoming doesn't work for remote"
992 ui.warn("abort: incoming doesn't work for remote"
993 + " repositories yet, sorry!\n")
993 + " repositories yet, sorry!\n")
994 return 1
994 return 1
995 o = repo.findincoming(other)
995 o = repo.findincoming(other)
996 if not o:
996 if not o:
997 return
997 return
998 o = other.newer(o)
998 o = other.newer(o)
999 o.reverse()
999 o.reverse()
1000 for n in o:
1000 for n in o:
1001 show_changeset(ui, other, changenode=n)
1001 show_changeset(ui, other, changenode=n)
1002
1002
1003 def init(ui, dest="."):
1003 def init(ui, dest="."):
1004 """create a new repository in the given directory"""
1004 """create a new repository in the given directory"""
1005 if not os.path.exists(dest):
1005 if not os.path.exists(dest):
1006 os.mkdir(dest)
1006 os.mkdir(dest)
1007 hg.repository(ui, dest, create=1)
1007 hg.repository(ui, dest, create=1)
1008
1008
1009 def locate(ui, repo, *pats, **opts):
1009 def locate(ui, repo, *pats, **opts):
1010 """locate files matching specific patterns"""
1010 """locate files matching specific patterns"""
1011 end = opts['print0'] and '\0' or '\n'
1011 end = opts['print0'] and '\0' or '\n'
1012
1012
1013 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1013 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1014 if repo.dirstate.state(abs) == '?':
1014 if repo.dirstate.state(abs) == '?':
1015 continue
1015 continue
1016 if opts['fullpath']:
1016 if opts['fullpath']:
1017 ui.write(os.path.join(repo.root, abs), end)
1017 ui.write(os.path.join(repo.root, abs), end)
1018 else:
1018 else:
1019 ui.write(rel, end)
1019 ui.write(rel, end)
1020
1020
1021 def log(ui, repo, *pats, **opts):
1021 def log(ui, repo, *pats, **opts):
1022 """show revision history of entire repository or files"""
1022 """show revision history of entire repository or files"""
1023 class dui:
1023 class dui:
1024 # Implement and delegate some ui protocol. Save hunks of
1024 # Implement and delegate some ui protocol. Save hunks of
1025 # output for later display in the desired order.
1025 # output for later display in the desired order.
1026 def __init__(self, ui):
1026 def __init__(self, ui):
1027 self.ui = ui
1027 self.ui = ui
1028 self.hunk = {}
1028 self.hunk = {}
1029 def bump(self, rev):
1029 def bump(self, rev):
1030 self.rev = rev
1030 self.rev = rev
1031 self.hunk[rev] = []
1031 self.hunk[rev] = []
1032 def note(self, *args):
1032 def note(self, *args):
1033 if self.verbose:
1033 if self.verbose:
1034 self.write(*args)
1034 self.write(*args)
1035 def status(self, *args):
1035 def status(self, *args):
1036 if not self.quiet:
1036 if not self.quiet:
1037 self.write(*args)
1037 self.write(*args)
1038 def write(self, *args):
1038 def write(self, *args):
1039 self.hunk[self.rev].append(args)
1039 self.hunk[self.rev].append(args)
1040 def __getattr__(self, key):
1040 def __getattr__(self, key):
1041 return getattr(self.ui, key)
1041 return getattr(self.ui, key)
1042 cwd = repo.getcwd()
1042 cwd = repo.getcwd()
1043 if not pats and cwd:
1043 if not pats and cwd:
1044 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1044 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1045 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1045 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1046 for st, rev, fns in walkchangerevs(ui, repo, (pats and cwd) or '', pats,
1046 for st, rev, fns in walkchangerevs(ui, repo, (pats and cwd) or '', pats,
1047 opts):
1047 opts):
1048 if st == 'window':
1048 if st == 'window':
1049 du = dui(ui)
1049 du = dui(ui)
1050 elif st == 'add':
1050 elif st == 'add':
1051 du.bump(rev)
1051 du.bump(rev)
1052 show_changeset(du, repo, rev)
1052 show_changeset(du, repo, rev)
1053 if opts['patch']:
1053 if opts['patch']:
1054 changenode = repo.changelog.node(rev)
1054 changenode = repo.changelog.node(rev)
1055 prev, other = repo.changelog.parents(changenode)
1055 prev, other = repo.changelog.parents(changenode)
1056 dodiff(du, du, repo, prev, changenode, fns)
1056 dodiff(du, du, repo, prev, changenode, fns)
1057 du.write("\n\n")
1057 du.write("\n\n")
1058 elif st == 'iter':
1058 elif st == 'iter':
1059 for args in du.hunk[rev]:
1059 for args in du.hunk[rev]:
1060 ui.write(*args)
1060 ui.write(*args)
1061
1061
1062 def manifest(ui, repo, rev=None):
1062 def manifest(ui, repo, rev=None):
1063 """output the latest or given revision of the project manifest"""
1063 """output the latest or given revision of the project manifest"""
1064 if rev:
1064 if rev:
1065 try:
1065 try:
1066 # assume all revision numbers are for changesets
1066 # assume all revision numbers are for changesets
1067 n = repo.lookup(rev)
1067 n = repo.lookup(rev)
1068 change = repo.changelog.read(n)
1068 change = repo.changelog.read(n)
1069 n = change[0]
1069 n = change[0]
1070 except hg.RepoError:
1070 except hg.RepoError:
1071 n = repo.manifest.lookup(rev)
1071 n = repo.manifest.lookup(rev)
1072 else:
1072 else:
1073 n = repo.manifest.tip()
1073 n = repo.manifest.tip()
1074 m = repo.manifest.read(n)
1074 m = repo.manifest.read(n)
1075 mf = repo.manifest.readflags(n)
1075 mf = repo.manifest.readflags(n)
1076 files = m.keys()
1076 files = m.keys()
1077 files.sort()
1077 files.sort()
1078
1078
1079 for f in files:
1079 for f in files:
1080 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
1080 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
1081
1081
1082 def outgoing(ui, repo, dest="default-push"):
1082 def outgoing(ui, repo, dest="default-push"):
1083 """show changesets not found in destination"""
1083 """show changesets not found in destination"""
1084 dest = ui.expandpath(dest)
1084 dest = ui.expandpath(dest)
1085 other = hg.repository(ui, dest)
1085 other = hg.repository(ui, dest)
1086 o = repo.findoutgoing(other)
1086 o = repo.findoutgoing(other)
1087 o = repo.newer(o)
1087 o = repo.newer(o)
1088 o.reverse()
1088 o.reverse()
1089 for n in o:
1089 for n in o:
1090 show_changeset(ui, repo, changenode=n)
1090 show_changeset(ui, repo, changenode=n)
1091
1091
1092 def parents(ui, repo, rev=None):
1092 def parents(ui, repo, rev=None):
1093 """show the parents of the working dir or revision"""
1093 """show the parents of the working dir or revision"""
1094 if rev:
1094 if rev:
1095 p = repo.changelog.parents(repo.lookup(rev))
1095 p = repo.changelog.parents(repo.lookup(rev))
1096 else:
1096 else:
1097 p = repo.dirstate.parents()
1097 p = repo.dirstate.parents()
1098
1098
1099 for n in p:
1099 for n in p:
1100 if n != hg.nullid:
1100 if n != hg.nullid:
1101 show_changeset(ui, repo, changenode=n)
1101 show_changeset(ui, repo, changenode=n)
1102
1102
1103 def paths(ui, search=None):
1103 def paths(ui, search=None):
1104 """show definition of symbolic path names"""
1104 """show definition of symbolic path names"""
1105 try:
1105 try:
1106 repo = hg.repository(ui=ui)
1106 repo = hg.repository(ui=ui)
1107 except hg.RepoError:
1107 except hg.RepoError:
1108 pass
1108 pass
1109
1109
1110 if search:
1110 if search:
1111 for name, path in ui.configitems("paths"):
1111 for name, path in ui.configitems("paths"):
1112 if name == search:
1112 if name == search:
1113 ui.write("%s\n" % path)
1113 ui.write("%s\n" % path)
1114 return
1114 return
1115 ui.warn("not found!\n")
1115 ui.warn("not found!\n")
1116 return 1
1116 return 1
1117 else:
1117 else:
1118 for name, path in ui.configitems("paths"):
1118 for name, path in ui.configitems("paths"):
1119 ui.write("%s = %s\n" % (name, path))
1119 ui.write("%s = %s\n" % (name, path))
1120
1120
1121 def pull(ui, repo, source="default", **opts):
1121 def pull(ui, repo, source="default", **opts):
1122 """pull changes from the specified source"""
1122 """pull changes from the specified source"""
1123 source = ui.expandpath(source)
1123 source = ui.expandpath(source)
1124 ui.status('pulling from %s\n' % (source))
1124 ui.status('pulling from %s\n' % (source))
1125
1125
1126 if opts['ssh']:
1126 if opts['ssh']:
1127 ui.setconfig("ui", "ssh", opts['ssh'])
1127 ui.setconfig("ui", "ssh", opts['ssh'])
1128 if opts['remotecmd']:
1128 if opts['remotecmd']:
1129 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1129 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1130
1130
1131 other = hg.repository(ui, source)
1131 other = hg.repository(ui, source)
1132 r = repo.pull(other)
1132 r = repo.pull(other)
1133 if not r:
1133 if not r:
1134 if opts['update']:
1134 if opts['update']:
1135 return update(ui, repo)
1135 return update(ui, repo)
1136 else:
1136 else:
1137 ui.status("(run 'hg update' to get a working copy)\n")
1137 ui.status("(run 'hg update' to get a working copy)\n")
1138
1138
1139 return r
1139 return r
1140
1140
1141 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1141 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1142 """push changes to the specified destination"""
1142 """push changes to the specified destination"""
1143 dest = ui.expandpath(dest)
1143 dest = ui.expandpath(dest)
1144 ui.status('pushing to %s\n' % (dest))
1144 ui.status('pushing to %s\n' % (dest))
1145
1145
1146 if ssh:
1146 if ssh:
1147 ui.setconfig("ui", "ssh", ssh)
1147 ui.setconfig("ui", "ssh", ssh)
1148 if remotecmd:
1148 if remotecmd:
1149 ui.setconfig("ui", "remotecmd", remotecmd)
1149 ui.setconfig("ui", "remotecmd", remotecmd)
1150
1150
1151 other = hg.repository(ui, dest)
1151 other = hg.repository(ui, dest)
1152 r = repo.push(other, force)
1152 r = repo.push(other, force)
1153 return r
1153 return r
1154
1154
1155 def rawcommit(ui, repo, *flist, **rc):
1155 def rawcommit(ui, repo, *flist, **rc):
1156 "raw commit interface"
1156 "raw commit interface"
1157 if rc['text']:
1157 if rc['text']:
1158 ui.warn("Warning: -t and --text is deprecated,"
1158 ui.warn("Warning: -t and --text is deprecated,"
1159 " please use -m or --message instead.\n")
1159 " please use -m or --message instead.\n")
1160 message = rc['message'] or rc['text']
1160 message = rc['message'] or rc['text']
1161 if not message and rc['logfile']:
1161 if not message and rc['logfile']:
1162 try:
1162 try:
1163 message = open(rc['logfile']).read()
1163 message = open(rc['logfile']).read()
1164 except IOError:
1164 except IOError:
1165 pass
1165 pass
1166 if not message and not rc['logfile']:
1166 if not message and not rc['logfile']:
1167 ui.warn("abort: missing commit message\n")
1167 ui.warn("abort: missing commit message\n")
1168 return 1
1168 return 1
1169
1169
1170 files = relpath(repo, list(flist))
1170 files = relpath(repo, list(flist))
1171 if rc['files']:
1171 if rc['files']:
1172 files += open(rc['files']).read().splitlines()
1172 files += open(rc['files']).read().splitlines()
1173
1173
1174 rc['parent'] = map(repo.lookup, rc['parent'])
1174 rc['parent'] = map(repo.lookup, rc['parent'])
1175
1175
1176 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1176 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1177
1177
1178 def recover(ui, repo):
1178 def recover(ui, repo):
1179 """roll back an interrupted transaction"""
1179 """roll back an interrupted transaction"""
1180 repo.recover()
1180 repo.recover()
1181
1181
1182 def remove(ui, repo, file1, *files):
1182 def remove(ui, repo, file1, *files):
1183 """remove the specified files on the next commit"""
1183 """remove the specified files on the next commit"""
1184 repo.remove(relpath(repo, (file1,) + files))
1184 repo.remove(relpath(repo, (file1,) + files))
1185
1185
1186 def revert(ui, repo, *names, **opts):
1186 def revert(ui, repo, *names, **opts):
1187 """revert modified files or dirs back to their unmodified states"""
1187 """revert modified files or dirs back to their unmodified states"""
1188 node = opts['rev'] and repo.lookup(opts['rev']) or \
1188 node = opts['rev'] and repo.lookup(opts['rev']) or \
1189 repo.dirstate.parents()[0]
1189 repo.dirstate.parents()[0]
1190 root = os.path.realpath(repo.root)
1190 root = os.path.realpath(repo.root)
1191
1191
1192 def trimpath(p):
1192 def trimpath(p):
1193 p = os.path.realpath(p)
1193 p = os.path.realpath(p)
1194 if p.startswith(root):
1194 if p.startswith(root):
1195 rest = p[len(root):]
1195 rest = p[len(root):]
1196 if not rest:
1196 if not rest:
1197 return rest
1197 return rest
1198 if p.startswith(os.sep):
1198 if p.startswith(os.sep):
1199 return rest[1:]
1199 return rest[1:]
1200 return p
1200 return p
1201
1201
1202 relnames = map(trimpath, names or [os.getcwd()])
1202 relnames = map(trimpath, names or [os.getcwd()])
1203 chosen = {}
1203 chosen = {}
1204
1204
1205 def choose(name):
1205 def choose(name):
1206 def body(name):
1206 def body(name):
1207 for r in relnames:
1207 for r in relnames:
1208 if not name.startswith(r):
1208 if not name.startswith(r):
1209 continue
1209 continue
1210 rest = name[len(r):]
1210 rest = name[len(r):]
1211 if not rest:
1211 if not rest:
1212 return r, True
1212 return r, True
1213 depth = rest.count(os.sep)
1213 depth = rest.count(os.sep)
1214 if not r:
1214 if not r:
1215 if depth == 0 or not opts['nonrecursive']:
1215 if depth == 0 or not opts['nonrecursive']:
1216 return r, True
1216 return r, True
1217 elif rest[0] == os.sep:
1217 elif rest[0] == os.sep:
1218 if depth == 1 or not opts['nonrecursive']:
1218 if depth == 1 or not opts['nonrecursive']:
1219 return r, True
1219 return r, True
1220 return None, False
1220 return None, False
1221 relname, ret = body(name)
1221 relname, ret = body(name)
1222 if ret:
1222 if ret:
1223 chosen[relname] = 1
1223 chosen[relname] = 1
1224 return ret
1224 return ret
1225
1225
1226 r = repo.update(node, False, True, choose, False)
1226 r = repo.update(node, False, True, choose, False)
1227 for n in relnames:
1227 for n in relnames:
1228 if n not in chosen:
1228 if n not in chosen:
1229 ui.warn('error: no matches for %s\n' % n)
1229 ui.warn('error: no matches for %s\n' % n)
1230 r = 1
1230 r = 1
1231 sys.stdout.flush()
1231 sys.stdout.flush()
1232 return r
1232 return r
1233
1233
1234 def root(ui, repo):
1234 def root(ui, repo):
1235 """print the root (top) of the current working dir"""
1235 """print the root (top) of the current working dir"""
1236 ui.write(repo.root + "\n")
1236 ui.write(repo.root + "\n")
1237
1237
1238 def serve(ui, repo, **opts):
1238 def serve(ui, repo, **opts):
1239 """export the repository via HTTP"""
1239 """export the repository via HTTP"""
1240
1240
1241 if opts["stdio"]:
1241 if opts["stdio"]:
1242 fin, fout = sys.stdin, sys.stdout
1242 fin, fout = sys.stdin, sys.stdout
1243 sys.stdout = sys.stderr
1243 sys.stdout = sys.stderr
1244
1244
1245 def getarg():
1245 def getarg():
1246 argline = fin.readline()[:-1]
1246 argline = fin.readline()[:-1]
1247 arg, l = argline.split()
1247 arg, l = argline.split()
1248 val = fin.read(int(l))
1248 val = fin.read(int(l))
1249 return arg, val
1249 return arg, val
1250 def respond(v):
1250 def respond(v):
1251 fout.write("%d\n" % len(v))
1251 fout.write("%d\n" % len(v))
1252 fout.write(v)
1252 fout.write(v)
1253 fout.flush()
1253 fout.flush()
1254
1254
1255 lock = None
1255 lock = None
1256
1256
1257 while 1:
1257 while 1:
1258 cmd = fin.readline()[:-1]
1258 cmd = fin.readline()[:-1]
1259 if cmd == '':
1259 if cmd == '':
1260 return
1260 return
1261 if cmd == "heads":
1261 if cmd == "heads":
1262 h = repo.heads()
1262 h = repo.heads()
1263 respond(" ".join(map(hg.hex, h)) + "\n")
1263 respond(" ".join(map(hg.hex, h)) + "\n")
1264 if cmd == "lock":
1264 if cmd == "lock":
1265 lock = repo.lock()
1265 lock = repo.lock()
1266 respond("")
1266 respond("")
1267 if cmd == "unlock":
1267 if cmd == "unlock":
1268 if lock:
1268 if lock:
1269 lock.release()
1269 lock.release()
1270 lock = None
1270 lock = None
1271 respond("")
1271 respond("")
1272 elif cmd == "branches":
1272 elif cmd == "branches":
1273 arg, nodes = getarg()
1273 arg, nodes = getarg()
1274 nodes = map(hg.bin, nodes.split(" "))
1274 nodes = map(hg.bin, nodes.split(" "))
1275 r = []
1275 r = []
1276 for b in repo.branches(nodes):
1276 for b in repo.branches(nodes):
1277 r.append(" ".join(map(hg.hex, b)) + "\n")
1277 r.append(" ".join(map(hg.hex, b)) + "\n")
1278 respond("".join(r))
1278 respond("".join(r))
1279 elif cmd == "between":
1279 elif cmd == "between":
1280 arg, pairs = getarg()
1280 arg, pairs = getarg()
1281 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1281 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1282 r = []
1282 r = []
1283 for b in repo.between(pairs):
1283 for b in repo.between(pairs):
1284 r.append(" ".join(map(hg.hex, b)) + "\n")
1284 r.append(" ".join(map(hg.hex, b)) + "\n")
1285 respond("".join(r))
1285 respond("".join(r))
1286 elif cmd == "changegroup":
1286 elif cmd == "changegroup":
1287 nodes = []
1287 nodes = []
1288 arg, roots = getarg()
1288 arg, roots = getarg()
1289 nodes = map(hg.bin, roots.split(" "))
1289 nodes = map(hg.bin, roots.split(" "))
1290
1290
1291 cg = repo.changegroup(nodes)
1291 cg = repo.changegroup(nodes)
1292 while 1:
1292 while 1:
1293 d = cg.read(4096)
1293 d = cg.read(4096)
1294 if not d:
1294 if not d:
1295 break
1295 break
1296 fout.write(d)
1296 fout.write(d)
1297
1297
1298 fout.flush()
1298 fout.flush()
1299
1299
1300 elif cmd == "addchangegroup":
1300 elif cmd == "addchangegroup":
1301 if not lock:
1301 if not lock:
1302 respond("not locked")
1302 respond("not locked")
1303 continue
1303 continue
1304 respond("")
1304 respond("")
1305
1305
1306 r = repo.addchangegroup(fin)
1306 r = repo.addchangegroup(fin)
1307 respond("")
1307 respond("")
1308
1308
1309 optlist = "name templates style address port ipv6 accesslog errorlog"
1309 optlist = "name templates style address port ipv6 accesslog errorlog"
1310 for o in optlist.split():
1310 for o in optlist.split():
1311 if opts[o]:
1311 if opts[o]:
1312 ui.setconfig("web", o, opts[o])
1312 ui.setconfig("web", o, opts[o])
1313
1313
1314 httpd = hgweb.create_server(repo)
1314 httpd = hgweb.create_server(repo)
1315
1315
1316 if ui.verbose:
1316 if ui.verbose:
1317 addr, port = httpd.socket.getsockname()
1317 addr, port = httpd.socket.getsockname()
1318 if addr == '0.0.0.0':
1318 if addr == '0.0.0.0':
1319 addr = socket.gethostname()
1319 addr = socket.gethostname()
1320 else:
1320 else:
1321 try:
1321 try:
1322 addr = socket.gethostbyaddr(addr)[0]
1322 addr = socket.gethostbyaddr(addr)[0]
1323 except socket.error:
1323 except socket.error:
1324 pass
1324 pass
1325 if port != 80:
1325 if port != 80:
1326 ui.status('listening at http://%s:%d/\n' % (addr, port))
1326 ui.status('listening at http://%s:%d/\n' % (addr, port))
1327 else:
1327 else:
1328 ui.status('listening at http://%s/\n' % addr)
1328 ui.status('listening at http://%s/\n' % addr)
1329 httpd.serve_forever()
1329 httpd.serve_forever()
1330
1330
1331 def status(ui, repo, *pats, **opts):
1331 def status(ui, repo, *pats, **opts):
1332 '''show changed files in the working directory
1332 '''show changed files in the working directory
1333
1333
1334 M = modified
1334 M = modified
1335 A = added
1335 A = added
1336 R = removed
1336 R = removed
1337 ? = not tracked
1337 ? = not tracked
1338 '''
1338 '''
1339
1339
1340 cwd = repo.getcwd()
1340 cwd = repo.getcwd()
1341 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1341 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1342 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1342 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1343 for n in repo.changes(files=files, match=matchfn)]
1343 for n in repo.changes(files=files, match=matchfn)]
1344
1344
1345 changetypes = [('modified', 'M', c),
1345 changetypes = [('modified', 'M', c),
1346 ('added', 'A', a),
1346 ('added', 'A', a),
1347 ('removed', 'R', d),
1347 ('removed', 'R', d),
1348 ('unknown', '?', u)]
1348 ('unknown', '?', u)]
1349
1349
1350 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1350 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1351 or changetypes):
1351 or changetypes):
1352 for f in changes:
1352 for f in changes:
1353 ui.write("%s %s\n" % (char, f))
1353 ui.write("%s %s\n" % (char, f))
1354
1354
1355 def tag(ui, repo, name, rev=None, **opts):
1355 def tag(ui, repo, name, rev=None, **opts):
1356 """add a tag for the current tip or a given revision"""
1356 """add a tag for the current tip or a given revision"""
1357 if opts['text']:
1357 if opts['text']:
1358 ui.warn("Warning: -t and --text is deprecated,"
1358 ui.warn("Warning: -t and --text is deprecated,"
1359 " please use -m or --message instead.\n")
1359 " please use -m or --message instead.\n")
1360 if name == "tip":
1360 if name == "tip":
1361 ui.warn("abort: 'tip' is a reserved name!\n")
1361 ui.warn("abort: 'tip' is a reserved name!\n")
1362 return -1
1362 return -1
1363 if rev:
1363 if rev:
1364 r = hg.hex(repo.lookup(rev))
1364 r = hg.hex(repo.lookup(rev))
1365 else:
1365 else:
1366 r = hg.hex(repo.changelog.tip())
1366 r = hg.hex(repo.changelog.tip())
1367
1367
1368 if name.find(revrangesep) >= 0:
1368 if name.find(revrangesep) >= 0:
1369 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1369 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1370 return -1
1370 return -1
1371
1371
1372 if opts['local']:
1372 if opts['local']:
1373 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1373 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1374 return
1374 return
1375
1375
1376 (c, a, d, u) = repo.changes()
1376 (c, a, d, u) = repo.changes()
1377 for x in (c, a, d, u):
1377 for x in (c, a, d, u):
1378 if ".hgtags" in x:
1378 if ".hgtags" in x:
1379 ui.warn("abort: working copy of .hgtags is changed!\n")
1379 ui.warn("abort: working copy of .hgtags is changed!\n")
1380 ui.status("(please commit .hgtags manually)\n")
1380 ui.status("(please commit .hgtags manually)\n")
1381 return -1
1381 return -1
1382
1382
1383 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1383 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1384 if repo.dirstate.state(".hgtags") == '?':
1384 if repo.dirstate.state(".hgtags") == '?':
1385 repo.add([".hgtags"])
1385 repo.add([".hgtags"])
1386
1386
1387 message = (opts['message'] or opts['text'] or
1387 message = (opts['message'] or opts['text'] or
1388 "Added tag %s for changeset %s" % (name, r))
1388 "Added tag %s for changeset %s" % (name, r))
1389 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1389 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1390
1390
1391 def tags(ui, repo):
1391 def tags(ui, repo):
1392 """list repository tags"""
1392 """list repository tags"""
1393
1393
1394 l = repo.tagslist()
1394 l = repo.tagslist()
1395 l.reverse()
1395 l.reverse()
1396 for t, n in l:
1396 for t, n in l:
1397 try:
1397 try:
1398 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1398 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1399 except KeyError:
1399 except KeyError:
1400 r = " ?:?"
1400 r = " ?:?"
1401 ui.write("%-30s %s\n" % (t, r))
1401 ui.write("%-30s %s\n" % (t, r))
1402
1402
1403 def tip(ui, repo):
1403 def tip(ui, repo):
1404 """show the tip revision"""
1404 """show the tip revision"""
1405 n = repo.changelog.tip()
1405 n = repo.changelog.tip()
1406 show_changeset(ui, repo, changenode=n)
1406 show_changeset(ui, repo, changenode=n)
1407
1407
1408 def undo(ui, repo):
1408 def undo(ui, repo):
1409 """undo the last commit or pull
1409 """undo the last commit or pull
1410
1410
1411 Roll back the last pull or commit transaction on the
1411 Roll back the last pull or commit transaction on the
1412 repository, restoring the project to its earlier state.
1412 repository, restoring the project to its earlier state.
1413
1413
1414 This command should be used with care. There is only one level of
1414 This command should be used with care. There is only one level of
1415 undo and there is no redo.
1415 undo and there is no redo.
1416
1416
1417 This command is not intended for use on public repositories. Once
1417 This command is not intended for use on public repositories. Once
1418 a change is visible for pull by other users, undoing it locally is
1418 a change is visible for pull by other users, undoing it locally is
1419 ineffective.
1419 ineffective.
1420 """
1420 """
1421 repo.undo()
1421 repo.undo()
1422
1422
1423 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1423 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1424 '''update or merge working directory
1424 '''update or merge working directory
1425
1425
1426 If there are no outstanding changes in the working directory and
1426 If there are no outstanding changes in the working directory and
1427 there is a linear relationship between the current version and the
1427 there is a linear relationship between the current version and the
1428 requested version, the result is the requested version.
1428 requested version, the result is the requested version.
1429
1429
1430 Otherwise the result is a merge between the contents of the
1430 Otherwise the result is a merge between the contents of the
1431 current working directory and the requested version. Files that
1431 current working directory and the requested version. Files that
1432 changed between either parent are marked as changed for the next
1432 changed between either parent are marked as changed for the next
1433 commit and a commit must be performed before any further updates
1433 commit and a commit must be performed before any further updates
1434 are allowed.
1434 are allowed.
1435 '''
1435 '''
1436 if branch:
1436 if branch:
1437 br = repo.branchlookup(branch=branch)
1437 br = repo.branchlookup(branch=branch)
1438 found = []
1438 found = []
1439 for x in br:
1439 for x in br:
1440 if branch in br[x]:
1440 if branch in br[x]:
1441 found.append(x)
1441 found.append(x)
1442 if len(found) > 1:
1442 if len(found) > 1:
1443 ui.warn("Found multiple heads for %s\n" % branch)
1443 ui.warn("Found multiple heads for %s\n" % branch)
1444 for x in found:
1444 for x in found:
1445 show_changeset(ui, repo, changenode=x, brinfo=br)
1445 show_changeset(ui, repo, changenode=x, brinfo=br)
1446 return 1
1446 return 1
1447 if len(found) == 1:
1447 if len(found) == 1:
1448 node = found[0]
1448 node = found[0]
1449 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1449 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1450 else:
1450 else:
1451 ui.warn("branch %s not found\n" % (branch))
1451 ui.warn("branch %s not found\n" % (branch))
1452 return 1
1452 return 1
1453 else:
1453 else:
1454 node = node and repo.lookup(node) or repo.changelog.tip()
1454 node = node and repo.lookup(node) or repo.changelog.tip()
1455 return repo.update(node, allow=merge, force=clean)
1455 return repo.update(node, allow=merge, force=clean)
1456
1456
1457 def verify(ui, repo):
1457 def verify(ui, repo):
1458 """verify the integrity of the repository"""
1458 """verify the integrity of the repository"""
1459 return repo.verify()
1459 return repo.verify()
1460
1460
1461 # Command options and aliases are listed here, alphabetically
1461 # Command options and aliases are listed here, alphabetically
1462
1462
1463 table = {
1463 table = {
1464 "^add":
1464 "^add":
1465 (add,
1465 (add,
1466 [('I', 'include', [], 'include path in search'),
1466 [('I', 'include', [], 'include path in search'),
1467 ('X', 'exclude', [], 'exclude path from search')],
1467 ('X', 'exclude', [], 'exclude path from search')],
1468 "hg add [OPTION]... [FILE]..."),
1468 "hg add [OPTION]... [FILE]..."),
1469 "addremove":
1469 "addremove":
1470 (addremove,
1470 (addremove,
1471 [('I', 'include', [], 'include path in search'),
1471 [('I', 'include', [], 'include path in search'),
1472 ('X', 'exclude', [], 'exclude path from search')],
1472 ('X', 'exclude', [], 'exclude path from search')],
1473 "hg addremove [OPTION]... [FILE]..."),
1473 "hg addremove [OPTION]... [FILE]..."),
1474 "^annotate":
1474 "^annotate":
1475 (annotate,
1475 (annotate,
1476 [('r', 'rev', '', 'revision'),
1476 [('r', 'rev', '', 'revision'),
1477 ('a', 'text', None, 'treat all files as text'),
1477 ('a', 'text', None, 'treat all files as text'),
1478 ('u', 'user', None, 'show user'),
1478 ('u', 'user', None, 'show user'),
1479 ('n', 'number', None, 'show revision number'),
1479 ('n', 'number', None, 'show revision number'),
1480 ('c', 'changeset', None, 'show changeset'),
1480 ('c', 'changeset', None, 'show changeset'),
1481 ('I', 'include', [], 'include path in search'),
1481 ('I', 'include', [], 'include path in search'),
1482 ('X', 'exclude', [], 'exclude path from search')],
1482 ('X', 'exclude', [], 'exclude path from search')],
1483 'hg annotate [OPTION]... FILE...'),
1483 'hg annotate [OPTION]... FILE...'),
1484 "cat":
1484 "cat":
1485 (cat,
1485 (cat,
1486 [('o', 'output', "", 'output to file')],
1486 [('o', 'output', "", 'output to file')],
1487 'hg cat [-o OUTFILE] FILE [REV]'),
1487 'hg cat [-o OUTFILE] FILE [REV]'),
1488 "^clone":
1488 "^clone":
1489 (clone,
1489 (clone,
1490 [('U', 'noupdate', None, 'skip update after cloning'),
1490 [('U', 'noupdate', None, 'skip update after cloning'),
1491 ('e', 'ssh', "", 'ssh command'),
1491 ('e', 'ssh', "", 'ssh command'),
1492 ('', 'remotecmd', "", 'remote hg command')],
1492 ('', 'remotecmd', "", 'remote hg command')],
1493 'hg clone [OPTION]... SOURCE [DEST]'),
1493 'hg clone [OPTION]... SOURCE [DEST]'),
1494 "^commit|ci":
1494 "^commit|ci":
1495 (commit,
1495 (commit,
1496 [('A', 'addremove', None, 'run add/remove during commit'),
1496 [('A', 'addremove', None, 'run add/remove during commit'),
1497 ('I', 'include', [], 'include path in search'),
1497 ('I', 'include', [], 'include path in search'),
1498 ('X', 'exclude', [], 'exclude path from search'),
1498 ('X', 'exclude', [], 'exclude path from search'),
1499 ('m', 'message', "", 'commit message'),
1499 ('m', 'message', "", 'commit message'),
1500 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1500 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1501 ('l', 'logfile', "", 'commit message file'),
1501 ('l', 'logfile', "", 'commit message file'),
1502 ('d', 'date', "", 'date code'),
1502 ('d', 'date', "", 'date code'),
1503 ('u', 'user', "", 'user')],
1503 ('u', 'user', "", 'user')],
1504 'hg commit [OPTION]... [FILE]...'),
1504 'hg commit [OPTION]... [FILE]...'),
1505 "copy": (copy, [], 'hg copy SOURCE DEST'),
1505 "copy": (copy, [], 'hg copy SOURCE DEST'),
1506 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1506 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1507 "debugconfig": (debugconfig, [], 'debugconfig'),
1507 "debugconfig": (debugconfig, [], 'debugconfig'),
1508 "debugstate": (debugstate, [], 'debugstate'),
1508 "debugstate": (debugstate, [], 'debugstate'),
1509 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1509 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1510 "debugindex": (debugindex, [], 'debugindex FILE'),
1510 "debugindex": (debugindex, [], 'debugindex FILE'),
1511 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1511 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1512 "debugwalk":
1512 "debugwalk":
1513 (debugwalk,
1513 (debugwalk,
1514 [('I', 'include', [], 'include path in search'),
1514 [('I', 'include', [], 'include path in search'),
1515 ('X', 'exclude', [], 'exclude path from search')],
1515 ('X', 'exclude', [], 'exclude path from search')],
1516 'debugwalk [OPTION]... [FILE]...'),
1516 'debugwalk [OPTION]... [FILE]...'),
1517 "^diff":
1517 "^diff":
1518 (diff,
1518 (diff,
1519 [('r', 'rev', [], 'revision'),
1519 [('r', 'rev', [], 'revision'),
1520 ('a', 'text', None, 'treat all files as text'),
1520 ('a', 'text', None, 'treat all files as text'),
1521 ('I', 'include', [], 'include path in search'),
1521 ('I', 'include', [], 'include path in search'),
1522 ('X', 'exclude', [], 'exclude path from search')],
1522 ('X', 'exclude', [], 'exclude path from search')],
1523 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1523 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1524 "^export":
1524 "^export":
1525 (export,
1525 (export,
1526 [('o', 'output', "", 'output to file'),
1526 [('o', 'output', "", 'output to file'),
1527 ('a', 'text', None, 'treat all files as text')],
1527 ('a', 'text', None, 'treat all files as text')],
1528 "hg export [-a] [-o OUTFILE] REV..."),
1528 "hg export [-a] [-o OUTFILE] REV..."),
1529 "forget":
1529 "forget":
1530 (forget,
1530 (forget,
1531 [('I', 'include', [], 'include path in search'),
1531 [('I', 'include', [], 'include path in search'),
1532 ('X', 'exclude', [], 'exclude path from search')],
1532 ('X', 'exclude', [], 'exclude path from search')],
1533 "hg forget [OPTION]... FILE..."),
1533 "hg forget [OPTION]... FILE..."),
1534 "grep":
1534 "grep":
1535 (grep,
1535 (grep,
1536 [('0', 'print0', None, 'terminate file names with NUL'),
1536 [('0', 'print0', None, 'terminate file names with NUL'),
1537 ('I', 'include', [], 'include path in search'),
1537 ('I', 'include', [], 'include path in search'),
1538 ('X', 'exclude', [], 'include path in search'),
1538 ('X', 'exclude', [], 'include path in search'),
1539 ('Z', 'null', None, 'terminate file names with NUL'),
1539 ('Z', 'null', None, 'terminate file names with NUL'),
1540 ('a', 'all-revs', '', 'search all revs'),
1540 ('a', 'all-revs', '', 'search all revs'),
1541 ('e', 'regexp', '', 'pattern to search for'),
1541 ('e', 'regexp', '', 'pattern to search for'),
1542 ('f', 'full-path', None, 'print complete paths'),
1542 ('f', 'full-path', None, 'print complete paths'),
1543 ('i', 'ignore-case', None, 'ignore case when matching'),
1543 ('i', 'ignore-case', None, 'ignore case when matching'),
1544 ('l', 'files-with-matches', None, 'print names of files with matches'),
1544 ('l', 'files-with-matches', None, 'print names of files with matches'),
1545 ('n', 'line-number', '', 'print line numbers'),
1545 ('n', 'line-number', '', 'print line numbers'),
1546 ('r', 'rev', [], 'search in revision rev'),
1546 ('r', 'rev', [], 'search in revision rev'),
1547 ('s', 'no-messages', None, 'do not print error messages'),
1547 ('s', 'no-messages', None, 'do not print error messages'),
1548 ('v', 'invert-match', None, 'select non-matching lines')],
1548 ('v', 'invert-match', None, 'select non-matching lines')],
1549 "hg grep [OPTION]... [PATTERN] [FILE]..."),
1549 "hg grep [OPTION]... [PATTERN] [FILE]..."),
1550 "heads":
1550 "heads":
1551 (heads,
1551 (heads,
1552 [('b', 'branches', None, 'find branch info')],
1552 [('b', 'branches', None, 'find branch info')],
1553 'hg heads [-b]'),
1553 'hg heads [-b]'),
1554 "help": (help_, [], 'hg help [COMMAND]'),
1554 "help": (help_, [], 'hg help [COMMAND]'),
1555 "identify|id": (identify, [], 'hg identify'),
1555 "identify|id": (identify, [], 'hg identify'),
1556 "import|patch":
1556 "import|patch":
1557 (import_,
1557 (import_,
1558 [('p', 'strip', 1, 'path strip'),
1558 [('p', 'strip', 1, 'path strip'),
1559 ('f', 'force', None, 'skip check for outstanding changes'),
1559 ('f', 'force', None, 'skip check for outstanding changes'),
1560 ('b', 'base', "", 'base path')],
1560 ('b', 'base', "", 'base path')],
1561 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1561 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1562 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1562 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1563 "^init": (init, [], 'hg init [DEST]'),
1563 "^init": (init, [], 'hg init [DEST]'),
1564 "locate":
1564 "locate":
1565 (locate,
1565 (locate,
1566 [('r', 'rev', '', 'revision'),
1566 [('r', 'rev', '', 'revision'),
1567 ('0', 'print0', None, 'end records with NUL'),
1567 ('0', 'print0', None, 'end records with NUL'),
1568 ('f', 'fullpath', None, 'print complete paths'),
1568 ('f', 'fullpath', None, 'print complete paths'),
1569 ('I', 'include', [], 'include path in search'),
1569 ('I', 'include', [], 'include path in search'),
1570 ('X', 'exclude', [], 'exclude path from search')],
1570 ('X', 'exclude', [], 'exclude path from search')],
1571 'hg locate [OPTION]... [PATTERN]...'),
1571 'hg locate [OPTION]... [PATTERN]...'),
1572 "^log|history":
1572 "^log|history":
1573 (log,
1573 (log,
1574 [('I', 'include', [], 'include path in search'),
1574 [('I', 'include', [], 'include path in search'),
1575 ('X', 'exclude', [], 'exclude path from search'),
1575 ('X', 'exclude', [], 'exclude path from search'),
1576 ('r', 'rev', [], 'revision'),
1576 ('r', 'rev', [], 'revision'),
1577 ('p', 'patch', None, 'show patch')],
1577 ('p', 'patch', None, 'show patch')],
1578 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1578 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1579 "manifest": (manifest, [], 'hg manifest [REV]'),
1579 "manifest": (manifest, [], 'hg manifest [REV]'),
1580 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1580 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1581 "parents": (parents, [], 'hg parents [REV]'),
1581 "parents": (parents, [], 'hg parents [REV]'),
1582 "paths": (paths, [], 'hg paths [NAME]'),
1582 "paths": (paths, [], 'hg paths [NAME]'),
1583 "^pull":
1583 "^pull":
1584 (pull,
1584 (pull,
1585 [('u', 'update', None, 'update working directory'),
1585 [('u', 'update', None, 'update working directory'),
1586 ('e', 'ssh', "", 'ssh command'),
1586 ('e', 'ssh', "", 'ssh command'),
1587 ('', 'remotecmd', "", 'remote hg command')],
1587 ('', 'remotecmd', "", 'remote hg command')],
1588 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1588 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1589 "^push":
1589 "^push":
1590 (push,
1590 (push,
1591 [('f', 'force', None, 'force push'),
1591 [('f', 'force', None, 'force push'),
1592 ('e', 'ssh', "", 'ssh command'),
1592 ('e', 'ssh', "", 'ssh command'),
1593 ('', 'remotecmd', "", 'remote hg command')],
1593 ('', 'remotecmd', "", 'remote hg command')],
1594 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1594 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1595 "rawcommit":
1595 "rawcommit":
1596 (rawcommit,
1596 (rawcommit,
1597 [('p', 'parent', [], 'parent'),
1597 [('p', 'parent', [], 'parent'),
1598 ('d', 'date', "", 'date code'),
1598 ('d', 'date', "", 'date code'),
1599 ('u', 'user', "", 'user'),
1599 ('u', 'user', "", 'user'),
1600 ('F', 'files', "", 'file list'),
1600 ('F', 'files', "", 'file list'),
1601 ('m', 'message', "", 'commit message'),
1601 ('m', 'message', "", 'commit message'),
1602 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1602 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1603 ('l', 'logfile', "", 'commit message file')],
1603 ('l', 'logfile', "", 'commit message file')],
1604 'hg rawcommit [OPTION]... [FILE]...'),
1604 'hg rawcommit [OPTION]... [FILE]...'),
1605 "recover": (recover, [], "hg recover"),
1605 "recover": (recover, [], "hg recover"),
1606 "^remove|rm": (remove, [], "hg remove FILE..."),
1606 "^remove|rm": (remove, [], "hg remove FILE..."),
1607 "^revert":
1607 "^revert":
1608 (revert,
1608 (revert,
1609 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1609 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1610 ("r", "rev", "", "revision")],
1610 ("r", "rev", "", "revision")],
1611 "hg revert [-n] [-r REV] [NAME]..."),
1611 "hg revert [-n] [-r REV] [NAME]..."),
1612 "root": (root, [], "hg root"),
1612 "root": (root, [], "hg root"),
1613 "^serve":
1613 "^serve":
1614 (serve,
1614 (serve,
1615 [('A', 'accesslog', '', 'access log file'),
1615 [('A', 'accesslog', '', 'access log file'),
1616 ('E', 'errorlog', '', 'error log file'),
1616 ('E', 'errorlog', '', 'error log file'),
1617 ('p', 'port', 0, 'listen port'),
1617 ('p', 'port', 0, 'listen port'),
1618 ('a', 'address', '', 'interface address'),
1618 ('a', 'address', '', 'interface address'),
1619 ('n', 'name', "", 'repository name'),
1619 ('n', 'name', "", 'repository name'),
1620 ('', 'stdio', None, 'for remote clients'),
1620 ('', 'stdio', None, 'for remote clients'),
1621 ('t', 'templates', "", 'template directory'),
1621 ('t', 'templates', "", 'template directory'),
1622 ('', 'style', "", 'template style'),
1622 ('', 'style', "", 'template style'),
1623 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1623 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1624 "hg serve [OPTION]..."),
1624 "hg serve [OPTION]..."),
1625 "^status":
1625 "^status":
1626 (status,
1626 (status,
1627 [('m', 'modified', None, 'show only modified files'),
1627 [('m', 'modified', None, 'show only modified files'),
1628 ('a', 'added', None, 'show only added files'),
1628 ('a', 'added', None, 'show only added files'),
1629 ('r', 'removed', None, 'show only removed files'),
1629 ('r', 'removed', None, 'show only removed files'),
1630 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1630 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1631 ('I', 'include', [], 'include path in search'),
1631 ('I', 'include', [], 'include path in search'),
1632 ('X', 'exclude', [], 'exclude path from search')],
1632 ('X', 'exclude', [], 'exclude path from search')],
1633 "hg status [OPTION]... [FILE]..."),
1633 "hg status [OPTION]... [FILE]..."),
1634 "tag":
1634 "tag":
1635 (tag,
1635 (tag,
1636 [('l', 'local', None, 'make the tag local'),
1636 [('l', 'local', None, 'make the tag local'),
1637 ('m', 'message', "", 'commit message'),
1637 ('m', 'message', "", 'commit message'),
1638 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1638 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1639 ('d', 'date', "", 'date code'),
1639 ('d', 'date', "", 'date code'),
1640 ('u', 'user', "", 'user')],
1640 ('u', 'user', "", 'user')],
1641 'hg tag [OPTION]... NAME [REV]'),
1641 'hg tag [OPTION]... NAME [REV]'),
1642 "tags": (tags, [], 'hg tags'),
1642 "tags": (tags, [], 'hg tags'),
1643 "tip": (tip, [], 'hg tip'),
1643 "tip": (tip, [], 'hg tip'),
1644 "undo": (undo, [], 'hg undo'),
1644 "undo": (undo, [], 'hg undo'),
1645 "^update|up|checkout|co":
1645 "^update|up|checkout|co":
1646 (update,
1646 (update,
1647 [('b', 'branch', "", 'checkout the head of a specific branch'),
1647 [('b', 'branch', "", 'checkout the head of a specific branch'),
1648 ('m', 'merge', None, 'allow merging of conflicts'),
1648 ('m', 'merge', None, 'allow merging of conflicts'),
1649 ('C', 'clean', None, 'overwrite locally modified files')],
1649 ('C', 'clean', None, 'overwrite locally modified files')],
1650 'hg update [-b TAG] [-m] [-C] [REV]'),
1650 'hg update [-b TAG] [-m] [-C] [REV]'),
1651 "verify": (verify, [], 'hg verify'),
1651 "verify": (verify, [], 'hg verify'),
1652 "version": (show_version, [], 'hg version'),
1652 "version": (show_version, [], 'hg version'),
1653 }
1653 }
1654
1654
1655 globalopts = [
1655 globalopts = [
1656 ('R', 'repository', "", 'repository root directory'),
1656 ('R', 'repository', "", 'repository root directory'),
1657 ('', 'cwd', '', 'change working directory'),
1657 ('', 'cwd', '', 'change working directory'),
1658 ('y', 'noninteractive', None, 'run non-interactively'),
1658 ('y', 'noninteractive', None, 'run non-interactively'),
1659 ('q', 'quiet', None, 'quiet mode'),
1659 ('q', 'quiet', None, 'quiet mode'),
1660 ('v', 'verbose', None, 'verbose mode'),
1660 ('v', 'verbose', None, 'verbose mode'),
1661 ('', 'debug', None, 'debug mode'),
1661 ('', 'debug', None, 'debug mode'),
1662 ('', 'traceback', None, 'print traceback on exception'),
1662 ('', 'traceback', None, 'print traceback on exception'),
1663 ('', 'time', None, 'time how long the command takes'),
1663 ('', 'time', None, 'time how long the command takes'),
1664 ('', 'profile', None, 'profile'),
1664 ('', 'profile', None, 'profile'),
1665 ('', 'version', None, 'output version information and exit'),
1665 ('', 'version', None, 'output version information and exit'),
1666 ('h', 'help', None, 'display help and exit'),
1666 ('h', 'help', None, 'display help and exit'),
1667 ]
1667 ]
1668
1668
1669 norepo = ("clone init version help debugconfig debugdata"
1669 norepo = ("clone init version help debugconfig debugdata"
1670 " debugindex debugindexdot paths")
1670 " debugindex debugindexdot paths")
1671
1671
1672 def find(cmd):
1672 def find(cmd):
1673 for e in table.keys():
1673 for e in table.keys():
1674 if re.match("(%s)$" % e, cmd):
1674 if re.match("(%s)$" % e, cmd):
1675 return e, table[e]
1675 return e, table[e]
1676
1676
1677 raise UnknownCommand(cmd)
1677 raise UnknownCommand(cmd)
1678
1678
1679 class SignalInterrupt(Exception):
1679 class SignalInterrupt(Exception):
1680 """Exception raised on SIGTERM and SIGHUP."""
1680 """Exception raised on SIGTERM and SIGHUP."""
1681
1681
1682 def catchterm(*args):
1682 def catchterm(*args):
1683 raise SignalInterrupt
1683 raise SignalInterrupt
1684
1684
1685 def run():
1685 def run():
1686 sys.exit(dispatch(sys.argv[1:]))
1686 sys.exit(dispatch(sys.argv[1:]))
1687
1687
1688 class ParseError(Exception):
1688 class ParseError(Exception):
1689 """Exception raised on errors in parsing the command line."""
1689 """Exception raised on errors in parsing the command line."""
1690
1690
1691 def parse(args):
1691 def parse(args):
1692 options = {}
1692 options = {}
1693 cmdoptions = {}
1693 cmdoptions = {}
1694
1694
1695 try:
1695 try:
1696 args = fancyopts.fancyopts(args, globalopts, options)
1696 args = fancyopts.fancyopts(args, globalopts, options)
1697 except fancyopts.getopt.GetoptError, inst:
1697 except fancyopts.getopt.GetoptError, inst:
1698 raise ParseError(None, inst)
1698 raise ParseError(None, inst)
1699
1699
1700 if args:
1700 if args:
1701 cmd, args = args[0], args[1:]
1701 cmd, args = args[0], args[1:]
1702 i = find(cmd)[1]
1702 i = find(cmd)[1]
1703 c = list(i[1])
1703 c = list(i[1])
1704 else:
1704 else:
1705 cmd = None
1705 cmd = None
1706 c = []
1706 c = []
1707
1707
1708 # combine global options into local
1708 # combine global options into local
1709 for o in globalopts:
1709 for o in globalopts:
1710 c.append((o[0], o[1], options[o[1]], o[3]))
1710 c.append((o[0], o[1], options[o[1]], o[3]))
1711
1711
1712 try:
1712 try:
1713 args = fancyopts.fancyopts(args, c, cmdoptions)
1713 args = fancyopts.fancyopts(args, c, cmdoptions)
1714 except fancyopts.getopt.GetoptError, inst:
1714 except fancyopts.getopt.GetoptError, inst:
1715 raise ParseError(cmd, inst)
1715 raise ParseError(cmd, inst)
1716
1716
1717 # separate global options back out
1717 # separate global options back out
1718 for o in globalopts:
1718 for o in globalopts:
1719 n = o[1]
1719 n = o[1]
1720 options[n] = cmdoptions[n]
1720 options[n] = cmdoptions[n]
1721 del cmdoptions[n]
1721 del cmdoptions[n]
1722
1722
1723 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1723 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1724
1724
1725 def dispatch(args):
1725 def dispatch(args):
1726 signal.signal(signal.SIGTERM, catchterm)
1726 signal.signal(signal.SIGTERM, catchterm)
1727 try:
1727 try:
1728 signal.signal(signal.SIGHUP, catchterm)
1728 signal.signal(signal.SIGHUP, catchterm)
1729 except AttributeError:
1729 except AttributeError:
1730 pass
1730 pass
1731
1731
1732 u = ui.ui()
1732 u = ui.ui()
1733 external = []
1733 external = []
1734 for x in u.extensions():
1734 for x in u.extensions():
1735 if x[1]:
1735 if x[1]:
1736 mod = imp.load_source(x[0], x[1])
1736 mod = imp.load_source(x[0], x[1])
1737 else:
1737 else:
1738 def importh(name):
1738 def importh(name):
1739 mod = __import__(name)
1739 mod = __import__(name)
1740 components = name.split('.')
1740 components = name.split('.')
1741 for comp in components[1:]:
1741 for comp in components[1:]:
1742 mod = getattr(mod, comp)
1742 mod = getattr(mod, comp)
1743 return mod
1743 return mod
1744 mod = importh(x[0])
1744 mod = importh(x[0])
1745 external.append(mod)
1745 external.append(mod)
1746 for x in external:
1746 for x in external:
1747 for t in x.cmdtable:
1747 for t in x.cmdtable:
1748 if t in table:
1748 if t in table:
1749 u.warn("module %s override %s\n" % (x.__name__, t))
1749 u.warn("module %s override %s\n" % (x.__name__, t))
1750 table.update(x.cmdtable)
1750 table.update(x.cmdtable)
1751
1751
1752 try:
1752 try:
1753 cmd, func, args, options, cmdoptions = parse(args)
1753 cmd, func, args, options, cmdoptions = parse(args)
1754 except ParseError, inst:
1754 except ParseError, inst:
1755 if inst.args[0]:
1755 if inst.args[0]:
1756 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1756 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1757 help_(u, inst.args[0])
1757 help_(u, inst.args[0])
1758 else:
1758 else:
1759 u.warn("hg: %s\n" % inst.args[1])
1759 u.warn("hg: %s\n" % inst.args[1])
1760 help_(u, 'shortlist')
1760 help_(u, 'shortlist')
1761 sys.exit(-1)
1761 sys.exit(-1)
1762 except UnknownCommand, inst:
1762 except UnknownCommand, inst:
1763 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1763 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1764 help_(u, 'shortlist')
1764 help_(u, 'shortlist')
1765 sys.exit(1)
1765 sys.exit(1)
1766
1766
1767 if options["time"]:
1767 if options["time"]:
1768 def get_times():
1768 def get_times():
1769 t = os.times()
1769 t = os.times()
1770 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1770 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1771 t = (t[0], t[1], t[2], t[3], time.clock())
1771 t = (t[0], t[1], t[2], t[3], time.clock())
1772 return t
1772 return t
1773 s = get_times()
1773 s = get_times()
1774 def print_time():
1774 def print_time():
1775 t = get_times()
1775 t = get_times()
1776 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1776 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1777 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1777 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1778 atexit.register(print_time)
1778 atexit.register(print_time)
1779
1779
1780 u.updateopts(options["verbose"], options["debug"], options["quiet"],
1780 u.updateopts(options["verbose"], options["debug"], options["quiet"],
1781 not options["noninteractive"])
1781 not options["noninteractive"])
1782
1782
1783 try:
1783 try:
1784 try:
1784 try:
1785 if options['help']:
1785 if options['help']:
1786 help_(u, cmd, options['version'])
1786 help_(u, cmd, options['version'])
1787 sys.exit(0)
1787 sys.exit(0)
1788 elif options['version']:
1788 elif options['version']:
1789 show_version(u)
1789 show_version(u)
1790 sys.exit(0)
1790 sys.exit(0)
1791 elif not cmd:
1791 elif not cmd:
1792 help_(u, 'shortlist')
1792 help_(u, 'shortlist')
1793 sys.exit(0)
1793 sys.exit(0)
1794
1794
1795 if options['cwd']:
1795 if options['cwd']:
1796 try:
1796 try:
1797 os.chdir(options['cwd'])
1797 os.chdir(options['cwd'])
1798 except OSError, inst:
1798 except OSError, inst:
1799 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1799 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1800 sys.exit(1)
1800 sys.exit(1)
1801
1801
1802 if cmd not in norepo.split():
1802 if cmd not in norepo.split():
1803 path = options["repository"] or ""
1803 path = options["repository"] or ""
1804 repo = hg.repository(ui=u, path=path)
1804 repo = hg.repository(ui=u, path=path)
1805 for x in external:
1805 for x in external:
1806 x.reposetup(u, repo)
1806 x.reposetup(u, repo)
1807 d = lambda: func(u, repo, *args, **cmdoptions)
1807 d = lambda: func(u, repo, *args, **cmdoptions)
1808 else:
1808 else:
1809 d = lambda: func(u, *args, **cmdoptions)
1809 d = lambda: func(u, *args, **cmdoptions)
1810
1810
1811 if options['profile']:
1811 if options['profile']:
1812 import hotshot, hotshot.stats
1812 import hotshot, hotshot.stats
1813 prof = hotshot.Profile("hg.prof")
1813 prof = hotshot.Profile("hg.prof")
1814 r = prof.runcall(d)
1814 r = prof.runcall(d)
1815 prof.close()
1815 prof.close()
1816 stats = hotshot.stats.load("hg.prof")
1816 stats = hotshot.stats.load("hg.prof")
1817 stats.strip_dirs()
1817 stats.strip_dirs()
1818 stats.sort_stats('time', 'calls')
1818 stats.sort_stats('time', 'calls')
1819 stats.print_stats(40)
1819 stats.print_stats(40)
1820 return r
1820 return r
1821 else:
1821 else:
1822 return d()
1822 return d()
1823 except:
1823 except:
1824 if options['traceback']:
1824 if options['traceback']:
1825 traceback.print_exc()
1825 traceback.print_exc()
1826 raise
1826 raise
1827 except hg.RepoError, inst:
1827 except hg.RepoError, inst:
1828 u.warn("abort: ", inst, "!\n")
1828 u.warn("abort: ", inst, "!\n")
1829 except SignalInterrupt:
1829 except SignalInterrupt:
1830 u.warn("killed!\n")
1830 u.warn("killed!\n")
1831 except KeyboardInterrupt:
1831 except KeyboardInterrupt:
1832 try:
1832 try:
1833 u.warn("interrupted!\n")
1833 u.warn("interrupted!\n")
1834 except IOError, inst:
1834 except IOError, inst:
1835 if inst.errno == errno.EPIPE:
1835 if inst.errno == errno.EPIPE:
1836 if u.debugflag:
1836 if u.debugflag:
1837 u.warn("\nbroken pipe\n")
1837 u.warn("\nbroken pipe\n")
1838 else:
1838 else:
1839 raise
1839 raise
1840 except IOError, inst:
1840 except IOError, inst:
1841 if hasattr(inst, "code"):
1841 if hasattr(inst, "code"):
1842 u.warn("abort: %s\n" % inst)
1842 u.warn("abort: %s\n" % inst)
1843 elif hasattr(inst, "reason"):
1843 elif hasattr(inst, "reason"):
1844 u.warn("abort: error: %s\n" % inst.reason[1])
1844 u.warn("abort: error: %s\n" % inst.reason[1])
1845 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1845 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1846 if u.debugflag:
1846 if u.debugflag:
1847 u.warn("broken pipe\n")
1847 u.warn("broken pipe\n")
1848 else:
1848 else:
1849 raise
1849 raise
1850 except OSError, inst:
1850 except OSError, inst:
1851 if hasattr(inst, "filename"):
1851 if hasattr(inst, "filename"):
1852 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1852 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1853 else:
1853 else:
1854 u.warn("abort: %s\n" % inst.strerror)
1854 u.warn("abort: %s\n" % inst.strerror)
1855 except util.Abort, inst:
1855 except util.Abort, inst:
1856 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1856 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1857 sys.exit(1)
1857 sys.exit(1)
1858 except TypeError, inst:
1858 except TypeError, inst:
1859 # was this an argument error?
1859 # was this an argument error?
1860 tb = traceback.extract_tb(sys.exc_info()[2])
1860 tb = traceback.extract_tb(sys.exc_info()[2])
1861 if len(tb) > 2: # no
1861 if len(tb) > 2: # no
1862 raise
1862 raise
1863 u.debug(inst, "\n")
1863 u.debug(inst, "\n")
1864 u.warn("%s: invalid arguments\n" % cmd)
1864 u.warn("%s: invalid arguments\n" % cmd)
1865 help_(u, cmd)
1865 help_(u, cmd)
1866 except UnknownCommand, inst:
1866 except UnknownCommand, inst:
1867 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1867 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1868 help_(u, 'shortlist')
1868 help_(u, 'shortlist')
1869
1869
1870 sys.exit(-1)
1870 sys.exit(-1)
@@ -1,290 +1,290 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 binary(s):
12 def binary(s):
13 if s and '\0' in s[:4096]:
13 if s and '\0' in s[:4096]:
14 return True
14 return True
15 return False
15 return False
16
16
17 def unique(g):
17 def unique(g):
18 seen = {}
18 seen = {}
19 for f in g:
19 for f in g:
20 if f not in seen:
20 if f not in seen:
21 seen[f] = 1
21 seen[f] = 1
22 yield f
22 yield f
23
23
24 class Abort(Exception):
24 class Abort(Exception):
25 """Raised if a command needs to print an error and exit."""
25 """Raised if a command needs to print an error and exit."""
26
26
27 def always(fn): return True
27 def always(fn): return True
28 def never(fn): return False
28 def never(fn): return False
29
29
30 def globre(pat, head='^', tail='$'):
30 def globre(pat, head='^', tail='$'):
31 "convert a glob pattern into a regexp"
31 "convert a glob pattern into a regexp"
32 i, n = 0, len(pat)
32 i, n = 0, len(pat)
33 res = ''
33 res = ''
34 group = False
34 group = False
35 def peek(): return i < n and pat[i]
35 def peek(): return i < n and pat[i]
36 while i < n:
36 while i < n:
37 c = pat[i]
37 c = pat[i]
38 i = i+1
38 i = i+1
39 if c == '*':
39 if c == '*':
40 if peek() == '*':
40 if peek() == '*':
41 i += 1
41 i += 1
42 res += '.*'
42 res += '.*'
43 else:
43 else:
44 res += '[^/]*'
44 res += '[^/]*'
45 elif c == '?':
45 elif c == '?':
46 res += '.'
46 res += '.'
47 elif c == '[':
47 elif c == '[':
48 j = i
48 j = i
49 if j < n and pat[j] in '!]':
49 if j < n and pat[j] in '!]':
50 j += 1
50 j += 1
51 while j < n and pat[j] != ']':
51 while j < n and pat[j] != ']':
52 j += 1
52 j += 1
53 if j >= n:
53 if j >= n:
54 res += '\\['
54 res += '\\['
55 else:
55 else:
56 stuff = pat[i:j].replace('\\','\\\\')
56 stuff = pat[i:j].replace('\\','\\\\')
57 i = j + 1
57 i = j + 1
58 if stuff[0] == '!':
58 if stuff[0] == '!':
59 stuff = '^' + stuff[1:]
59 stuff = '^' + stuff[1:]
60 elif stuff[0] == '^':
60 elif stuff[0] == '^':
61 stuff = '\\' + stuff
61 stuff = '\\' + stuff
62 res = '%s[%s]' % (res, stuff)
62 res = '%s[%s]' % (res, stuff)
63 elif c == '{':
63 elif c == '{':
64 group = True
64 group = True
65 res += '(?:'
65 res += '(?:'
66 elif c == '}' and group:
66 elif c == '}' and group:
67 res += ')'
67 res += ')'
68 group = False
68 group = False
69 elif c == ',' and group:
69 elif c == ',' and group:
70 res += '|'
70 res += '|'
71 else:
71 else:
72 res += re.escape(c)
72 res += re.escape(c)
73 return head + res + tail
73 return head + res + tail
74
74
75 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
75 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
76
76
77 def pathto(n1, n2):
77 def pathto(n1, n2):
78 '''return the relative path from one place to another.
78 '''return the relative path from one place to another.
79 this returns a path in the form used by the local filesystem, not hg.'''
79 this returns a path in the form used by the local filesystem, not hg.'''
80 if not n1: return localpath(n2)
80 if not n1: return localpath(n2)
81 a, b = n1.split('/'), n2.split('/')
81 a, b = n1.split('/'), n2.split('/')
82 a.reverse(), b.reverse()
82 a.reverse(), b.reverse()
83 while a and b and a[-1] == b[-1]:
83 while a and b and a[-1] == b[-1]:
84 a.pop(), b.pop()
84 a.pop(), b.pop()
85 b.reverse()
85 b.reverse()
86 return os.sep.join((['..'] * len(a)) + b)
86 return os.sep.join((['..'] * len(a)) + b)
87
87
88 def canonpath(repo, cwd, myname):
88 def canonpath(root, cwd, myname):
89 rootsep = repo.root + os.sep
89 rootsep = root + os.sep
90 name = myname
90 name = myname
91 if not name.startswith(os.sep):
91 if not name.startswith(os.sep):
92 name = os.path.join(repo.root, cwd, name)
92 name = os.path.join(root, cwd, name)
93 name = os.path.normpath(name)
93 name = os.path.normpath(name)
94 if name.startswith(rootsep):
94 if name.startswith(rootsep):
95 return pconvert(name[len(rootsep):])
95 return pconvert(name[len(rootsep):])
96 elif name == repo.root:
96 elif name == root:
97 return ''
97 return ''
98 else:
98 else:
99 raise Abort('%s not under repository root' % myname)
99 raise Abort('%s not under root' % myname)
100
100
101 def matcher(repo, cwd, names, inc, exc, head=''):
101 def matcher(canonroot, cwd, names, inc, exc, head=''):
102 def patkind(name):
102 def patkind(name):
103 for prefix in 're:', 'glob:', 'path:', 'relpath:':
103 for prefix in 're:', 'glob:', 'path:', 'relpath:':
104 if name.startswith(prefix): return name.split(':', 1)
104 if name.startswith(prefix): return name.split(':', 1)
105 for c in name:
105 for c in name:
106 if c in _globchars: return 'glob', name
106 if c in _globchars: return 'glob', name
107 return 'relpath', name
107 return 'relpath', name
108
108
109 def regex(kind, name, tail):
109 def regex(kind, name, tail):
110 '''convert a pattern into a regular expression'''
110 '''convert a pattern into a regular expression'''
111 if kind == 're':
111 if kind == 're':
112 return name
112 return name
113 elif kind == 'path':
113 elif kind == 'path':
114 return '^' + re.escape(name) + '(?:/|$)'
114 return '^' + re.escape(name) + '(?:/|$)'
115 elif kind == 'relpath':
115 elif kind == 'relpath':
116 return head + re.escape(name) + tail
116 return head + re.escape(name) + tail
117 return head + globre(name, '', tail)
117 return head + globre(name, '', tail)
118
118
119 def matchfn(pats, tail):
119 def matchfn(pats, tail):
120 """build a matching function from a set of patterns"""
120 """build a matching function from a set of patterns"""
121 if pats:
121 if pats:
122 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
122 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
123 return re.compile(pat).match
123 return re.compile(pat).match
124
124
125 def globprefix(pat):
125 def globprefix(pat):
126 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
126 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
127 root = []
127 root = []
128 for p in pat.split(os.sep):
128 for p in pat.split(os.sep):
129 if patkind(p)[0] == 'glob': break
129 if patkind(p)[0] == 'glob': break
130 root.append(p)
130 root.append(p)
131 return '/'.join(root)
131 return '/'.join(root)
132
132
133 pats = []
133 pats = []
134 files = []
134 files = []
135 roots = []
135 roots = []
136 for kind, name in map(patkind, names):
136 for kind, name in map(patkind, names):
137 if kind in ('glob', 'relpath'):
137 if kind in ('glob', 'relpath'):
138 name = canonpath(repo, cwd, name)
138 name = canonpath(canonroot, cwd, name)
139 if name == '':
139 if name == '':
140 kind, name = 'glob', '**'
140 kind, name = 'glob', '**'
141 if kind in ('glob', 'path', 're'):
141 if kind in ('glob', 'path', 're'):
142 pats.append((kind, name))
142 pats.append((kind, name))
143 if kind == 'glob':
143 if kind == 'glob':
144 root = globprefix(name)
144 root = globprefix(name)
145 if root: roots.append(root)
145 if root: roots.append(root)
146 elif kind == 'relpath':
146 elif kind == 'relpath':
147 files.append((kind, name))
147 files.append((kind, name))
148 roots.append(name)
148 roots.append(name)
149
149
150 patmatch = matchfn(pats, '$') or always
150 patmatch = matchfn(pats, '$') or always
151 filematch = matchfn(files, '(?:/|$)') or always
151 filematch = matchfn(files, '(?:/|$)') or always
152 incmatch = always
152 incmatch = always
153 if inc:
153 if inc:
154 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
154 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
155 excmatch = lambda fn: False
155 excmatch = lambda fn: False
156 if exc:
156 if exc:
157 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
157 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
158
158
159 return (roots,
159 return (roots,
160 lambda fn: (incmatch(fn) and not excmatch(fn) and
160 lambda fn: (incmatch(fn) and not excmatch(fn) and
161 (fn.endswith('/') or
161 (fn.endswith('/') or
162 (not pats and not files) or
162 (not pats and not files) or
163 (pats and patmatch(fn)) or
163 (pats and patmatch(fn)) or
164 (files and filematch(fn)))),
164 (files and filematch(fn)))),
165 (inc or exc or (pats and pats != [('glob', '**')])) and True)
165 (inc or exc or (pats and pats != [('glob', '**')])) and True)
166
166
167 def system(cmd, errprefix=None):
167 def system(cmd, errprefix=None):
168 """execute a shell command that must succeed"""
168 """execute a shell command that must succeed"""
169 rc = os.system(cmd)
169 rc = os.system(cmd)
170 if rc:
170 if rc:
171 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
171 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
172 explain_exit(rc)[0])
172 explain_exit(rc)[0])
173 if errprefix:
173 if errprefix:
174 errmsg = "%s: %s" % (errprefix, errmsg)
174 errmsg = "%s: %s" % (errprefix, errmsg)
175 raise Abort(errmsg)
175 raise Abort(errmsg)
176
176
177 def rename(src, dst):
177 def rename(src, dst):
178 try:
178 try:
179 os.rename(src, dst)
179 os.rename(src, dst)
180 except:
180 except:
181 os.unlink(dst)
181 os.unlink(dst)
182 os.rename(src, dst)
182 os.rename(src, dst)
183
183
184 def copytree(src, dst, copyfile):
184 def copytree(src, dst, copyfile):
185 """Copy a directory tree, files are copied using 'copyfile'."""
185 """Copy a directory tree, files are copied using 'copyfile'."""
186 names = os.listdir(src)
186 names = os.listdir(src)
187 os.mkdir(dst)
187 os.mkdir(dst)
188
188
189 for name in names:
189 for name in names:
190 srcname = os.path.join(src, name)
190 srcname = os.path.join(src, name)
191 dstname = os.path.join(dst, name)
191 dstname = os.path.join(dst, name)
192 if os.path.isdir(srcname):
192 if os.path.isdir(srcname):
193 copytree(srcname, dstname, copyfile)
193 copytree(srcname, dstname, copyfile)
194 elif os.path.isfile(srcname):
194 elif os.path.isfile(srcname):
195 copyfile(srcname, dstname)
195 copyfile(srcname, dstname)
196 else:
196 else:
197 pass
197 pass
198
198
199 def _makelock_file(info, pathname):
199 def _makelock_file(info, pathname):
200 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
200 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
201 os.write(ld, info)
201 os.write(ld, info)
202 os.close(ld)
202 os.close(ld)
203
203
204 def _readlock_file(pathname):
204 def _readlock_file(pathname):
205 return file(pathname).read()
205 return file(pathname).read()
206
206
207 # Platfor specific varients
207 # Platfor specific varients
208 if os.name == 'nt':
208 if os.name == 'nt':
209 nulldev = 'NUL:'
209 nulldev = 'NUL:'
210
210
211 def is_exec(f, last):
211 def is_exec(f, last):
212 return last
212 return last
213
213
214 def set_exec(f, mode):
214 def set_exec(f, mode):
215 pass
215 pass
216
216
217 def pconvert(path):
217 def pconvert(path):
218 return path.replace("\\", "/")
218 return path.replace("\\", "/")
219
219
220 def localpath(path):
220 def localpath(path):
221 return path.replace('/', '\\')
221 return path.replace('/', '\\')
222
222
223 def normpath(path):
223 def normpath(path):
224 return pconvert(os.path.normpath(path))
224 return pconvert(os.path.normpath(path))
225
225
226 makelock = _makelock_file
226 makelock = _makelock_file
227 readlock = _readlock_file
227 readlock = _readlock_file
228
228
229 def explain_exit(code):
229 def explain_exit(code):
230 return "exited with status %d" % code, code
230 return "exited with status %d" % code, code
231
231
232 else:
232 else:
233 nulldev = '/dev/null'
233 nulldev = '/dev/null'
234
234
235 def is_exec(f, last):
235 def is_exec(f, last):
236 return (os.stat(f).st_mode & 0100 != 0)
236 return (os.stat(f).st_mode & 0100 != 0)
237
237
238 def set_exec(f, mode):
238 def set_exec(f, mode):
239 s = os.stat(f).st_mode
239 s = os.stat(f).st_mode
240 if (s & 0100 != 0) == mode:
240 if (s & 0100 != 0) == mode:
241 return
241 return
242 if mode:
242 if mode:
243 # Turn on +x for every +r bit when making a file executable
243 # Turn on +x for every +r bit when making a file executable
244 # and obey umask.
244 # and obey umask.
245 umask = os.umask(0)
245 umask = os.umask(0)
246 os.umask(umask)
246 os.umask(umask)
247 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
247 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
248 else:
248 else:
249 os.chmod(f, s & 0666)
249 os.chmod(f, s & 0666)
250
250
251 def pconvert(path):
251 def pconvert(path):
252 return path
252 return path
253
253
254 def localpath(path):
254 def localpath(path):
255 return path
255 return path
256
256
257 normpath = os.path.normpath
257 normpath = os.path.normpath
258
258
259 def makelock(info, pathname):
259 def makelock(info, pathname):
260 try:
260 try:
261 os.symlink(info, pathname)
261 os.symlink(info, pathname)
262 except OSError, why:
262 except OSError, why:
263 if why.errno == errno.EEXIST:
263 if why.errno == errno.EEXIST:
264 raise
264 raise
265 else:
265 else:
266 _makelock_file(info, pathname)
266 _makelock_file(info, pathname)
267
267
268 def readlock(pathname):
268 def readlock(pathname):
269 try:
269 try:
270 return os.readlink(pathname)
270 return os.readlink(pathname)
271 except OSError, why:
271 except OSError, why:
272 if why.errno == errno.EINVAL:
272 if why.errno == errno.EINVAL:
273 return _readlock_file(pathname)
273 return _readlock_file(pathname)
274 else:
274 else:
275 raise
275 raise
276
276
277 def explain_exit(code):
277 def explain_exit(code):
278 """return a 2-tuple (desc, code) describing a process's status"""
278 """return a 2-tuple (desc, code) describing a process's status"""
279 if os.name == 'nt': # os.WIFxx is not supported on windows
279 if os.name == 'nt': # os.WIFxx is not supported on windows
280 return "aborted with error." , -1
280 return "aborted with error." , -1
281 if os.WIFEXITED(code):
281 if os.WIFEXITED(code):
282 val = os.WEXITSTATUS(code)
282 val = os.WEXITSTATUS(code)
283 return "exited with status %d" % val, val
283 return "exited with status %d" % val, val
284 elif os.WIFSIGNALED(code):
284 elif os.WIFSIGNALED(code):
285 val = os.WTERMSIG(code)
285 val = os.WTERMSIG(code)
286 return "killed by signal %d" % val, val
286 return "killed by signal %d" % val, val
287 elif os.WIFSTOPPED(code):
287 elif os.WIFSTOPPED(code):
288 val = os.WSTOPSIG(code)
288 val = os.WSTOPSIG(code)
289 return "stopped by signal %d" % val, val
289 return "stopped by signal %d" % val, val
290 raise ValueError("invalid exit code")
290 raise ValueError("invalid exit code")
General Comments 0
You need to be logged in to leave comments. Login now