##// END OF EJS Templates
Add web:style option
mpm@selenic.com -
r986:2810c625 default
parent child Browse files
Show More
@@ -1,159 +1,161 b''
1 HGRC(5)
1 HGRC(5)
2 =======
2 =======
3 Bryan O'Sullivan <bos@serpentine.com>
3 Bryan O'Sullivan <bos@serpentine.com>
4
4
5 NAME
5 NAME
6 ----
6 ----
7 hgrc - configuration files for Mercurial
7 hgrc - configuration files for Mercurial
8
8
9 SYNOPSIS
9 SYNOPSIS
10 --------
10 --------
11
11
12 The Mercurial system uses a set of configuration files to control
12 The Mercurial system uses a set of configuration files to control
13 aspects of its behaviour.
13 aspects of its behaviour.
14
14
15 FILES
15 FILES
16 -----
16 -----
17
17
18 Mercurial reads configuration data from three files:
18 Mercurial reads configuration data from three files:
19
19
20 /etc/mercurial/hgrc::
20 /etc/mercurial/hgrc::
21 Options in this global configuration file apply to all Mercurial
21 Options in this global configuration file apply to all Mercurial
22 commands executed by any user in any directory.
22 commands executed by any user in any directory.
23
23
24 $HOME/.hgrc::
24 $HOME/.hgrc::
25 Per-user configuration options that apply to all Mercurial commands,
25 Per-user configuration options that apply to all Mercurial commands,
26 no matter from which directory they are run. Values in this file
26 no matter from which directory they are run. Values in this file
27 override global settings.
27 override global settings.
28
28
29 <repo>/.hg/hgrc::
29 <repo>/.hg/hgrc::
30 Per-repository configuration options that only apply in a
30 Per-repository configuration options that only apply in a
31 particular repository. This file is not version-controlled, and
31 particular repository. This file is not version-controlled, and
32 will not get transferred during a "clone" operation. Values in
32 will not get transferred during a "clone" operation. Values in
33 this file override global and per-user settings.
33 this file override global and per-user settings.
34
34
35 SYNTAX
35 SYNTAX
36 ------
36 ------
37
37
38 A configuration file consists of sections, led by a "[section]" header
38 A configuration file consists of sections, led by a "[section]" header
39 and followed by "name: value" entries; "name=value" is also accepted.
39 and followed by "name: value" entries; "name=value" is also accepted.
40
40
41 [spam]
41 [spam]
42 eggs=ham
42 eggs=ham
43 green=
43 green=
44 eggs
44 eggs
45
45
46 Each line contains one entry. If the lines that follow are indented,
46 Each line contains one entry. If the lines that follow are indented,
47 they are treated as continuations of that entry.
47 they are treated as continuations of that entry.
48
48
49 Leading whitespace is removed from values. Empty lines are skipped.
49 Leading whitespace is removed from values. Empty lines are skipped.
50
50
51 The optional values can contain format strings which refer to other
51 The optional values can contain format strings which refer to other
52 values in the same section, or values in a special DEFAULT section.
52 values in the same section, or values in a special DEFAULT section.
53
53
54 Lines beginning with "#" or ";" are ignored and may be used to provide
54 Lines beginning with "#" or ";" are ignored and may be used to provide
55 comments.
55 comments.
56
56
57 SECTIONS
57 SECTIONS
58 --------
58 --------
59
59
60 This section describes the different sections that may appear in a
60 This section describes the different sections that may appear in a
61 Mercurial "hgrc" file, the purpose of each section, its possible
61 Mercurial "hgrc" file, the purpose of each section, its possible
62 keys, and their possible values.
62 keys, and their possible values.
63
63
64 hooks::
64 hooks::
65 Commands that get automatically executed by various actions such as
65 Commands that get automatically executed by various actions such as
66 starting or finishing a commit.
66 starting or finishing a commit.
67 precommit;;
67 precommit;;
68 Run before starting a commit. Exit status 0 allows the commit to
68 Run before starting a commit. Exit status 0 allows the commit to
69 proceed. Non-zero status will cause the commit to fail.
69 proceed. Non-zero status will cause the commit to fail.
70 commit;;
70 commit;;
71 Run after a changeset has been created. Passed the ID of the newly
71 Run after a changeset has been created. Passed the ID of the newly
72 created changeset.
72 created changeset.
73 changegroup;;
73 changegroup;;
74 Run after a changegroup has been added via push or pull.
74 Run after a changegroup has been added via push or pull.
75
75
76 http_proxy::
76 http_proxy::
77 Used to access web-based Mercurial repositories through a HTTP
77 Used to access web-based Mercurial repositories through a HTTP
78 proxy.
78 proxy.
79 host;;
79 host;;
80 Host name and (optional) port of the proxy server, for example
80 Host name and (optional) port of the proxy server, for example
81 "myproxy:8000".
81 "myproxy:8000".
82 user;;
82 user;;
83 Optional. User name to authenticate with at the proxy server.
83 Optional. User name to authenticate with at the proxy server.
84 passwd;;
84 passwd;;
85 Optional. Password to authenticate with at the proxy server.
85 Optional. Password to authenticate with at the proxy server.
86 no;;
86 no;;
87 Optional. Comma-separated list of host names that should bypass
87 Optional. Comma-separated list of host names that should bypass
88 the proxy.
88 the proxy.
89
89
90 paths::
90 paths::
91 Assigns symbolic names to repositories. The left side is the
91 Assigns symbolic names to repositories. The left side is the
92 symbolic name, and the right gives the directory or URL that is the
92 symbolic name, and the right gives the directory or URL that is the
93 location of the repository.
93 location of the repository.
94
94
95 ui::
95 ui::
96 User interface controls.
96 User interface controls.
97 debug;;
97 debug;;
98 Print debugging information. True or False. Default is False.
98 Print debugging information. True or False. Default is False.
99 editor;;
99 editor;;
100 The editor to use during a commit. Default is $EDITOR or "vi".
100 The editor to use during a commit. Default is $EDITOR or "vi".
101 interactive;;
101 interactive;;
102 Allow to prompt the user. True or False. Default is True.
102 Allow to prompt the user. True or False. Default is True.
103 merge;;
103 merge;;
104 The conflict resolution program to use during a manual merge.
104 The conflict resolution program to use during a manual merge.
105 Default is "hgmerge".
105 Default is "hgmerge".
106 quiet;;
106 quiet;;
107 Reduce the amount of output printed. True or False. Default is False.
107 Reduce the amount of output printed. True or False. Default is False.
108 username;;
108 username;;
109 The committer of a changeset created when running "commit".
109 The committer of a changeset created when running "commit".
110 Typically a person's name and email address, e.g. "Fred Widget
110 Typically a person's name and email address, e.g. "Fred Widget
111 <fred@example.com>". Default is $EMAIL or username@hostname.
111 <fred@example.com>". Default is $EMAIL or username@hostname.
112 verbose;;
112 verbose;;
113 Increase the amount of output printed. True or False. Default is False.
113 Increase the amount of output printed. True or False. Default is False.
114 ssh;;
114 ssh;;
115 command to use for SSH connections. Default is 'ssh'.
115 command to use for SSH connections. Default is 'ssh'.
116 remotecmd;;
116 remotecmd;;
117 remote command to use for clone/push/pull operations. Default is 'hg'.
117 remote command to use for clone/push/pull operations. Default is 'hg'.
118
118
119
119
120 web::
120 web::
121 Web interface configuration.
121 Web interface configuration.
122 name;;
122 name;;
123 Repository name to use in the web interface. Default is current
123 Repository name to use in the web interface. Default is current
124 working directory.
124 working directory.
125 address;;
125 address;;
126 Interface address to bind to. Default is all.
126 Interface address to bind to. Default is all.
127 port;;
127 port;;
128 Port to listen on. Default is 8000.
128 Port to listen on. Default is 8000.
129 ipv6;;
129 ipv6;;
130 Whether to use IPv6. Default is false.
130 Whether to use IPv6. Default is false.
131 accesslog;;
131 accesslog;;
132 Where to output the access log. Default is stdout.
132 Where to output the access log. Default is stdout.
133 errorlog;;
133 errorlog;;
134 Where to output the error log. Default is stderr.
134 Where to output the error log. Default is stderr.
135 templates;;
135 templates;;
136 Where to find the HTML templates. Default is install path.
136 Where to find the HTML templates. Default is install path.
137 style;;
138 Which template map style to use.
137 maxchanges;;
139 maxchanges;;
138 Maximum number of changes to list on the changelog. Default is 10.
140 Maximum number of changes to list on the changelog. Default is 10.
139 maxfiles;;
141 maxfiles;;
140 Maximum number of files to list per changeset. Default is 10.
142 Maximum number of files to list per changeset. Default is 10.
141 allowpull;;
143 allowpull;;
142 Whether to allow pulling from the repository. Default is true.
144 Whether to allow pulling from the repository. Default is true.
143
145
144 AUTHOR
146 AUTHOR
145 ------
147 ------
146 Bryan O'Sullivan <bos@serpentine.com>.
148 Bryan O'Sullivan <bos@serpentine.com>.
147
149
148 Mercurial was written by Matt Mackall <mpm@selenic.com>.
150 Mercurial was written by Matt Mackall <mpm@selenic.com>.
149
151
150 SEE ALSO
152 SEE ALSO
151 --------
153 --------
152 hg(1)
154 hg(1)
153
155
154 COPYING
156 COPYING
155 -------
157 -------
156 This manual page is copyright 2005 Bryan O'Sullivan.
158 This manual page is copyright 2005 Bryan O'Sullivan.
157 Mercurial is copyright 2005 Matt Mackall.
159 Mercurial is copyright 2005 Matt Mackall.
158 Free use of this software is granted under the terms of the GNU General
160 Free use of this software is granted under the terms of the GNU General
159 Public License (GPL).
161 Public License (GPL).
@@ -1,895 +1,898 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, difflib, socket, sys, zlib
9 import os, cgi, time, re, difflib, socket, sys, zlib
10 from mercurial.hg import *
10 from mercurial.hg import *
11 from mercurial.ui import *
11 from mercurial.ui import *
12
12
13 def templatepath():
13 def templatepath():
14 for f in "templates", "../templates":
14 for f in "templates", "../templates":
15 p = os.path.join(os.path.dirname(__file__), f)
15 p = os.path.join(os.path.dirname(__file__), f)
16 if os.path.isdir(p): return p
16 if os.path.isdir(p): return p
17
17
18 def age(t):
18 def age(t):
19 def plural(t, c):
19 def plural(t, c):
20 if c == 1: return t
20 if c == 1: return t
21 return t + "s"
21 return t + "s"
22 def fmt(t, c):
22 def fmt(t, c):
23 return "%d %s" % (c, plural(t, c))
23 return "%d %s" % (c, plural(t, c))
24
24
25 now = time.time()
25 now = time.time()
26 delta = max(1, int(now - t))
26 delta = max(1, int(now - t))
27
27
28 scales = [["second", 1],
28 scales = [["second", 1],
29 ["minute", 60],
29 ["minute", 60],
30 ["hour", 3600],
30 ["hour", 3600],
31 ["day", 3600 * 24],
31 ["day", 3600 * 24],
32 ["week", 3600 * 24 * 7],
32 ["week", 3600 * 24 * 7],
33 ["month", 3600 * 24 * 30],
33 ["month", 3600 * 24 * 30],
34 ["year", 3600 * 24 * 365]]
34 ["year", 3600 * 24 * 365]]
35
35
36 scales.reverse()
36 scales.reverse()
37
37
38 for t, s in scales:
38 for t, s in scales:
39 n = delta / s
39 n = delta / s
40 if n >= 2 or s == 1: return fmt(t, n)
40 if n >= 2 or s == 1: return fmt(t, n)
41
41
42 def nl2br(text):
42 def nl2br(text):
43 return text.replace('\n', '<br/>\n')
43 return text.replace('\n', '<br/>\n')
44
44
45 def obfuscate(text):
45 def obfuscate(text):
46 return ''.join([ '&#%d;' % ord(c) for c in text ])
46 return ''.join([ '&#%d;' % ord(c) for c in text ])
47
47
48 def up(p):
48 def up(p):
49 if p[0] != "/": p = "/" + p
49 if p[0] != "/": p = "/" + p
50 if p[-1] == "/": p = p[:-1]
50 if p[-1] == "/": p = p[:-1]
51 up = os.path.dirname(p)
51 up = os.path.dirname(p)
52 if up == "/":
52 if up == "/":
53 return "/"
53 return "/"
54 return up + "/"
54 return up + "/"
55
55
56 def httphdr(type):
56 def httphdr(type):
57 sys.stdout.write('Content-type: %s\n\n' % type)
57 sys.stdout.write('Content-type: %s\n\n' % type)
58
58
59 def write(*things):
59 def write(*things):
60 for thing in things:
60 for thing in things:
61 if hasattr(thing, "__iter__"):
61 if hasattr(thing, "__iter__"):
62 for part in thing:
62 for part in thing:
63 write(part)
63 write(part)
64 else:
64 else:
65 sys.stdout.write(str(thing))
65 sys.stdout.write(str(thing))
66
66
67 class templater:
67 class templater:
68 def __init__(self, mapfile, filters = {}, defaults = {}):
68 def __init__(self, mapfile, filters = {}, defaults = {}):
69 self.cache = {}
69 self.cache = {}
70 self.map = {}
70 self.map = {}
71 self.base = os.path.dirname(mapfile)
71 self.base = os.path.dirname(mapfile)
72 self.filters = filters
72 self.filters = filters
73 self.defaults = defaults
73 self.defaults = defaults
74
74
75 for l in file(mapfile):
75 for l in file(mapfile):
76 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
76 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
77 if m:
77 if m:
78 self.cache[m.group(1)] = m.group(2)
78 self.cache[m.group(1)] = m.group(2)
79 else:
79 else:
80 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
80 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
81 if m:
81 if m:
82 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
82 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
83 else:
83 else:
84 raise "unknown map entry '%s'" % l
84 raise "unknown map entry '%s'" % l
85
85
86 def __call__(self, t, **map):
86 def __call__(self, t, **map):
87 m = self.defaults.copy()
87 m = self.defaults.copy()
88 m.update(map)
88 m.update(map)
89 try:
89 try:
90 tmpl = self.cache[t]
90 tmpl = self.cache[t]
91 except KeyError:
91 except KeyError:
92 tmpl = self.cache[t] = file(self.map[t]).read()
92 tmpl = self.cache[t] = file(self.map[t]).read()
93 return self.template(tmpl, self.filters, **m)
93 return self.template(tmpl, self.filters, **m)
94
94
95 def template(self, tmpl, filters = {}, **map):
95 def template(self, tmpl, filters = {}, **map):
96 while tmpl:
96 while tmpl:
97 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
97 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
98 if m:
98 if m:
99 yield tmpl[:m.start(0)]
99 yield tmpl[:m.start(0)]
100 v = map.get(m.group(1), "")
100 v = map.get(m.group(1), "")
101 v = callable(v) and v(**map) or v
101 v = callable(v) and v(**map) or v
102
102
103 format = m.group(2)
103 format = m.group(2)
104 fl = m.group(4)
104 fl = m.group(4)
105
105
106 if format:
106 if format:
107 q = v.__iter__
107 q = v.__iter__
108 for i in q():
108 for i in q():
109 lm = map.copy()
109 lm = map.copy()
110 lm.update(i)
110 lm.update(i)
111 yield self(format[1:], **lm)
111 yield self(format[1:], **lm)
112
112
113 v = ""
113 v = ""
114
114
115 elif fl:
115 elif fl:
116 for f in fl.split("|")[1:]:
116 for f in fl.split("|")[1:]:
117 v = filters[f](v)
117 v = filters[f](v)
118
118
119 yield v
119 yield v
120 tmpl = tmpl[m.end(0):]
120 tmpl = tmpl[m.end(0):]
121 else:
121 else:
122 yield tmpl
122 yield tmpl
123 return
123 return
124
124
125 def rfc822date(x):
125 def rfc822date(x):
126 return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(x))
126 return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(x))
127
127
128 common_filters = {
128 common_filters = {
129 "escape": cgi.escape,
129 "escape": cgi.escape,
130 "age": age,
130 "age": age,
131 "date": (lambda x: time.asctime(time.gmtime(x))),
131 "date": (lambda x: time.asctime(time.gmtime(x))),
132 "addbreaks": nl2br,
132 "addbreaks": nl2br,
133 "obfuscate": obfuscate,
133 "obfuscate": obfuscate,
134 "short": (lambda x: x[:12]),
134 "short": (lambda x: x[:12]),
135 "firstline": (lambda x: x.splitlines(1)[0]),
135 "firstline": (lambda x: x.splitlines(1)[0]),
136 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
136 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
137 "rfc822date": rfc822date,
137 "rfc822date": rfc822date,
138 }
138 }
139
139
140 class hgweb:
140 class hgweb:
141
141
142 def __init__(self, path, name=None, templates=""):
142 def __init__(self, path, name=None, templates=""):
143 self.templates = templates
143 self.templates = templates
144 self.reponame = name
144 self.reponame = name
145 self.path = path
145 self.path = path
146 self.mtime = -1
146 self.mtime = -1
147 self.viewonly = 0
147 self.viewonly = 0
148
148
149 def refresh(self):
149 def refresh(self):
150 s = os.stat(os.path.join(self.path, ".hg", "00changelog.i"))
150 s = os.stat(os.path.join(self.path, ".hg", "00changelog.i"))
151 if s.st_mtime != self.mtime:
151 if s.st_mtime != self.mtime:
152 self.mtime = s.st_mtime
152 self.mtime = s.st_mtime
153 self.repo = repository(ui(), self.path)
153 self.repo = repository(ui(), self.path)
154 self.maxchanges = self.repo.ui.config("web", "maxchanges", 10)
154 self.maxchanges = self.repo.ui.config("web", "maxchanges", 10)
155 self.maxfiles = self.repo.ui.config("web", "maxchanges", 10)
155 self.maxfiles = self.repo.ui.config("web", "maxchanges", 10)
156 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
156 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
157
157
158 def date(self, cs):
158 def date(self, cs):
159 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
159 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
160
160
161 def listfiles(self, files, mf):
161 def listfiles(self, files, mf):
162 for f in files[:self.maxfiles]:
162 for f in files[:self.maxfiles]:
163 yield self.t("filenodelink", node = hex(mf[f]), file = f)
163 yield self.t("filenodelink", node = hex(mf[f]), file = f)
164 if len(files) > self.maxfiles:
164 if len(files) > self.maxfiles:
165 yield self.t("fileellipses")
165 yield self.t("fileellipses")
166
166
167 def listfilediffs(self, files, changeset):
167 def listfilediffs(self, files, changeset):
168 for f in files[:self.maxfiles]:
168 for f in files[:self.maxfiles]:
169 yield self.t("filedifflink", node = hex(changeset), file = f)
169 yield self.t("filedifflink", node = hex(changeset), file = f)
170 if len(files) > self.maxfiles:
170 if len(files) > self.maxfiles:
171 yield self.t("fileellipses")
171 yield self.t("fileellipses")
172
172
173 def parents(self, t1, nodes=[], rev=None,**args):
173 def parents(self, t1, nodes=[], rev=None,**args):
174 if not rev: rev = lambda x: ""
174 if not rev: rev = lambda x: ""
175 for node in nodes:
175 for node in nodes:
176 if node != nullid:
176 if node != nullid:
177 yield self.t(t1, node = hex(node), rev = rev(node), **args)
177 yield self.t(t1, node = hex(node), rev = rev(node), **args)
178
178
179 def showtag(self, t1, node=nullid, **args):
179 def showtag(self, t1, node=nullid, **args):
180 for t in self.repo.nodetags(node):
180 for t in self.repo.nodetags(node):
181 yield self.t(t1, tag = t, **args)
181 yield self.t(t1, tag = t, **args)
182
182
183 def diff(self, node1, node2, files):
183 def diff(self, node1, node2, files):
184 def filterfiles(list, files):
184 def filterfiles(list, files):
185 l = [ x for x in list if x in files ]
185 l = [ x for x in list if x in files ]
186
186
187 for f in files:
187 for f in files:
188 if f[-1] != os.sep: f += os.sep
188 if f[-1] != os.sep: f += os.sep
189 l += [ x for x in list if x.startswith(f) ]
189 l += [ x for x in list if x.startswith(f) ]
190 return l
190 return l
191
191
192 parity = [0]
192 parity = [0]
193 def diffblock(diff, f, fn):
193 def diffblock(diff, f, fn):
194 yield self.t("diffblock",
194 yield self.t("diffblock",
195 lines = prettyprintlines(diff),
195 lines = prettyprintlines(diff),
196 parity = parity[0],
196 parity = parity[0],
197 file = f,
197 file = f,
198 filenode = hex(fn or nullid))
198 filenode = hex(fn or nullid))
199 parity[0] = 1 - parity[0]
199 parity[0] = 1 - parity[0]
200
200
201 def prettyprintlines(diff):
201 def prettyprintlines(diff):
202 for l in diff.splitlines(1):
202 for l in diff.splitlines(1):
203 if l.startswith('+'):
203 if l.startswith('+'):
204 yield self.t("difflineplus", line = l)
204 yield self.t("difflineplus", line = l)
205 elif l.startswith('-'):
205 elif l.startswith('-'):
206 yield self.t("difflineminus", line = l)
206 yield self.t("difflineminus", line = l)
207 elif l.startswith('@'):
207 elif l.startswith('@'):
208 yield self.t("difflineat", line = l)
208 yield self.t("difflineat", line = l)
209 else:
209 else:
210 yield self.t("diffline", line = l)
210 yield self.t("diffline", line = l)
211
211
212 r = self.repo
212 r = self.repo
213 cl = r.changelog
213 cl = r.changelog
214 mf = r.manifest
214 mf = r.manifest
215 change1 = cl.read(node1)
215 change1 = cl.read(node1)
216 change2 = cl.read(node2)
216 change2 = cl.read(node2)
217 mmap1 = mf.read(change1[0])
217 mmap1 = mf.read(change1[0])
218 mmap2 = mf.read(change2[0])
218 mmap2 = mf.read(change2[0])
219 date1 = self.date(change1)
219 date1 = self.date(change1)
220 date2 = self.date(change2)
220 date2 = self.date(change2)
221
221
222 c, a, d, u = r.changes(node1, node2)
222 c, a, d, u = r.changes(node1, node2)
223 if files:
223 if files:
224 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
224 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
225
225
226 for f in c:
226 for f in c:
227 to = r.file(f).read(mmap1[f])
227 to = r.file(f).read(mmap1[f])
228 tn = r.file(f).read(mmap2[f])
228 tn = r.file(f).read(mmap2[f])
229 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
229 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
230 for f in a:
230 for f in a:
231 to = None
231 to = None
232 tn = r.file(f).read(mmap2[f])
232 tn = r.file(f).read(mmap2[f])
233 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
233 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
234 for f in d:
234 for f in d:
235 to = r.file(f).read(mmap1[f])
235 to = r.file(f).read(mmap1[f])
236 tn = None
236 tn = None
237 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
237 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
238
238
239 def changelog(self, pos):
239 def changelog(self, pos):
240 def changenav(**map):
240 def changenav(**map):
241 def seq(factor = 1):
241 def seq(factor = 1):
242 yield 1 * factor
242 yield 1 * factor
243 yield 3 * factor
243 yield 3 * factor
244 #yield 5 * factor
244 #yield 5 * factor
245 for f in seq(factor * 10):
245 for f in seq(factor * 10):
246 yield f
246 yield f
247
247
248 l = []
248 l = []
249 for f in seq():
249 for f in seq():
250 if f < self.maxchanges / 2: continue
250 if f < self.maxchanges / 2: continue
251 if f > count: break
251 if f > count: break
252 r = "%d" % f
252 r = "%d" % f
253 if pos + f < count: l.append(("+" + r, pos + f))
253 if pos + f < count: l.append(("+" + r, pos + f))
254 if pos - f >= 0: l.insert(0, ("-" + r, pos - f))
254 if pos - f >= 0: l.insert(0, ("-" + r, pos - f))
255
255
256 yield {"rev": 0, "label": "(0)"}
256 yield {"rev": 0, "label": "(0)"}
257
257
258 for label, rev in l:
258 for label, rev in l:
259 yield {"label": label, "rev": rev}
259 yield {"label": label, "rev": rev}
260
260
261 yield {"label": "tip", "rev": ""}
261 yield {"label": "tip", "rev": ""}
262
262
263 def changelist(**map):
263 def changelist(**map):
264 parity = (start - end) & 1
264 parity = (start - end) & 1
265 cl = self.repo.changelog
265 cl = self.repo.changelog
266 l = [] # build a list in forward order for efficiency
266 l = [] # build a list in forward order for efficiency
267 for i in range(start, end):
267 for i in range(start, end):
268 n = cl.node(i)
268 n = cl.node(i)
269 changes = cl.read(n)
269 changes = cl.read(n)
270 hn = hex(n)
270 hn = hex(n)
271 t = float(changes[2].split(' ')[0])
271 t = float(changes[2].split(' ')[0])
272
272
273 l.insert(0, {
273 l.insert(0, {
274 "parity": parity,
274 "parity": parity,
275 "author": changes[1],
275 "author": changes[1],
276 "parent": self.parents("changelogparent",
276 "parent": self.parents("changelogparent",
277 cl.parents(n), cl.rev),
277 cl.parents(n), cl.rev),
278 "changelogtag": self.showtag("changelogtag",n),
278 "changelogtag": self.showtag("changelogtag",n),
279 "manifest": hex(changes[0]),
279 "manifest": hex(changes[0]),
280 "desc": changes[4],
280 "desc": changes[4],
281 "date": t,
281 "date": t,
282 "files": self.listfilediffs(changes[3], n),
282 "files": self.listfilediffs(changes[3], n),
283 "rev": i,
283 "rev": i,
284 "node": hn})
284 "node": hn})
285 parity = 1 - parity
285 parity = 1 - parity
286
286
287 for e in l: yield e
287 for e in l: yield e
288
288
289 cl = self.repo.changelog
289 cl = self.repo.changelog
290 mf = cl.read(cl.tip())[0]
290 mf = cl.read(cl.tip())[0]
291 count = cl.count()
291 count = cl.count()
292 start = max(0, pos - self.maxchanges + 1)
292 start = max(0, pos - self.maxchanges + 1)
293 end = min(count, start + self.maxchanges)
293 end = min(count, start + self.maxchanges)
294 pos = end - 1
294 pos = end - 1
295
295
296 yield self.t('changelog',
296 yield self.t('changelog',
297 changenav = changenav,
297 changenav = changenav,
298 manifest = hex(mf),
298 manifest = hex(mf),
299 rev = pos, changesets = count, entries = changelist)
299 rev = pos, changesets = count, entries = changelist)
300
300
301 def search(self, query):
301 def search(self, query):
302
302
303 def changelist(**map):
303 def changelist(**map):
304 cl = self.repo.changelog
304 cl = self.repo.changelog
305 count = 0
305 count = 0
306 qw = query.lower().split()
306 qw = query.lower().split()
307
307
308 def revgen():
308 def revgen():
309 for i in range(cl.count() - 1, 0, -100):
309 for i in range(cl.count() - 1, 0, -100):
310 l = []
310 l = []
311 for j in range(max(0, i - 100), i):
311 for j in range(max(0, i - 100), i):
312 n = cl.node(j)
312 n = cl.node(j)
313 changes = cl.read(n)
313 changes = cl.read(n)
314 l.insert(0, (n, j, changes))
314 l.insert(0, (n, j, changes))
315 for e in l:
315 for e in l:
316 yield e
316 yield e
317
317
318 for n, i, changes in revgen():
318 for n, i, changes in revgen():
319 miss = 0
319 miss = 0
320 for q in qw:
320 for q in qw:
321 if not (q in changes[1].lower() or
321 if not (q in changes[1].lower() or
322 q in changes[4].lower() or
322 q in changes[4].lower() or
323 q in " ".join(changes[3][:20]).lower()):
323 q in " ".join(changes[3][:20]).lower()):
324 miss = 1
324 miss = 1
325 break
325 break
326 if miss: continue
326 if miss: continue
327
327
328 count += 1
328 count += 1
329 hn = hex(n)
329 hn = hex(n)
330 t = float(changes[2].split(' ')[0])
330 t = float(changes[2].split(' ')[0])
331
331
332 yield self.t(
332 yield self.t(
333 'searchentry',
333 'searchentry',
334 parity = count & 1,
334 parity = count & 1,
335 author = changes[1],
335 author = changes[1],
336 parent = self.parents("changelogparent",
336 parent = self.parents("changelogparent",
337 cl.parents(n), cl.rev),
337 cl.parents(n), cl.rev),
338 changelogtag = self.showtag("changelogtag",n),
338 changelogtag = self.showtag("changelogtag",n),
339 manifest = hex(changes[0]),
339 manifest = hex(changes[0]),
340 desc = changes[4],
340 desc = changes[4],
341 date = t,
341 date = t,
342 files = self.listfilediffs(changes[3], n),
342 files = self.listfilediffs(changes[3], n),
343 rev = i,
343 rev = i,
344 node = hn)
344 node = hn)
345
345
346 if count >= self.maxchanges: break
346 if count >= self.maxchanges: break
347
347
348 cl = self.repo.changelog
348 cl = self.repo.changelog
349 mf = cl.read(cl.tip())[0]
349 mf = cl.read(cl.tip())[0]
350
350
351 yield self.t('search',
351 yield self.t('search',
352 query = query,
352 query = query,
353 manifest = hex(mf),
353 manifest = hex(mf),
354 entries = changelist)
354 entries = changelist)
355
355
356 def changeset(self, nodeid):
356 def changeset(self, nodeid):
357 n = bin(nodeid)
357 n = bin(nodeid)
358 cl = self.repo.changelog
358 cl = self.repo.changelog
359 changes = cl.read(n)
359 changes = cl.read(n)
360 p1 = cl.parents(n)[0]
360 p1 = cl.parents(n)[0]
361 t = float(changes[2].split(' ')[0])
361 t = float(changes[2].split(' ')[0])
362
362
363 files = []
363 files = []
364 mf = self.repo.manifest.read(changes[0])
364 mf = self.repo.manifest.read(changes[0])
365 for f in changes[3]:
365 for f in changes[3]:
366 files.append(self.t("filenodelink",
366 files.append(self.t("filenodelink",
367 filenode = hex(mf.get(f, nullid)), file = f))
367 filenode = hex(mf.get(f, nullid)), file = f))
368
368
369 def diff(**map):
369 def diff(**map):
370 yield self.diff(p1, n, None)
370 yield self.diff(p1, n, None)
371
371
372 yield self.t('changeset',
372 yield self.t('changeset',
373 diff = diff,
373 diff = diff,
374 rev = cl.rev(n),
374 rev = cl.rev(n),
375 node = nodeid,
375 node = nodeid,
376 parent = self.parents("changesetparent",
376 parent = self.parents("changesetparent",
377 cl.parents(n), cl.rev),
377 cl.parents(n), cl.rev),
378 changesettag = self.showtag("changesettag",n),
378 changesettag = self.showtag("changesettag",n),
379 manifest = hex(changes[0]),
379 manifest = hex(changes[0]),
380 author = changes[1],
380 author = changes[1],
381 desc = changes[4],
381 desc = changes[4],
382 date = t,
382 date = t,
383 files = files)
383 files = files)
384
384
385 def filelog(self, f, filenode):
385 def filelog(self, f, filenode):
386 cl = self.repo.changelog
386 cl = self.repo.changelog
387 fl = self.repo.file(f)
387 fl = self.repo.file(f)
388 count = fl.count()
388 count = fl.count()
389
389
390 def entries(**map):
390 def entries(**map):
391 l = []
391 l = []
392 parity = (count - 1) & 1
392 parity = (count - 1) & 1
393
393
394 for i in range(count):
394 for i in range(count):
395
395
396 n = fl.node(i)
396 n = fl.node(i)
397 lr = fl.linkrev(n)
397 lr = fl.linkrev(n)
398 cn = cl.node(lr)
398 cn = cl.node(lr)
399 cs = cl.read(cl.node(lr))
399 cs = cl.read(cl.node(lr))
400 t = float(cs[2].split(' ')[0])
400 t = float(cs[2].split(' ')[0])
401
401
402 l.insert(0, {"parity": parity,
402 l.insert(0, {"parity": parity,
403 "filenode": hex(n),
403 "filenode": hex(n),
404 "filerev": i,
404 "filerev": i,
405 "file": f,
405 "file": f,
406 "node": hex(cn),
406 "node": hex(cn),
407 "author": cs[1],
407 "author": cs[1],
408 "date": t,
408 "date": t,
409 "parent": self.parents("filelogparent",
409 "parent": self.parents("filelogparent",
410 fl.parents(n), fl.rev, file=f),
410 fl.parents(n), fl.rev, file=f),
411 "desc": cs[4]})
411 "desc": cs[4]})
412 parity = 1 - parity
412 parity = 1 - parity
413
413
414 for e in l: yield e
414 for e in l: yield e
415
415
416 yield self.t("filelog",
416 yield self.t("filelog",
417 file = f,
417 file = f,
418 filenode = filenode,
418 filenode = filenode,
419 entries = entries)
419 entries = entries)
420
420
421 def filerevision(self, f, node):
421 def filerevision(self, f, node):
422 fl = self.repo.file(f)
422 fl = self.repo.file(f)
423 n = bin(node)
423 n = bin(node)
424 text = fl.read(n)
424 text = fl.read(n)
425 changerev = fl.linkrev(n)
425 changerev = fl.linkrev(n)
426 cl = self.repo.changelog
426 cl = self.repo.changelog
427 cn = cl.node(changerev)
427 cn = cl.node(changerev)
428 cs = cl.read(cn)
428 cs = cl.read(cn)
429 t = float(cs[2].split(' ')[0])
429 t = float(cs[2].split(' ')[0])
430 mfn = cs[0]
430 mfn = cs[0]
431
431
432 def lines():
432 def lines():
433 for l, t in enumerate(text.splitlines(1)):
433 for l, t in enumerate(text.splitlines(1)):
434 yield {"line": t,
434 yield {"line": t,
435 "linenumber": "% 6d" % (l + 1),
435 "linenumber": "% 6d" % (l + 1),
436 "parity": l & 1}
436 "parity": l & 1}
437
437
438 yield self.t("filerevision", file = f,
438 yield self.t("filerevision", file = f,
439 filenode = node,
439 filenode = node,
440 path = up(f),
440 path = up(f),
441 text = lines(),
441 text = lines(),
442 rev = changerev,
442 rev = changerev,
443 node = hex(cn),
443 node = hex(cn),
444 manifest = hex(mfn),
444 manifest = hex(mfn),
445 author = cs[1],
445 author = cs[1],
446 date = t,
446 date = t,
447 parent = self.parents("filerevparent",
447 parent = self.parents("filerevparent",
448 fl.parents(n), fl.rev, file=f),
448 fl.parents(n), fl.rev, file=f),
449 permissions = self.repo.manifest.readflags(mfn)[f])
449 permissions = self.repo.manifest.readflags(mfn)[f])
450
450
451 def fileannotate(self, f, node):
451 def fileannotate(self, f, node):
452 bcache = {}
452 bcache = {}
453 ncache = {}
453 ncache = {}
454 fl = self.repo.file(f)
454 fl = self.repo.file(f)
455 n = bin(node)
455 n = bin(node)
456 changerev = fl.linkrev(n)
456 changerev = fl.linkrev(n)
457
457
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 annotate(**map):
464 def annotate(**map):
465 parity = 1
465 parity = 1
466 last = None
466 last = None
467 for r, l in fl.annotate(n):
467 for r, l in fl.annotate(n):
468 try:
468 try:
469 cnode = ncache[r]
469 cnode = ncache[r]
470 except KeyError:
470 except KeyError:
471 cnode = ncache[r] = self.repo.changelog.node(r)
471 cnode = ncache[r] = self.repo.changelog.node(r)
472
472
473 try:
473 try:
474 name = bcache[r]
474 name = bcache[r]
475 except KeyError:
475 except KeyError:
476 cl = self.repo.changelog.read(cnode)
476 cl = self.repo.changelog.read(cnode)
477 name = cl[1]
477 name = cl[1]
478 f = name.find('@')
478 f = name.find('@')
479 if f >= 0:
479 if f >= 0:
480 name = name[:f]
480 name = name[:f]
481 f = name.find('<')
481 f = name.find('<')
482 if f >= 0:
482 if f >= 0:
483 name = name[f+1:]
483 name = name[f+1:]
484 bcache[r] = name
484 bcache[r] = name
485
485
486 if last != cnode:
486 if last != cnode:
487 parity = 1 - parity
487 parity = 1 - parity
488 last = cnode
488 last = cnode
489
489
490 yield {"parity": parity,
490 yield {"parity": parity,
491 "node": hex(cnode),
491 "node": hex(cnode),
492 "rev": r,
492 "rev": r,
493 "author": name,
493 "author": name,
494 "file": f,
494 "file": f,
495 "line": l}
495 "line": l}
496
496
497 yield self.t("fileannotate",
497 yield self.t("fileannotate",
498 file = f,
498 file = f,
499 filenode = node,
499 filenode = node,
500 annotate = annotate,
500 annotate = annotate,
501 path = up(f),
501 path = up(f),
502 rev = changerev,
502 rev = changerev,
503 node = hex(cn),
503 node = hex(cn),
504 manifest = hex(mfn),
504 manifest = hex(mfn),
505 author = cs[1],
505 author = cs[1],
506 date = t,
506 date = t,
507 parent = self.parents("fileannotateparent",
507 parent = self.parents("fileannotateparent",
508 fl.parents(n), fl.rev, file=f),
508 fl.parents(n), fl.rev, file=f),
509 permissions = self.repo.manifest.readflags(mfn)[f])
509 permissions = self.repo.manifest.readflags(mfn)[f])
510
510
511 def manifest(self, mnode, path):
511 def manifest(self, mnode, path):
512 mf = self.repo.manifest.read(bin(mnode))
512 mf = self.repo.manifest.read(bin(mnode))
513 rev = self.repo.manifest.rev(bin(mnode))
513 rev = self.repo.manifest.rev(bin(mnode))
514 node = self.repo.changelog.node(rev)
514 node = self.repo.changelog.node(rev)
515 mff=self.repo.manifest.readflags(bin(mnode))
515 mff=self.repo.manifest.readflags(bin(mnode))
516
516
517 files = {}
517 files = {}
518
518
519 p = path[1:]
519 p = path[1:]
520 l = len(p)
520 l = len(p)
521
521
522 for f,n in mf.items():
522 for f,n in mf.items():
523 if f[:l] != p:
523 if f[:l] != p:
524 continue
524 continue
525 remain = f[l:]
525 remain = f[l:]
526 if "/" in remain:
526 if "/" in remain:
527 short = remain[:remain.find("/") + 1] # bleah
527 short = remain[:remain.find("/") + 1] # bleah
528 files[short] = (f, None)
528 files[short] = (f, None)
529 else:
529 else:
530 short = os.path.basename(remain)
530 short = os.path.basename(remain)
531 files[short] = (f, n)
531 files[short] = (f, n)
532
532
533 def filelist(**map):
533 def filelist(**map):
534 parity = 0
534 parity = 0
535 fl = files.keys()
535 fl = files.keys()
536 fl.sort()
536 fl.sort()
537 for f in fl:
537 for f in fl:
538 full, fnode = files[f]
538 full, fnode = files[f]
539 if not fnode:
539 if not fnode:
540 continue
540 continue
541
541
542 yield {"file": full,
542 yield {"file": full,
543 "manifest": mnode,
543 "manifest": mnode,
544 "filenode": hex(fnode),
544 "filenode": hex(fnode),
545 "parity": parity,
545 "parity": parity,
546 "basename": f,
546 "basename": f,
547 "permissions": mff[full]}
547 "permissions": mff[full]}
548 parity = 1 - parity
548 parity = 1 - parity
549
549
550 def dirlist(**map):
550 def dirlist(**map):
551 parity = 0
551 parity = 0
552 fl = files.keys()
552 fl = files.keys()
553 fl.sort()
553 fl.sort()
554 for f in fl:
554 for f in fl:
555 full, fnode = files[f]
555 full, fnode = files[f]
556 if fnode:
556 if fnode:
557 continue
557 continue
558
558
559 yield {"parity": parity,
559 yield {"parity": parity,
560 "path": os.path.join(path, f),
560 "path": os.path.join(path, f),
561 "manifest": mnode,
561 "manifest": mnode,
562 "basename": f[:-1]}
562 "basename": f[:-1]}
563 parity = 1 - parity
563 parity = 1 - parity
564
564
565 yield self.t("manifest",
565 yield self.t("manifest",
566 manifest = mnode,
566 manifest = mnode,
567 rev = rev,
567 rev = rev,
568 node = hex(node),
568 node = hex(node),
569 path = path,
569 path = path,
570 up = up(path),
570 up = up(path),
571 fentries = filelist,
571 fentries = filelist,
572 dentries = dirlist)
572 dentries = dirlist)
573
573
574 def tags(self):
574 def tags(self):
575 cl = self.repo.changelog
575 cl = self.repo.changelog
576 mf = cl.read(cl.tip())[0]
576 mf = cl.read(cl.tip())[0]
577
577
578 i = self.repo.tagslist()
578 i = self.repo.tagslist()
579 i.reverse()
579 i.reverse()
580
580
581 def entries(**map):
581 def entries(**map):
582 parity = 0
582 parity = 0
583 for k,n in i:
583 for k,n in i:
584 yield {"parity": parity,
584 yield {"parity": parity,
585 "tag": k,
585 "tag": k,
586 "node": hex(n)}
586 "node": hex(n)}
587 parity = 1 - parity
587 parity = 1 - parity
588
588
589 yield self.t("tags",
589 yield self.t("tags",
590 manifest = hex(mf),
590 manifest = hex(mf),
591 entries = entries)
591 entries = entries)
592
592
593 def filediff(self, file, changeset):
593 def filediff(self, file, changeset):
594 n = bin(changeset)
594 n = bin(changeset)
595 cl = self.repo.changelog
595 cl = self.repo.changelog
596 p1 = cl.parents(n)[0]
596 p1 = cl.parents(n)[0]
597 cs = cl.read(n)
597 cs = cl.read(n)
598 mf = self.repo.manifest.read(cs[0])
598 mf = self.repo.manifest.read(cs[0])
599
599
600 def diff(**map):
600 def diff(**map):
601 yield self.diff(p1, n, file)
601 yield self.diff(p1, n, file)
602
602
603 yield self.t("filediff",
603 yield self.t("filediff",
604 file = file,
604 file = file,
605 filenode = hex(mf.get(file, nullid)),
605 filenode = hex(mf.get(file, nullid)),
606 node = changeset,
606 node = changeset,
607 rev = self.repo.changelog.rev(n),
607 rev = self.repo.changelog.rev(n),
608 parent = self.parents("filediffparent",
608 parent = self.parents("filediffparent",
609 cl.parents(n), cl.rev),
609 cl.parents(n), cl.rev),
610 diff = diff)
610 diff = diff)
611
611
612 # add tags to things
612 # add tags to things
613 # tags -> list of changesets corresponding to tags
613 # tags -> list of changesets corresponding to tags
614 # find tag, changeset, file
614 # find tag, changeset, file
615
615
616 def run(self):
616 def run(self):
617 def header(**map):
617 def header(**map):
618 yield self.t("header", **map)
618 yield self.t("header", **map)
619
619
620 def footer(**map):
620 def footer(**map):
621 yield self.t("footer", **map)
621 yield self.t("footer", **map)
622
622
623 self.refresh()
623 self.refresh()
624 args = cgi.parse()
624 args = cgi.parse()
625
625
626 t = self.templates or self.repo.ui.config("web", "templates",
626 t = self.templates or self.repo.ui.config("web", "templates",
627 templatepath())
627 templatepath())
628 m = os.path.join(t, "map")
628 m = os.path.join(t, "map")
629 style = self.repo.ui.config("web", "style", "")
629 if args.has_key('style'):
630 if args.has_key('style'):
630 b = os.path.basename("map-" + args['style'][0])
631 style = args['style'][0]
632 if style:
633 b = os.path.basename("map-" + style)
631 p = os.path.join(t, b)
634 p = os.path.join(t, b)
632 if os.path.isfile(p): m = p
635 if os.path.isfile(p): m = p
633
636
634 port = os.environ["SERVER_PORT"]
637 port = os.environ["SERVER_PORT"]
635 port = port != "80" and (":" + port) or ""
638 port = port != "80" and (":" + port) or ""
636 uri = os.environ["REQUEST_URI"]
639 uri = os.environ["REQUEST_URI"]
637 if "?" in uri: uri = uri.split("?")[0]
640 if "?" in uri: uri = uri.split("?")[0]
638 url = "http://%s%s%s" % (os.environ["SERVER_NAME"], port, uri)
641 url = "http://%s%s%s" % (os.environ["SERVER_NAME"], port, uri)
639
642
640 name = self.reponame or self.repo.ui.config("web", "name", os.getcwd())
643 name = self.reponame or self.repo.ui.config("web", "name", os.getcwd())
641
644
642 self.t = templater(m, common_filters,
645 self.t = templater(m, common_filters,
643 {"url":url,
646 {"url":url,
644 "repo":name,
647 "repo":name,
645 "header":header,
648 "header":header,
646 "footer":footer,
649 "footer":footer,
647 })
650 })
648
651
649 if not args.has_key('cmd'):
652 if not args.has_key('cmd'):
650 args['cmd'] = [self.t.cache['default'],]
653 args['cmd'] = [self.t.cache['default'],]
651
654
652 if args['cmd'][0] == 'changelog':
655 if args['cmd'][0] == 'changelog':
653 c = self.repo.changelog.count() - 1
656 c = self.repo.changelog.count() - 1
654 hi = c
657 hi = c
655 if args.has_key('rev'):
658 if args.has_key('rev'):
656 hi = args['rev'][0]
659 hi = args['rev'][0]
657 try:
660 try:
658 hi = self.repo.changelog.rev(self.repo.lookup(hi))
661 hi = self.repo.changelog.rev(self.repo.lookup(hi))
659 except RepoError:
662 except RepoError:
660 write(self.search(hi))
663 write(self.search(hi))
661 return
664 return
662
665
663 write(self.changelog(hi))
666 write(self.changelog(hi))
664
667
665 elif args['cmd'][0] == 'changeset':
668 elif args['cmd'][0] == 'changeset':
666 write(self.changeset(args['node'][0]))
669 write(self.changeset(args['node'][0]))
667
670
668 elif args['cmd'][0] == 'manifest':
671 elif args['cmd'][0] == 'manifest':
669 write(self.manifest(args['manifest'][0], args['path'][0]))
672 write(self.manifest(args['manifest'][0], args['path'][0]))
670
673
671 elif args['cmd'][0] == 'tags':
674 elif args['cmd'][0] == 'tags':
672 write(self.tags())
675 write(self.tags())
673
676
674 elif args['cmd'][0] == 'filediff':
677 elif args['cmd'][0] == 'filediff':
675 write(self.filediff(args['file'][0], args['node'][0]))
678 write(self.filediff(args['file'][0], args['node'][0]))
676
679
677 elif args['cmd'][0] == 'file':
680 elif args['cmd'][0] == 'file':
678 write(self.filerevision(args['file'][0], args['filenode'][0]))
681 write(self.filerevision(args['file'][0], args['filenode'][0]))
679
682
680 elif args['cmd'][0] == 'annotate':
683 elif args['cmd'][0] == 'annotate':
681 write(self.fileannotate(args['file'][0], args['filenode'][0]))
684 write(self.fileannotate(args['file'][0], args['filenode'][0]))
682
685
683 elif args['cmd'][0] == 'filelog':
686 elif args['cmd'][0] == 'filelog':
684 write(self.filelog(args['file'][0], args['filenode'][0]))
687 write(self.filelog(args['file'][0], args['filenode'][0]))
685
688
686 elif args['cmd'][0] == 'heads':
689 elif args['cmd'][0] == 'heads':
687 httphdr("application/mercurial-0.1")
690 httphdr("application/mercurial-0.1")
688 h = self.repo.heads()
691 h = self.repo.heads()
689 sys.stdout.write(" ".join(map(hex, h)) + "\n")
692 sys.stdout.write(" ".join(map(hex, h)) + "\n")
690
693
691 elif args['cmd'][0] == 'branches':
694 elif args['cmd'][0] == 'branches':
692 httphdr("application/mercurial-0.1")
695 httphdr("application/mercurial-0.1")
693 nodes = []
696 nodes = []
694 if args.has_key('nodes'):
697 if args.has_key('nodes'):
695 nodes = map(bin, args['nodes'][0].split(" "))
698 nodes = map(bin, args['nodes'][0].split(" "))
696 for b in self.repo.branches(nodes):
699 for b in self.repo.branches(nodes):
697 sys.stdout.write(" ".join(map(hex, b)) + "\n")
700 sys.stdout.write(" ".join(map(hex, b)) + "\n")
698
701
699 elif args['cmd'][0] == 'between':
702 elif args['cmd'][0] == 'between':
700 httphdr("application/mercurial-0.1")
703 httphdr("application/mercurial-0.1")
701 nodes = []
704 nodes = []
702 if args.has_key('pairs'):
705 if args.has_key('pairs'):
703 pairs = [ map(bin, p.split("-"))
706 pairs = [ map(bin, p.split("-"))
704 for p in args['pairs'][0].split(" ") ]
707 for p in args['pairs'][0].split(" ") ]
705 for b in self.repo.between(pairs):
708 for b in self.repo.between(pairs):
706 sys.stdout.write(" ".join(map(hex, b)) + "\n")
709 sys.stdout.write(" ".join(map(hex, b)) + "\n")
707
710
708 elif args['cmd'][0] == 'changegroup':
711 elif args['cmd'][0] == 'changegroup':
709 httphdr("application/mercurial-0.1")
712 httphdr("application/mercurial-0.1")
710 nodes = []
713 nodes = []
711 if not self.allowpull:
714 if not self.allowpull:
712 return
715 return
713
716
714 if args.has_key('roots'):
717 if args.has_key('roots'):
715 nodes = map(bin, args['roots'][0].split(" "))
718 nodes = map(bin, args['roots'][0].split(" "))
716
719
717 z = zlib.compressobj()
720 z = zlib.compressobj()
718 f = self.repo.changegroup(nodes)
721 f = self.repo.changegroup(nodes)
719 while 1:
722 while 1:
720 chunk = f.read(4096)
723 chunk = f.read(4096)
721 if not chunk: break
724 if not chunk: break
722 sys.stdout.write(z.compress(chunk))
725 sys.stdout.write(z.compress(chunk))
723
726
724 sys.stdout.write(z.flush())
727 sys.stdout.write(z.flush())
725
728
726 else:
729 else:
727 write(self.t("error"))
730 write(self.t("error"))
728
731
729 def create_server(path, name, templates, address, port, use_ipv6 = False,
732 def create_server(path, name, templates, address, port, use_ipv6 = False,
730 accesslog = sys.stdout, errorlog = sys.stderr):
733 accesslog = sys.stdout, errorlog = sys.stderr):
731
734
732 def openlog(opt, default):
735 def openlog(opt, default):
733 if opt and opt != '-':
736 if opt and opt != '-':
734 return open(opt, 'w')
737 return open(opt, 'w')
735 return default
738 return default
736
739
737 u = ui()
740 u = ui()
738 repo = repository(u, path)
741 repo = repository(u, path)
739 if not address:
742 if not address:
740 address = u.config("web", "address", "")
743 address = u.config("web", "address", "")
741 if not port:
744 if not port:
742 port = int(u.config("web", "port", 8000))
745 port = int(u.config("web", "port", 8000))
743 if not use_ipv6:
746 if not use_ipv6:
744 use_ipv6 = u.configbool("web", "ipv6")
747 use_ipv6 = u.configbool("web", "ipv6")
745
748
746 accesslog = openlog(accesslog or u.config("web", "accesslog", "-"),
749 accesslog = openlog(accesslog or u.config("web", "accesslog", "-"),
747 sys.stdout)
750 sys.stdout)
748 errorlog = openlog(errorlog or u.config("web", "errorlog", "-"),
751 errorlog = openlog(errorlog or u.config("web", "errorlog", "-"),
749 sys.stderr)
752 sys.stderr)
750
753
751 import BaseHTTPServer
754 import BaseHTTPServer
752
755
753 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
756 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
754 address_family = getattr(socket, 'AF_INET6', None)
757 address_family = getattr(socket, 'AF_INET6', None)
755
758
756 def __init__(self, *args, **kwargs):
759 def __init__(self, *args, **kwargs):
757 if self.address_family is None:
760 if self.address_family is None:
758 raise RepoError('IPv6 not available on this system')
761 raise RepoError('IPv6 not available on this system')
759 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
762 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
760
763
761 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
764 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
762 def log_error(self, format, *args):
765 def log_error(self, format, *args):
763 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
766 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
764 self.log_date_time_string(),
767 self.log_date_time_string(),
765 format % args))
768 format % args))
766
769
767 def log_message(self, format, *args):
770 def log_message(self, format, *args):
768 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
771 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
769 self.log_date_time_string(),
772 self.log_date_time_string(),
770 format % args))
773 format % args))
771
774
772 def do_POST(self):
775 def do_POST(self):
773 try:
776 try:
774 self.do_hgweb()
777 self.do_hgweb()
775 except socket.error, inst:
778 except socket.error, inst:
776 if inst.args[0] != 32: raise
779 if inst.args[0] != 32: raise
777
780
778 def do_GET(self):
781 def do_GET(self):
779 self.do_POST()
782 self.do_POST()
780
783
781 def do_hgweb(self):
784 def do_hgweb(self):
782 query = ""
785 query = ""
783 p = self.path.find("?")
786 p = self.path.find("?")
784 if p:
787 if p:
785 query = self.path[p + 1:]
788 query = self.path[p + 1:]
786 query = query.replace('+', ' ')
789 query = query.replace('+', ' ')
787
790
788 env = {}
791 env = {}
789 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
792 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
790 env['REQUEST_METHOD'] = self.command
793 env['REQUEST_METHOD'] = self.command
791 env['SERVER_NAME'] = self.server.server_name
794 env['SERVER_NAME'] = self.server.server_name
792 env['SERVER_PORT'] = str(self.server.server_port)
795 env['SERVER_PORT'] = str(self.server.server_port)
793 env['REQUEST_URI'] = "/"
796 env['REQUEST_URI'] = "/"
794 if query:
797 if query:
795 env['QUERY_STRING'] = query
798 env['QUERY_STRING'] = query
796 host = self.address_string()
799 host = self.address_string()
797 if host != self.client_address[0]:
800 if host != self.client_address[0]:
798 env['REMOTE_HOST'] = host
801 env['REMOTE_HOST'] = host
799 env['REMOTE_ADDR'] = self.client_address[0]
802 env['REMOTE_ADDR'] = self.client_address[0]
800
803
801 if self.headers.typeheader is None:
804 if self.headers.typeheader is None:
802 env['CONTENT_TYPE'] = self.headers.type
805 env['CONTENT_TYPE'] = self.headers.type
803 else:
806 else:
804 env['CONTENT_TYPE'] = self.headers.typeheader
807 env['CONTENT_TYPE'] = self.headers.typeheader
805 length = self.headers.getheader('content-length')
808 length = self.headers.getheader('content-length')
806 if length:
809 if length:
807 env['CONTENT_LENGTH'] = length
810 env['CONTENT_LENGTH'] = length
808 accept = []
811 accept = []
809 for line in self.headers.getallmatchingheaders('accept'):
812 for line in self.headers.getallmatchingheaders('accept'):
810 if line[:1] in "\t\n\r ":
813 if line[:1] in "\t\n\r ":
811 accept.append(line.strip())
814 accept.append(line.strip())
812 else:
815 else:
813 accept = accept + line[7:].split(',')
816 accept = accept + line[7:].split(',')
814 env['HTTP_ACCEPT'] = ','.join(accept)
817 env['HTTP_ACCEPT'] = ','.join(accept)
815
818
816 os.environ.update(env)
819 os.environ.update(env)
817
820
818 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
821 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
819 try:
822 try:
820 sys.stdin = self.rfile
823 sys.stdin = self.rfile
821 sys.stdout = self.wfile
824 sys.stdout = self.wfile
822 sys.argv = ["hgweb.py"]
825 sys.argv = ["hgweb.py"]
823 if '=' not in query:
826 if '=' not in query:
824 sys.argv.append(query)
827 sys.argv.append(query)
825 self.send_response(200, "Script output follows")
828 self.send_response(200, "Script output follows")
826 hg.run()
829 hg.run()
827 finally:
830 finally:
828 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
831 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
829
832
830 hg = hgweb(path, name, templates)
833 hg = hgweb(path, name, templates)
831 if use_ipv6:
834 if use_ipv6:
832 return IPv6HTTPServer((address, port), hgwebhandler)
835 return IPv6HTTPServer((address, port), hgwebhandler)
833 else:
836 else:
834 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
837 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
835
838
836 def server(path, name, templates, address, port, use_ipv6 = False,
839 def server(path, name, templates, address, port, use_ipv6 = False,
837 accesslog = sys.stdout, errorlog = sys.stderr):
840 accesslog = sys.stdout, errorlog = sys.stderr):
838 httpd = create_server(path, name, templates, address, port, use_ipv6,
841 httpd = create_server(path, name, templates, address, port, use_ipv6,
839 accesslog, errorlog)
842 accesslog, errorlog)
840 httpd.serve_forever()
843 httpd.serve_forever()
841
844
842 # This is a stopgap
845 # This is a stopgap
843 class hgwebdir:
846 class hgwebdir:
844 def __init__(self, config):
847 def __init__(self, config):
845 self.cp = ConfigParser.SafeConfigParser()
848 self.cp = ConfigParser.SafeConfigParser()
846 self.cp.read(config)
849 self.cp.read(config)
847
850
848 def run(self):
851 def run(self):
849 try:
852 try:
850 virtual = os.environ["PATH_INFO"]
853 virtual = os.environ["PATH_INFO"]
851 except:
854 except:
852 virtual = ""
855 virtual = ""
853
856
854 if virtual:
857 if virtual:
855 real = self.cp.get("paths", virtual[1:])
858 real = self.cp.get("paths", virtual[1:])
856 h = hgweb(real)
859 h = hgweb(real)
857 h.run()
860 h.run()
858 return
861 return
859
862
860 def header(**map):
863 def header(**map):
861 yield tmpl("header", **map)
864 yield tmpl("header", **map)
862
865
863 def footer(**map):
866 def footer(**map):
864 yield tmpl("footer", **map)
867 yield tmpl("footer", **map)
865
868
866 templates = templatepath()
869 templates = templatepath()
867 m = os.path.join(templates, "map")
870 m = os.path.join(templates, "map")
868 tmpl = templater(m, common_filters,
871 tmpl = templater(m, common_filters,
869 {"header": header, "footer": footer})
872 {"header": header, "footer": footer})
870
873
871 def entries(**map):
874 def entries(**map):
872 parity = 0
875 parity = 0
873 l = self.cp.items("paths")
876 l = self.cp.items("paths")
874 l.sort()
877 l.sort()
875 for v,r in l:
878 for v,r in l:
876 cp2 = ConfigParser.SafeConfigParser()
879 cp2 = ConfigParser.SafeConfigParser()
877 cp2.read(os.path.join(r, ".hg", "hgrc"))
880 cp2.read(os.path.join(r, ".hg", "hgrc"))
878
881
879 def get(sec, val, default):
882 def get(sec, val, default):
880 try:
883 try:
881 return cp2.get(sec, val)
884 return cp2.get(sec, val)
882 except:
885 except:
883 return default
886 return default
884
887
885 yield dict(author = get("web", "author", "unknown"),
888 yield dict(author = get("web", "author", "unknown"),
886 name = get("web", "name", v),
889 name = get("web", "name", v),
887 url = os.environ["REQUEST_URI"] + "/" + v,
890 url = os.environ["REQUEST_URI"] + "/" + v,
888 parity = parity,
891 parity = parity,
889 shortdesc = get("web", "description", "unknown"),
892 shortdesc = get("web", "description", "unknown"),
890 lastupdate = os.stat(os.path.join(r, ".hg",
893 lastupdate = os.stat(os.path.join(r, ".hg",
891 "00changelog.d")).st_mtime)
894 "00changelog.d")).st_mtime)
892
895
893 parity = 1 - parity
896 parity = 1 - parity
894
897
895 write(tmpl("index", entries = entries))
898 write(tmpl("index", entries = entries))
@@ -1,5 +1,6 b''
1 default = "changelog"
1 header = header-rss.tmpl
2 header = header-rss.tmpl
2 changelog = changelog-rss.tmpl
3 changelog = changelog-rss.tmpl
3 changelogentry = changelogentry-rss.tmpl
4 changelogentry = changelogentry-rss.tmpl
4 filelog = filelog-rss.tmpl
5 filelog = filelog-rss.tmpl
5 filelogentry = filelogentry-rss.tmpl
6 filelogentry = filelogentry-rss.tmpl
General Comments 0
You need to be logged in to leave comments. Login now