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