|
@@
-1,1164
+1,1165
b''
|
|
1
|
# hgweb/hgweb_mod.py - Web interface for a repository.
|
|
1
|
# hgweb/hgweb_mod.py - Web interface for a 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, 2006 Matt Mackall <mpm@selenic.com>
|
|
4
|
# Copyright 2005, 2006 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
|
|
9
|
import os
|
|
10
|
import os.path
|
|
10
|
import os.path
|
|
11
|
import mimetypes
|
|
11
|
import mimetypes
|
|
12
|
from mercurial.demandload import demandload
|
|
12
|
from mercurial.demandload import demandload
|
|
13
|
demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
|
|
13
|
demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
|
|
14
|
demandload(globals(), 'urllib bz2')
|
|
14
|
demandload(globals(), 'urllib bz2')
|
|
15
|
demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone,patch")
|
|
15
|
demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone,patch")
|
|
16
|
demandload(globals(), "mercurial:revlog,templater")
|
|
16
|
demandload(globals(), "mercurial:revlog,templater")
|
|
17
|
demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile,style_map")
|
|
17
|
demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile,style_map")
|
|
18
|
from mercurial.node import *
|
|
18
|
from mercurial.node import *
|
|
19
|
from mercurial.i18n import gettext as _
|
|
19
|
from mercurial.i18n import gettext as _
|
|
20
|
|
|
20
|
|
|
21
|
def _up(p):
|
|
21
|
def _up(p):
|
|
22
|
if p[0] != "/":
|
|
22
|
if p[0] != "/":
|
|
23
|
p = "/" + p
|
|
23
|
p = "/" + p
|
|
24
|
if p[-1] == "/":
|
|
24
|
if p[-1] == "/":
|
|
25
|
p = p[:-1]
|
|
25
|
p = p[:-1]
|
|
26
|
up = os.path.dirname(p)
|
|
26
|
up = os.path.dirname(p)
|
|
27
|
if up == "/":
|
|
27
|
if up == "/":
|
|
28
|
return "/"
|
|
28
|
return "/"
|
|
29
|
return up + "/"
|
|
29
|
return up + "/"
|
|
30
|
|
|
30
|
|
|
31
|
def revnavgen(pos, pagelen, limit, nodefunc):
|
|
31
|
def revnavgen(pos, pagelen, limit, nodefunc):
|
|
32
|
def seq(factor, limit=None):
|
|
32
|
def seq(factor, limit=None):
|
|
33
|
if limit:
|
|
33
|
if limit:
|
|
34
|
yield limit
|
|
34
|
yield limit
|
|
35
|
if limit >= 20 and limit <= 40:
|
|
35
|
if limit >= 20 and limit <= 40:
|
|
36
|
yield 50
|
|
36
|
yield 50
|
|
37
|
else:
|
|
37
|
else:
|
|
38
|
yield 1 * factor
|
|
38
|
yield 1 * factor
|
|
39
|
yield 3 * factor
|
|
39
|
yield 3 * factor
|
|
40
|
for f in seq(factor * 10):
|
|
40
|
for f in seq(factor * 10):
|
|
41
|
yield f
|
|
41
|
yield f
|
|
42
|
|
|
42
|
|
|
43
|
def nav(**map):
|
|
43
|
def nav(**map):
|
|
44
|
l = []
|
|
44
|
l = []
|
|
45
|
last = 0
|
|
45
|
last = 0
|
|
46
|
for f in seq(1, pagelen):
|
|
46
|
for f in seq(1, pagelen):
|
|
47
|
if f < pagelen or f <= last:
|
|
47
|
if f < pagelen or f <= last:
|
|
48
|
continue
|
|
48
|
continue
|
|
49
|
if f > limit:
|
|
49
|
if f > limit:
|
|
50
|
break
|
|
50
|
break
|
|
51
|
last = f
|
|
51
|
last = f
|
|
52
|
if pos + f < limit:
|
|
52
|
if pos + f < limit:
|
|
53
|
l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
|
|
53
|
l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
|
|
54
|
if pos - f >= 0:
|
|
54
|
if pos - f >= 0:
|
|
55
|
l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
|
|
55
|
l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
|
|
56
|
|
|
56
|
|
|
57
|
try:
|
|
57
|
try:
|
|
58
|
yield {"label": "(0)", "node": hex(nodefunc('0').node())}
|
|
58
|
yield {"label": "(0)", "node": hex(nodefunc('0').node())}
|
|
59
|
|
|
59
|
|
|
60
|
for label, node in l:
|
|
60
|
for label, node in l:
|
|
61
|
yield {"label": label, "node": node}
|
|
61
|
yield {"label": label, "node": node}
|
|
62
|
|
|
62
|
|
|
63
|
yield {"label": "tip", "node": "tip"}
|
|
63
|
yield {"label": "tip", "node": "tip"}
|
|
64
|
except hg.RepoError:
|
|
64
|
except hg.RepoError:
|
|
65
|
pass
|
|
65
|
pass
|
|
66
|
|
|
66
|
|
|
67
|
return nav
|
|
67
|
return nav
|
|
68
|
|
|
68
|
|
|
69
|
class hgweb(object):
|
|
69
|
class hgweb(object):
|
|
70
|
def __init__(self, repo, name=None):
|
|
70
|
def __init__(self, repo, name=None):
|
|
71
|
if type(repo) == type(""):
|
|
71
|
if type(repo) == type(""):
|
|
72
|
self.repo = hg.repository(ui.ui(report_untrusted=False), repo)
|
|
72
|
self.repo = hg.repository(ui.ui(report_untrusted=False), repo)
|
|
73
|
else:
|
|
73
|
else:
|
|
74
|
self.repo = repo
|
|
74
|
self.repo = repo
|
|
75
|
|
|
75
|
|
|
76
|
self.mtime = -1
|
|
76
|
self.mtime = -1
|
|
77
|
self.reponame = name
|
|
77
|
self.reponame = name
|
|
78
|
self.archives = 'zip', 'gz', 'bz2'
|
|
78
|
self.archives = 'zip', 'gz', 'bz2'
|
|
79
|
self.stripecount = 1
|
|
79
|
self.stripecount = 1
|
|
80
|
# a repo owner may set web.templates in .hg/hgrc to get any file
|
|
80
|
# a repo owner may set web.templates in .hg/hgrc to get any file
|
|
81
|
# readable by the user running the CGI script
|
|
81
|
# readable by the user running the CGI script
|
|
82
|
self.templatepath = self.config("web", "templates",
|
|
82
|
self.templatepath = self.config("web", "templates",
|
|
83
|
templater.templatepath(),
|
|
83
|
templater.templatepath(),
|
|
84
|
untrusted=False)
|
|
84
|
untrusted=False)
|
|
85
|
|
|
85
|
|
|
86
|
# The CGI scripts are often run by a user different from the repo owner.
|
|
86
|
# The CGI scripts are often run by a user different from the repo owner.
|
|
87
|
# Trust the settings from the .hg/hgrc files by default.
|
|
87
|
# Trust the settings from the .hg/hgrc files by default.
|
|
88
|
def config(self, section, name, default=None, untrusted=True):
|
|
88
|
def config(self, section, name, default=None, untrusted=True):
|
|
89
|
return self.repo.ui.config(section, name, default,
|
|
89
|
return self.repo.ui.config(section, name, default,
|
|
90
|
untrusted=untrusted)
|
|
90
|
untrusted=untrusted)
|
|
91
|
|
|
91
|
|
|
92
|
def configbool(self, section, name, default=False, untrusted=True):
|
|
92
|
def configbool(self, section, name, default=False, untrusted=True):
|
|
93
|
return self.repo.ui.configbool(section, name, default,
|
|
93
|
return self.repo.ui.configbool(section, name, default,
|
|
94
|
untrusted=untrusted)
|
|
94
|
untrusted=untrusted)
|
|
95
|
|
|
95
|
|
|
96
|
def configlist(self, section, name, default=None, untrusted=True):
|
|
96
|
def configlist(self, section, name, default=None, untrusted=True):
|
|
97
|
return self.repo.ui.configlist(section, name, default,
|
|
97
|
return self.repo.ui.configlist(section, name, default,
|
|
98
|
untrusted=untrusted)
|
|
98
|
untrusted=untrusted)
|
|
99
|
|
|
99
|
|
|
100
|
def refresh(self):
|
|
100
|
def refresh(self):
|
|
101
|
mtime = get_mtime(self.repo.root)
|
|
101
|
mtime = get_mtime(self.repo.root)
|
|
102
|
if mtime != self.mtime:
|
|
102
|
if mtime != self.mtime:
|
|
103
|
self.mtime = mtime
|
|
103
|
self.mtime = mtime
|
|
104
|
self.repo = hg.repository(self.repo.ui, self.repo.root)
|
|
104
|
self.repo = hg.repository(self.repo.ui, self.repo.root)
|
|
105
|
self.maxchanges = int(self.config("web", "maxchanges", 10))
|
|
105
|
self.maxchanges = int(self.config("web", "maxchanges", 10))
|
|
106
|
self.stripecount = int(self.config("web", "stripes", 1))
|
|
106
|
self.stripecount = int(self.config("web", "stripes", 1))
|
|
107
|
self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
|
|
107
|
self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
|
|
108
|
self.maxfiles = int(self.config("web", "maxfiles", 10))
|
|
108
|
self.maxfiles = int(self.config("web", "maxfiles", 10))
|
|
109
|
self.allowpull = self.configbool("web", "allowpull", True)
|
|
109
|
self.allowpull = self.configbool("web", "allowpull", True)
|
|
110
|
|
|
110
|
|
|
111
|
def archivelist(self, nodeid):
|
|
111
|
def archivelist(self, nodeid):
|
|
112
|
allowed = self.configlist("web", "allow_archive")
|
|
112
|
allowed = self.configlist("web", "allow_archive")
|
|
113
|
for i, spec in self.archive_specs.iteritems():
|
|
113
|
for i, spec in self.archive_specs.iteritems():
|
|
114
|
if i in allowed or self.configbool("web", "allow" + i):
|
|
114
|
if i in allowed or self.configbool("web", "allow" + i):
|
|
115
|
yield {"type" : i, "extension" : spec[2], "node" : nodeid}
|
|
115
|
yield {"type" : i, "extension" : spec[2], "node" : nodeid}
|
|
116
|
|
|
116
|
|
|
117
|
def listfilediffs(self, files, changeset):
|
|
117
|
def listfilediffs(self, files, changeset):
|
|
118
|
for f in files[:self.maxfiles]:
|
|
118
|
for f in files[:self.maxfiles]:
|
|
119
|
yield self.t("filedifflink", node=hex(changeset), file=f)
|
|
119
|
yield self.t("filedifflink", node=hex(changeset), file=f)
|
|
120
|
if len(files) > self.maxfiles:
|
|
120
|
if len(files) > self.maxfiles:
|
|
121
|
yield self.t("fileellipses")
|
|
121
|
yield self.t("fileellipses")
|
|
122
|
|
|
122
|
|
|
123
|
def siblings(self, siblings=[], hiderev=None, **args):
|
|
123
|
def siblings(self, siblings=[], hiderev=None, **args):
|
|
124
|
siblings = [s for s in siblings if s.node() != nullid]
|
|
124
|
siblings = [s for s in siblings if s.node() != nullid]
|
|
125
|
if len(siblings) == 1 and siblings[0].rev() == hiderev:
|
|
125
|
if len(siblings) == 1 and siblings[0].rev() == hiderev:
|
|
126
|
return
|
|
126
|
return
|
|
127
|
for s in siblings:
|
|
127
|
for s in siblings:
|
|
128
|
d = {'node': hex(s.node()), 'rev': s.rev()}
|
|
128
|
d = {'node': hex(s.node()), 'rev': s.rev()}
|
|
129
|
if hasattr(s, 'path'):
|
|
129
|
if hasattr(s, 'path'):
|
|
130
|
d['file'] = s.path()
|
|
130
|
d['file'] = s.path()
|
|
131
|
d.update(args)
|
|
131
|
d.update(args)
|
|
132
|
yield d
|
|
132
|
yield d
|
|
133
|
|
|
133
|
|
|
134
|
def renamelink(self, fl, node):
|
|
134
|
def renamelink(self, fl, node):
|
|
135
|
r = fl.renamed(node)
|
|
135
|
r = fl.renamed(node)
|
|
136
|
if r:
|
|
136
|
if r:
|
|
137
|
return [dict(file=r[0], node=hex(r[1]))]
|
|
137
|
return [dict(file=r[0], node=hex(r[1]))]
|
|
138
|
return []
|
|
138
|
return []
|
|
139
|
|
|
139
|
|
|
140
|
def showtag(self, t1, node=nullid, **args):
|
|
140
|
def showtag(self, t1, node=nullid, **args):
|
|
141
|
for t in self.repo.nodetags(node):
|
|
141
|
for t in self.repo.nodetags(node):
|
|
142
|
yield self.t(t1, tag=t, **args)
|
|
142
|
yield self.t(t1, tag=t, **args)
|
|
143
|
|
|
143
|
|
|
144
|
def diff(self, node1, node2, files):
|
|
144
|
def diff(self, node1, node2, files):
|
|
145
|
def filterfiles(filters, files):
|
|
145
|
def filterfiles(filters, files):
|
|
146
|
l = [x for x in files if x in filters]
|
|
146
|
l = [x for x in files if x in filters]
|
|
147
|
|
|
147
|
|
|
148
|
for t in filters:
|
|
148
|
for t in filters:
|
|
149
|
if t and t[-1] != os.sep:
|
|
149
|
if t and t[-1] != os.sep:
|
|
150
|
t += os.sep
|
|
150
|
t += os.sep
|
|
151
|
l += [x for x in files if x.startswith(t)]
|
|
151
|
l += [x for x in files if x.startswith(t)]
|
|
152
|
return l
|
|
152
|
return l
|
|
153
|
|
|
153
|
|
|
154
|
parity = [0]
|
|
154
|
parity = [0]
|
|
155
|
def diffblock(diff, f, fn):
|
|
155
|
def diffblock(diff, f, fn):
|
|
156
|
yield self.t("diffblock",
|
|
156
|
yield self.t("diffblock",
|
|
157
|
lines=prettyprintlines(diff),
|
|
157
|
lines=prettyprintlines(diff),
|
|
158
|
parity=parity[0],
|
|
158
|
parity=parity[0],
|
|
159
|
file=f,
|
|
159
|
file=f,
|
|
160
|
filenode=hex(fn or nullid))
|
|
160
|
filenode=hex(fn or nullid))
|
|
161
|
parity[0] = 1 - parity[0]
|
|
161
|
parity[0] = 1 - parity[0]
|
|
162
|
|
|
162
|
|
|
163
|
def prettyprintlines(diff):
|
|
163
|
def prettyprintlines(diff):
|
|
164
|
for l in diff.splitlines(1):
|
|
164
|
for l in diff.splitlines(1):
|
|
165
|
if l.startswith('+'):
|
|
165
|
if l.startswith('+'):
|
|
166
|
yield self.t("difflineplus", line=l)
|
|
166
|
yield self.t("difflineplus", line=l)
|
|
167
|
elif l.startswith('-'):
|
|
167
|
elif l.startswith('-'):
|
|
168
|
yield self.t("difflineminus", line=l)
|
|
168
|
yield self.t("difflineminus", line=l)
|
|
169
|
elif l.startswith('@'):
|
|
169
|
elif l.startswith('@'):
|
|
170
|
yield self.t("difflineat", line=l)
|
|
170
|
yield self.t("difflineat", line=l)
|
|
171
|
else:
|
|
171
|
else:
|
|
172
|
yield self.t("diffline", line=l)
|
|
172
|
yield self.t("diffline", line=l)
|
|
173
|
|
|
173
|
|
|
174
|
r = self.repo
|
|
174
|
r = self.repo
|
|
175
|
cl = r.changelog
|
|
175
|
cl = r.changelog
|
|
176
|
mf = r.manifest
|
|
176
|
mf = r.manifest
|
|
177
|
change1 = cl.read(node1)
|
|
177
|
change1 = cl.read(node1)
|
|
178
|
change2 = cl.read(node2)
|
|
178
|
change2 = cl.read(node2)
|
|
179
|
mmap1 = mf.read(change1[0])
|
|
179
|
mmap1 = mf.read(change1[0])
|
|
180
|
mmap2 = mf.read(change2[0])
|
|
180
|
mmap2 = mf.read(change2[0])
|
|
181
|
date1 = util.datestr(change1[2])
|
|
181
|
date1 = util.datestr(change1[2])
|
|
182
|
date2 = util.datestr(change2[2])
|
|
182
|
date2 = util.datestr(change2[2])
|
|
183
|
|
|
183
|
|
|
184
|
modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
|
|
184
|
modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
|
|
185
|
if files:
|
|
185
|
if files:
|
|
186
|
modified, added, removed = map(lambda x: filterfiles(files, x),
|
|
186
|
modified, added, removed = map(lambda x: filterfiles(files, x),
|
|
187
|
(modified, added, removed))
|
|
187
|
(modified, added, removed))
|
|
188
|
|
|
188
|
|
|
189
|
diffopts = patch.diffopts(self.repo.ui, untrusted=True)
|
|
189
|
diffopts = patch.diffopts(self.repo.ui, untrusted=True)
|
|
190
|
for f in modified:
|
|
190
|
for f in modified:
|
|
191
|
to = r.file(f).read(mmap1[f])
|
|
191
|
to = r.file(f).read(mmap1[f])
|
|
192
|
tn = r.file(f).read(mmap2[f])
|
|
192
|
tn = r.file(f).read(mmap2[f])
|
|
193
|
yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
|
|
193
|
yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
|
|
194
|
opts=diffopts), f, tn)
|
|
194
|
opts=diffopts), f, tn)
|
|
195
|
for f in added:
|
|
195
|
for f in added:
|
|
196
|
to = None
|
|
196
|
to = None
|
|
197
|
tn = r.file(f).read(mmap2[f])
|
|
197
|
tn = r.file(f).read(mmap2[f])
|
|
198
|
yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
|
|
198
|
yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
|
|
199
|
opts=diffopts), f, tn)
|
|
199
|
opts=diffopts), f, tn)
|
|
200
|
for f in removed:
|
|
200
|
for f in removed:
|
|
201
|
to = r.file(f).read(mmap1[f])
|
|
201
|
to = r.file(f).read(mmap1[f])
|
|
202
|
tn = None
|
|
202
|
tn = None
|
|
203
|
yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
|
|
203
|
yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
|
|
204
|
opts=diffopts), f, tn)
|
|
204
|
opts=diffopts), f, tn)
|
|
205
|
|
|
205
|
|
|
206
|
def changelog(self, ctx, shortlog=False):
|
|
206
|
def changelog(self, ctx, shortlog=False):
|
|
207
|
def changelist(**map):
|
|
207
|
def changelist(**map):
|
|
208
|
parity = (start - end) & 1
|
|
208
|
parity = (start - end) & 1
|
|
209
|
cl = self.repo.changelog
|
|
209
|
cl = self.repo.changelog
|
|
210
|
l = [] # build a list in forward order for efficiency
|
|
210
|
l = [] # build a list in forward order for efficiency
|
|
211
|
for i in xrange(start, end):
|
|
211
|
for i in xrange(start, end):
|
|
212
|
ctx = self.repo.changectx(i)
|
|
212
|
ctx = self.repo.changectx(i)
|
|
213
|
n = ctx.node()
|
|
213
|
n = ctx.node()
|
|
214
|
|
|
214
|
|
|
215
|
l.insert(0, {"parity": parity,
|
|
215
|
l.insert(0, {"parity": parity,
|
|
216
|
"author": ctx.user(),
|
|
216
|
"author": ctx.user(),
|
|
217
|
"parent": self.siblings(ctx.parents(), i - 1),
|
|
217
|
"parent": self.siblings(ctx.parents(), i - 1),
|
|
218
|
"child": self.siblings(ctx.children(), i + 1),
|
|
218
|
"child": self.siblings(ctx.children(), i + 1),
|
|
219
|
"changelogtag": self.showtag("changelogtag",n),
|
|
219
|
"changelogtag": self.showtag("changelogtag",n),
|
|
220
|
"desc": ctx.description(),
|
|
220
|
"desc": ctx.description(),
|
|
221
|
"date": ctx.date(),
|
|
221
|
"date": ctx.date(),
|
|
222
|
"files": self.listfilediffs(ctx.files(), n),
|
|
222
|
"files": self.listfilediffs(ctx.files(), n),
|
|
223
|
"rev": i,
|
|
223
|
"rev": i,
|
|
224
|
"node": hex(n)})
|
|
224
|
"node": hex(n)})
|
|
225
|
parity = 1 - parity
|
|
225
|
parity = 1 - parity
|
|
226
|
|
|
226
|
|
|
227
|
for e in l:
|
|
227
|
for e in l:
|
|
228
|
yield e
|
|
228
|
yield e
|
|
229
|
|
|
229
|
|
|
230
|
maxchanges = shortlog and self.maxshortchanges or self.maxchanges
|
|
230
|
maxchanges = shortlog and self.maxshortchanges or self.maxchanges
|
|
231
|
cl = self.repo.changelog
|
|
231
|
cl = self.repo.changelog
|
|
232
|
count = cl.count()
|
|
232
|
count = cl.count()
|
|
233
|
pos = ctx.rev()
|
|
233
|
pos = ctx.rev()
|
|
234
|
start = max(0, pos - maxchanges + 1)
|
|
234
|
start = max(0, pos - maxchanges + 1)
|
|
235
|
end = min(count, start + maxchanges)
|
|
235
|
end = min(count, start + maxchanges)
|
|
236
|
pos = end - 1
|
|
236
|
pos = end - 1
|
|
237
|
|
|
237
|
|
|
238
|
changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
|
|
238
|
changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
|
|
239
|
|
|
239
|
|
|
240
|
yield self.t(shortlog and 'shortlog' or 'changelog',
|
|
240
|
yield self.t(shortlog and 'shortlog' or 'changelog',
|
|
241
|
changenav=changenav,
|
|
241
|
changenav=changenav,
|
|
242
|
node=hex(cl.tip()),
|
|
242
|
node=hex(cl.tip()),
|
|
243
|
rev=pos, changesets=count, entries=changelist,
|
|
243
|
rev=pos, changesets=count, entries=changelist,
|
|
244
|
archives=self.archivelist("tip"))
|
|
244
|
archives=self.archivelist("tip"))
|
|
245
|
|
|
245
|
|
|
246
|
def search(self, query):
|
|
246
|
def search(self, query):
|
|
247
|
|
|
247
|
|
|
248
|
def changelist(**map):
|
|
248
|
def changelist(**map):
|
|
249
|
cl = self.repo.changelog
|
|
249
|
cl = self.repo.changelog
|
|
250
|
count = 0
|
|
250
|
count = 0
|
|
251
|
qw = query.lower().split()
|
|
251
|
qw = query.lower().split()
|
|
252
|
|
|
252
|
|
|
253
|
def revgen():
|
|
253
|
def revgen():
|
|
254
|
for i in xrange(cl.count() - 1, 0, -100):
|
|
254
|
for i in xrange(cl.count() - 1, 0, -100):
|
|
255
|
l = []
|
|
255
|
l = []
|
|
256
|
for j in xrange(max(0, i - 100), i):
|
|
256
|
for j in xrange(max(0, i - 100), i):
|
|
257
|
ctx = self.repo.changectx(j)
|
|
257
|
ctx = self.repo.changectx(j)
|
|
258
|
l.append(ctx)
|
|
258
|
l.append(ctx)
|
|
259
|
l.reverse()
|
|
259
|
l.reverse()
|
|
260
|
for e in l:
|
|
260
|
for e in l:
|
|
261
|
yield e
|
|
261
|
yield e
|
|
262
|
|
|
262
|
|
|
263
|
for ctx in revgen():
|
|
263
|
for ctx in revgen():
|
|
264
|
miss = 0
|
|
264
|
miss = 0
|
|
265
|
for q in qw:
|
|
265
|
for q in qw:
|
|
266
|
if not (q in ctx.user().lower() or
|
|
266
|
if not (q in ctx.user().lower() or
|
|
267
|
q in ctx.description().lower() or
|
|
267
|
q in ctx.description().lower() or
|
|
268
|
q in " ".join(ctx.files()[:20]).lower()):
|
|
268
|
q in " ".join(ctx.files()[:20]).lower()):
|
|
269
|
miss = 1
|
|
269
|
miss = 1
|
|
270
|
break
|
|
270
|
break
|
|
271
|
if miss:
|
|
271
|
if miss:
|
|
272
|
continue
|
|
272
|
continue
|
|
273
|
|
|
273
|
|
|
274
|
count += 1
|
|
274
|
count += 1
|
|
275
|
n = ctx.node()
|
|
275
|
n = ctx.node()
|
|
276
|
|
|
276
|
|
|
277
|
yield self.t('searchentry',
|
|
277
|
yield self.t('searchentry',
|
|
278
|
parity=self.stripes(count),
|
|
278
|
parity=self.stripes(count),
|
|
279
|
author=ctx.user(),
|
|
279
|
author=ctx.user(),
|
|
280
|
parent=self.siblings(ctx.parents()),
|
|
280
|
parent=self.siblings(ctx.parents()),
|
|
281
|
child=self.siblings(ctx.children()),
|
|
281
|
child=self.siblings(ctx.children()),
|
|
282
|
changelogtag=self.showtag("changelogtag",n),
|
|
282
|
changelogtag=self.showtag("changelogtag",n),
|
|
283
|
desc=ctx.description(),
|
|
283
|
desc=ctx.description(),
|
|
284
|
date=ctx.date(),
|
|
284
|
date=ctx.date(),
|
|
285
|
files=self.listfilediffs(ctx.files(), n),
|
|
285
|
files=self.listfilediffs(ctx.files(), n),
|
|
286
|
rev=ctx.rev(),
|
|
286
|
rev=ctx.rev(),
|
|
287
|
node=hex(n))
|
|
287
|
node=hex(n))
|
|
288
|
|
|
288
|
|
|
289
|
if count >= self.maxchanges:
|
|
289
|
if count >= self.maxchanges:
|
|
290
|
break
|
|
290
|
break
|
|
291
|
|
|
291
|
|
|
292
|
cl = self.repo.changelog
|
|
292
|
cl = self.repo.changelog
|
|
293
|
|
|
293
|
|
|
294
|
yield self.t('search',
|
|
294
|
yield self.t('search',
|
|
295
|
query=query,
|
|
295
|
query=query,
|
|
296
|
node=hex(cl.tip()),
|
|
296
|
node=hex(cl.tip()),
|
|
297
|
entries=changelist)
|
|
297
|
entries=changelist)
|
|
298
|
|
|
298
|
|
|
299
|
def changeset(self, ctx):
|
|
299
|
def changeset(self, ctx):
|
|
300
|
n = ctx.node()
|
|
300
|
n = ctx.node()
|
|
301
|
parents = ctx.parents()
|
|
301
|
parents = ctx.parents()
|
|
302
|
p1 = parents[0].node()
|
|
302
|
p1 = parents[0].node()
|
|
303
|
|
|
303
|
|
|
304
|
files = []
|
|
304
|
files = []
|
|
305
|
parity = 0
|
|
305
|
parity = 0
|
|
306
|
for f in ctx.files():
|
|
306
|
for f in ctx.files():
|
|
307
|
files.append(self.t("filenodelink",
|
|
307
|
files.append(self.t("filenodelink",
|
|
308
|
node=hex(n), file=f,
|
|
308
|
node=hex(n), file=f,
|
|
309
|
parity=parity))
|
|
309
|
parity=parity))
|
|
310
|
parity = 1 - parity
|
|
310
|
parity = 1 - parity
|
|
311
|
|
|
311
|
|
|
312
|
def diff(**map):
|
|
312
|
def diff(**map):
|
|
313
|
yield self.diff(p1, n, None)
|
|
313
|
yield self.diff(p1, n, None)
|
|
314
|
|
|
314
|
|
|
315
|
yield self.t('changeset',
|
|
315
|
yield self.t('changeset',
|
|
316
|
diff=diff,
|
|
316
|
diff=diff,
|
|
317
|
rev=ctx.rev(),
|
|
317
|
rev=ctx.rev(),
|
|
318
|
node=hex(n),
|
|
318
|
node=hex(n),
|
|
319
|
parent=self.siblings(parents),
|
|
319
|
parent=self.siblings(parents),
|
|
320
|
child=self.siblings(ctx.children()),
|
|
320
|
child=self.siblings(ctx.children()),
|
|
321
|
changesettag=self.showtag("changesettag",n),
|
|
321
|
changesettag=self.showtag("changesettag",n),
|
|
322
|
author=ctx.user(),
|
|
322
|
author=ctx.user(),
|
|
323
|
desc=ctx.description(),
|
|
323
|
desc=ctx.description(),
|
|
324
|
date=ctx.date(),
|
|
324
|
date=ctx.date(),
|
|
325
|
files=files,
|
|
325
|
files=files,
|
|
326
|
archives=self.archivelist(hex(n)))
|
|
326
|
archives=self.archivelist(hex(n)))
|
|
327
|
|
|
327
|
|
|
328
|
def filelog(self, fctx):
|
|
328
|
def filelog(self, fctx):
|
|
329
|
f = fctx.path()
|
|
329
|
f = fctx.path()
|
|
330
|
fl = fctx.filelog()
|
|
330
|
fl = fctx.filelog()
|
|
331
|
count = fl.count()
|
|
331
|
count = fl.count()
|
|
332
|
pagelen = self.maxshortchanges
|
|
332
|
pagelen = self.maxshortchanges
|
|
333
|
pos = fctx.filerev()
|
|
333
|
pos = fctx.filerev()
|
|
334
|
start = max(0, pos - pagelen + 1)
|
|
334
|
start = max(0, pos - pagelen + 1)
|
|
335
|
end = min(count, start + pagelen)
|
|
335
|
end = min(count, start + pagelen)
|
|
336
|
pos = end - 1
|
|
336
|
pos = end - 1
|
|
337
|
|
|
337
|
|
|
338
|
def entries(**map):
|
|
338
|
def entries(**map):
|
|
339
|
l = []
|
|
339
|
l = []
|
|
340
|
parity = (count - 1) & 1
|
|
340
|
parity = (count - 1) & 1
|
|
341
|
|
|
341
|
|
|
342
|
for i in xrange(start, end):
|
|
342
|
for i in xrange(start, end):
|
|
343
|
ctx = fctx.filectx(i)
|
|
343
|
ctx = fctx.filectx(i)
|
|
344
|
n = fl.node(i)
|
|
344
|
n = fl.node(i)
|
|
345
|
|
|
345
|
|
|
346
|
l.insert(0, {"parity": parity,
|
|
346
|
l.insert(0, {"parity": parity,
|
|
347
|
"filerev": i,
|
|
347
|
"filerev": i,
|
|
348
|
"file": f,
|
|
348
|
"file": f,
|
|
349
|
"node": hex(ctx.node()),
|
|
349
|
"node": hex(ctx.node()),
|
|
350
|
"author": ctx.user(),
|
|
350
|
"author": ctx.user(),
|
|
351
|
"date": ctx.date(),
|
|
351
|
"date": ctx.date(),
|
|
352
|
"rename": self.renamelink(fl, n),
|
|
352
|
"rename": self.renamelink(fl, n),
|
|
353
|
"parent": self.siblings(fctx.parents()),
|
|
353
|
"parent": self.siblings(fctx.parents()),
|
|
354
|
"child": self.siblings(fctx.children()),
|
|
354
|
"child": self.siblings(fctx.children()),
|
|
355
|
"desc": ctx.description()})
|
|
355
|
"desc": ctx.description()})
|
|
356
|
parity = 1 - parity
|
|
356
|
parity = 1 - parity
|
|
357
|
|
|
357
|
|
|
358
|
for e in l:
|
|
358
|
for e in l:
|
|
359
|
yield e
|
|
359
|
yield e
|
|
360
|
|
|
360
|
|
|
361
|
nodefunc = lambda x: fctx.filectx(fileid=x)
|
|
361
|
nodefunc = lambda x: fctx.filectx(fileid=x)
|
|
362
|
nav = revnavgen(pos, pagelen, count, nodefunc)
|
|
362
|
nav = revnavgen(pos, pagelen, count, nodefunc)
|
|
363
|
yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
|
|
363
|
yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
|
|
364
|
entries=entries)
|
|
364
|
entries=entries)
|
|
365
|
|
|
365
|
|
|
366
|
def filerevision(self, fctx):
|
|
366
|
def filerevision(self, fctx):
|
|
367
|
f = fctx.path()
|
|
367
|
f = fctx.path()
|
|
368
|
text = fctx.data()
|
|
368
|
text = fctx.data()
|
|
369
|
fl = fctx.filelog()
|
|
369
|
fl = fctx.filelog()
|
|
370
|
n = fctx.filenode()
|
|
370
|
n = fctx.filenode()
|
|
371
|
|
|
371
|
|
|
372
|
mt = mimetypes.guess_type(f)[0]
|
|
372
|
mt = mimetypes.guess_type(f)[0]
|
|
373
|
rawtext = text
|
|
373
|
rawtext = text
|
|
374
|
if util.binary(text):
|
|
374
|
if util.binary(text):
|
|
375
|
mt = mt or 'application/octet-stream'
|
|
375
|
mt = mt or 'application/octet-stream'
|
|
376
|
text = "(binary:%s)" % mt
|
|
376
|
text = "(binary:%s)" % mt
|
|
377
|
mt = mt or 'text/plain'
|
|
377
|
mt = mt or 'text/plain'
|
|
378
|
|
|
378
|
|
|
379
|
def lines():
|
|
379
|
def lines():
|
|
380
|
for l, t in enumerate(text.splitlines(1)):
|
|
380
|
for l, t in enumerate(text.splitlines(1)):
|
|
381
|
yield {"line": t,
|
|
381
|
yield {"line": t,
|
|
382
|
"linenumber": "% 6d" % (l + 1),
|
|
382
|
"linenumber": "% 6d" % (l + 1),
|
|
383
|
"parity": self.stripes(l)}
|
|
383
|
"parity": self.stripes(l)}
|
|
384
|
|
|
384
|
|
|
385
|
yield self.t("filerevision",
|
|
385
|
yield self.t("filerevision",
|
|
386
|
file=f,
|
|
386
|
file=f,
|
|
387
|
path=_up(f),
|
|
387
|
path=_up(f),
|
|
388
|
text=lines(),
|
|
388
|
text=lines(),
|
|
389
|
raw=rawtext,
|
|
389
|
raw=rawtext,
|
|
390
|
mimetype=mt,
|
|
390
|
mimetype=mt,
|
|
391
|
rev=fctx.rev(),
|
|
391
|
rev=fctx.rev(),
|
|
392
|
node=hex(fctx.node()),
|
|
392
|
node=hex(fctx.node()),
|
|
393
|
author=fctx.user(),
|
|
393
|
author=fctx.user(),
|
|
394
|
date=fctx.date(),
|
|
394
|
date=fctx.date(),
|
|
395
|
desc=fctx.description(),
|
|
395
|
desc=fctx.description(),
|
|
396
|
parent=self.siblings(fctx.parents()),
|
|
396
|
parent=self.siblings(fctx.parents()),
|
|
397
|
child=self.siblings(fctx.children()),
|
|
397
|
child=self.siblings(fctx.children()),
|
|
398
|
rename=self.renamelink(fl, n),
|
|
398
|
rename=self.renamelink(fl, n),
|
|
399
|
permissions=fctx.manifest().execf(f))
|
|
399
|
permissions=fctx.manifest().execf(f))
|
|
400
|
|
|
400
|
|
|
401
|
def fileannotate(self, fctx):
|
|
401
|
def fileannotate(self, fctx):
|
|
402
|
f = fctx.path()
|
|
402
|
f = fctx.path()
|
|
403
|
n = fctx.filenode()
|
|
403
|
n = fctx.filenode()
|
|
404
|
fl = fctx.filelog()
|
|
404
|
fl = fctx.filelog()
|
|
405
|
|
|
405
|
|
|
406
|
def annotate(**map):
|
|
406
|
def annotate(**map):
|
|
407
|
parity = 0
|
|
407
|
parity = 0
|
|
408
|
last = None
|
|
408
|
last = None
|
|
409
|
for f, l in fctx.annotate(follow=True):
|
|
409
|
for f, l in fctx.annotate(follow=True):
|
|
410
|
fnode = f.filenode()
|
|
410
|
fnode = f.filenode()
|
|
411
|
name = self.repo.ui.shortuser(f.user())
|
|
411
|
name = self.repo.ui.shortuser(f.user())
|
|
412
|
|
|
412
|
|
|
413
|
if last != fnode:
|
|
413
|
if last != fnode:
|
|
414
|
parity = 1 - parity
|
|
414
|
parity = 1 - parity
|
|
415
|
last = fnode
|
|
415
|
last = fnode
|
|
416
|
|
|
416
|
|
|
417
|
yield {"parity": parity,
|
|
417
|
yield {"parity": parity,
|
|
418
|
"node": hex(f.node()),
|
|
418
|
"node": hex(f.node()),
|
|
419
|
"rev": f.rev(),
|
|
419
|
"rev": f.rev(),
|
|
420
|
"author": name,
|
|
420
|
"author": name,
|
|
421
|
"file": f.path(),
|
|
421
|
"file": f.path(),
|
|
422
|
"line": l}
|
|
422
|
"line": l}
|
|
423
|
|
|
423
|
|
|
424
|
yield self.t("fileannotate",
|
|
424
|
yield self.t("fileannotate",
|
|
425
|
file=f,
|
|
425
|
file=f,
|
|
426
|
annotate=annotate,
|
|
426
|
annotate=annotate,
|
|
427
|
path=_up(f),
|
|
427
|
path=_up(f),
|
|
428
|
rev=fctx.rev(),
|
|
428
|
rev=fctx.rev(),
|
|
429
|
node=hex(fctx.node()),
|
|
429
|
node=hex(fctx.node()),
|
|
430
|
author=fctx.user(),
|
|
430
|
author=fctx.user(),
|
|
431
|
date=fctx.date(),
|
|
431
|
date=fctx.date(),
|
|
432
|
desc=fctx.description(),
|
|
432
|
desc=fctx.description(),
|
|
433
|
rename=self.renamelink(fl, n),
|
|
433
|
rename=self.renamelink(fl, n),
|
|
434
|
parent=self.siblings(fctx.parents()),
|
|
434
|
parent=self.siblings(fctx.parents()),
|
|
435
|
child=self.siblings(fctx.children()),
|
|
435
|
child=self.siblings(fctx.children()),
|
|
436
|
permissions=fctx.manifest().execf(f))
|
|
436
|
permissions=fctx.manifest().execf(f))
|
|
437
|
|
|
437
|
|
|
438
|
def manifest(self, ctx, path):
|
|
438
|
def manifest(self, ctx, path):
|
|
439
|
mf = ctx.manifest()
|
|
439
|
mf = ctx.manifest()
|
|
440
|
node = ctx.node()
|
|
440
|
node = ctx.node()
|
|
441
|
|
|
441
|
|
|
442
|
files = {}
|
|
442
|
files = {}
|
|
443
|
|
|
443
|
|
|
444
|
if path and path[-1] != "/":
|
|
444
|
if path and path[-1] != "/":
|
|
445
|
path += "/"
|
|
445
|
path += "/"
|
|
446
|
l = len(path)
|
|
446
|
l = len(path)
|
|
447
|
abspath = "/" + path
|
|
447
|
abspath = "/" + path
|
|
448
|
|
|
448
|
|
|
449
|
for f, n in mf.items():
|
|
449
|
for f, n in mf.items():
|
|
450
|
if f[:l] != path:
|
|
450
|
if f[:l] != path:
|
|
451
|
continue
|
|
451
|
continue
|
|
452
|
remain = f[l:]
|
|
452
|
remain = f[l:]
|
|
453
|
if "/" in remain:
|
|
453
|
if "/" in remain:
|
|
454
|
short = remain[:remain.index("/") + 1] # bleah
|
|
454
|
short = remain[:remain.index("/") + 1] # bleah
|
|
455
|
files[short] = (f, None)
|
|
455
|
files[short] = (f, None)
|
|
456
|
else:
|
|
456
|
else:
|
|
457
|
short = os.path.basename(remain)
|
|
457
|
short = os.path.basename(remain)
|
|
458
|
files[short] = (f, n)
|
|
458
|
files[short] = (f, n)
|
|
459
|
|
|
459
|
|
|
460
|
def filelist(**map):
|
|
460
|
def filelist(**map):
|
|
461
|
parity = 0
|
|
461
|
parity = 0
|
|
462
|
fl = files.keys()
|
|
462
|
fl = files.keys()
|
|
463
|
fl.sort()
|
|
463
|
fl.sort()
|
|
464
|
for f in fl:
|
|
464
|
for f in fl:
|
|
465
|
full, fnode = files[f]
|
|
465
|
full, fnode = files[f]
|
|
466
|
if not fnode:
|
|
466
|
if not fnode:
|
|
467
|
continue
|
|
467
|
continue
|
|
468
|
|
|
468
|
|
|
469
|
yield {"file": full,
|
|
469
|
yield {"file": full,
|
|
470
|
"parity": self.stripes(parity),
|
|
470
|
"parity": self.stripes(parity),
|
|
471
|
"basename": f,
|
|
471
|
"basename": f,
|
|
472
|
"size": ctx.filectx(full).size(),
|
|
472
|
"size": ctx.filectx(full).size(),
|
|
473
|
"permissions": mf.execf(full)}
|
|
473
|
"permissions": mf.execf(full)}
|
|
474
|
parity += 1
|
|
474
|
parity += 1
|
|
475
|
|
|
475
|
|
|
476
|
def dirlist(**map):
|
|
476
|
def dirlist(**map):
|
|
477
|
parity = 0
|
|
477
|
parity = 0
|
|
478
|
fl = files.keys()
|
|
478
|
fl = files.keys()
|
|
479
|
fl.sort()
|
|
479
|
fl.sort()
|
|
480
|
for f in fl:
|
|
480
|
for f in fl:
|
|
481
|
full, fnode = files[f]
|
|
481
|
full, fnode = files[f]
|
|
482
|
if fnode:
|
|
482
|
if fnode:
|
|
483
|
continue
|
|
483
|
continue
|
|
484
|
|
|
484
|
|
|
485
|
yield {"parity": self.stripes(parity),
|
|
485
|
yield {"parity": self.stripes(parity),
|
|
486
|
"path": os.path.join(abspath, f),
|
|
486
|
"path": os.path.join(abspath, f),
|
|
487
|
"basename": f[:-1]}
|
|
487
|
"basename": f[:-1]}
|
|
488
|
parity += 1
|
|
488
|
parity += 1
|
|
489
|
|
|
489
|
|
|
490
|
yield self.t("manifest",
|
|
490
|
yield self.t("manifest",
|
|
491
|
rev=ctx.rev(),
|
|
491
|
rev=ctx.rev(),
|
|
492
|
node=hex(node),
|
|
492
|
node=hex(node),
|
|
493
|
path=abspath,
|
|
493
|
path=abspath,
|
|
494
|
up=_up(abspath),
|
|
494
|
up=_up(abspath),
|
|
495
|
fentries=filelist,
|
|
495
|
fentries=filelist,
|
|
496
|
dentries=dirlist,
|
|
496
|
dentries=dirlist,
|
|
497
|
archives=self.archivelist(hex(node)))
|
|
497
|
archives=self.archivelist(hex(node)))
|
|
498
|
|
|
498
|
|
|
499
|
def tags(self):
|
|
499
|
def tags(self):
|
|
500
|
cl = self.repo.changelog
|
|
500
|
cl = self.repo.changelog
|
|
501
|
|
|
501
|
|
|
502
|
i = self.repo.tagslist()
|
|
502
|
i = self.repo.tagslist()
|
|
503
|
i.reverse()
|
|
503
|
i.reverse()
|
|
504
|
|
|
504
|
|
|
505
|
def entries(notip=False, **map):
|
|
505
|
def entries(notip=False, **map):
|
|
506
|
parity = 0
|
|
506
|
parity = 0
|
|
507
|
for k, n in i:
|
|
507
|
for k, n in i:
|
|
508
|
if notip and k == "tip":
|
|
508
|
if notip and k == "tip":
|
|
509
|
continue
|
|
509
|
continue
|
|
510
|
yield {"parity": self.stripes(parity),
|
|
510
|
yield {"parity": self.stripes(parity),
|
|
511
|
"tag": k,
|
|
511
|
"tag": k,
|
|
512
|
"date": cl.read(n)[2],
|
|
512
|
"date": cl.read(n)[2],
|
|
513
|
"node": hex(n)}
|
|
513
|
"node": hex(n)}
|
|
514
|
parity += 1
|
|
514
|
parity += 1
|
|
515
|
|
|
515
|
|
|
516
|
yield self.t("tags",
|
|
516
|
yield self.t("tags",
|
|
517
|
node=hex(self.repo.changelog.tip()),
|
|
517
|
node=hex(self.repo.changelog.tip()),
|
|
518
|
entries=lambda **x: entries(False, **x),
|
|
518
|
entries=lambda **x: entries(False, **x),
|
|
519
|
entriesnotip=lambda **x: entries(True, **x))
|
|
519
|
entriesnotip=lambda **x: entries(True, **x))
|
|
520
|
|
|
520
|
|
|
521
|
def summary(self):
|
|
521
|
def summary(self):
|
|
522
|
cl = self.repo.changelog
|
|
522
|
cl = self.repo.changelog
|
|
523
|
|
|
523
|
|
|
524
|
i = self.repo.tagslist()
|
|
524
|
i = self.repo.tagslist()
|
|
525
|
i.reverse()
|
|
525
|
i.reverse()
|
|
526
|
|
|
526
|
|
|
527
|
def tagentries(**map):
|
|
527
|
def tagentries(**map):
|
|
528
|
parity = 0
|
|
528
|
parity = 0
|
|
529
|
count = 0
|
|
529
|
count = 0
|
|
530
|
for k, n in i:
|
|
530
|
for k, n in i:
|
|
531
|
if k == "tip": # skip tip
|
|
531
|
if k == "tip": # skip tip
|
|
532
|
continue;
|
|
532
|
continue;
|
|
533
|
|
|
533
|
|
|
534
|
count += 1
|
|
534
|
count += 1
|
|
535
|
if count > 10: # limit to 10 tags
|
|
535
|
if count > 10: # limit to 10 tags
|
|
536
|
break;
|
|
536
|
break;
|
|
537
|
|
|
537
|
|
|
538
|
c = cl.read(n)
|
|
538
|
c = cl.read(n)
|
|
539
|
t = c[2]
|
|
539
|
t = c[2]
|
|
540
|
|
|
540
|
|
|
541
|
yield self.t("tagentry",
|
|
541
|
yield self.t("tagentry",
|
|
542
|
parity = self.stripes(parity),
|
|
542
|
parity = self.stripes(parity),
|
|
543
|
tag = k,
|
|
543
|
tag = k,
|
|
544
|
node = hex(n),
|
|
544
|
node = hex(n),
|
|
545
|
date = t)
|
|
545
|
date = t)
|
|
546
|
parity += 1
|
|
546
|
parity += 1
|
|
547
|
|
|
547
|
|
|
548
|
def heads(**map):
|
|
548
|
def heads(**map):
|
|
549
|
parity = 0
|
|
549
|
parity = 0
|
|
550
|
count = 0
|
|
550
|
count = 0
|
|
551
|
|
|
551
|
|
|
552
|
for node in self.repo.heads():
|
|
552
|
for node in self.repo.heads():
|
|
553
|
count += 1
|
|
553
|
count += 1
|
|
554
|
if count > 10:
|
|
554
|
if count > 10:
|
|
555
|
break;
|
|
555
|
break;
|
|
556
|
|
|
556
|
|
|
557
|
ctx = self.repo.changectx(node)
|
|
557
|
ctx = self.repo.changectx(node)
|
|
558
|
|
|
558
|
|
|
559
|
yield {'parity': self.stripes(parity),
|
|
559
|
yield {'parity': self.stripes(parity),
|
|
560
|
'branch': ctx.branch(),
|
|
560
|
'branch': ctx.branch(),
|
|
561
|
'node': hex(node),
|
|
561
|
'node': hex(node),
|
|
562
|
'date': ctx.date()}
|
|
562
|
'date': ctx.date()}
|
|
563
|
parity += 1
|
|
563
|
parity += 1
|
|
564
|
|
|
564
|
|
|
565
|
def changelist(**map):
|
|
565
|
def changelist(**map):
|
|
566
|
parity = 0
|
|
566
|
parity = 0
|
|
567
|
cl = self.repo.changelog
|
|
567
|
cl = self.repo.changelog
|
|
568
|
l = [] # build a list in forward order for efficiency
|
|
568
|
l = [] # build a list in forward order for efficiency
|
|
569
|
for i in xrange(start, end):
|
|
569
|
for i in xrange(start, end):
|
|
570
|
n = cl.node(i)
|
|
570
|
n = cl.node(i)
|
|
571
|
changes = cl.read(n)
|
|
571
|
changes = cl.read(n)
|
|
572
|
hn = hex(n)
|
|
572
|
hn = hex(n)
|
|
573
|
t = changes[2]
|
|
573
|
t = changes[2]
|
|
574
|
|
|
574
|
|
|
575
|
l.insert(0, self.t(
|
|
575
|
l.insert(0, self.t(
|
|
576
|
'shortlogentry',
|
|
576
|
'shortlogentry',
|
|
577
|
parity = parity,
|
|
577
|
parity = parity,
|
|
578
|
author = changes[1],
|
|
578
|
author = changes[1],
|
|
579
|
desc = changes[4],
|
|
579
|
desc = changes[4],
|
|
580
|
date = t,
|
|
580
|
date = t,
|
|
581
|
rev = i,
|
|
581
|
rev = i,
|
|
582
|
node = hn))
|
|
582
|
node = hn))
|
|
583
|
parity = 1 - parity
|
|
583
|
parity = 1 - parity
|
|
584
|
|
|
584
|
|
|
585
|
yield l
|
|
585
|
yield l
|
|
586
|
|
|
586
|
|
|
587
|
count = cl.count()
|
|
587
|
count = cl.count()
|
|
588
|
start = max(0, count - self.maxchanges)
|
|
588
|
start = max(0, count - self.maxchanges)
|
|
589
|
end = min(count, start + self.maxchanges)
|
|
589
|
end = min(count, start + self.maxchanges)
|
|
590
|
|
|
590
|
|
|
591
|
yield self.t("summary",
|
|
591
|
yield self.t("summary",
|
|
592
|
desc = self.config("web", "description", "unknown"),
|
|
592
|
desc = self.config("web", "description", "unknown"),
|
|
593
|
owner = (self.config("ui", "username") or # preferred
|
|
593
|
owner = (self.config("ui", "username") or # preferred
|
|
594
|
self.config("web", "contact") or # deprecated
|
|
594
|
self.config("web", "contact") or # deprecated
|
|
595
|
self.config("web", "author", "unknown")), # also
|
|
595
|
self.config("web", "author", "unknown")), # also
|
|
596
|
lastchange = cl.read(cl.tip())[2],
|
|
596
|
lastchange = cl.read(cl.tip())[2],
|
|
597
|
tags = tagentries,
|
|
597
|
tags = tagentries,
|
|
598
|
heads = heads,
|
|
598
|
heads = heads,
|
|
599
|
shortlog = changelist,
|
|
599
|
shortlog = changelist,
|
|
600
|
node = hex(cl.tip()),
|
|
600
|
node = hex(cl.tip()),
|
|
601
|
archives=self.archivelist("tip"))
|
|
601
|
archives=self.archivelist("tip"))
|
|
602
|
|
|
602
|
|
|
603
|
def filediff(self, fctx):
|
|
603
|
def filediff(self, fctx):
|
|
604
|
n = fctx.node()
|
|
604
|
n = fctx.node()
|
|
605
|
path = fctx.path()
|
|
605
|
path = fctx.path()
|
|
606
|
parents = fctx.parents()
|
|
606
|
parents = fctx.parents()
|
|
607
|
p1 = parents and parents[0].node() or nullid
|
|
607
|
p1 = parents and parents[0].node() or nullid
|
|
608
|
|
|
608
|
|
|
609
|
def diff(**map):
|
|
609
|
def diff(**map):
|
|
610
|
yield self.diff(p1, n, [path])
|
|
610
|
yield self.diff(p1, n, [path])
|
|
611
|
|
|
611
|
|
|
612
|
yield self.t("filediff",
|
|
612
|
yield self.t("filediff",
|
|
613
|
file=path,
|
|
613
|
file=path,
|
|
614
|
node=hex(n),
|
|
614
|
node=hex(n),
|
|
615
|
rev=fctx.rev(),
|
|
615
|
rev=fctx.rev(),
|
|
616
|
parent=self.siblings(parents),
|
|
616
|
parent=self.siblings(parents),
|
|
617
|
child=self.siblings(fctx.children()),
|
|
617
|
child=self.siblings(fctx.children()),
|
|
618
|
diff=diff)
|
|
618
|
diff=diff)
|
|
619
|
|
|
619
|
|
|
620
|
archive_specs = {
|
|
620
|
archive_specs = {
|
|
621
|
'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
|
|
621
|
'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
|
|
622
|
'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
|
|
622
|
'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
|
|
623
|
'zip': ('application/zip', 'zip', '.zip', None),
|
|
623
|
'zip': ('application/zip', 'zip', '.zip', None),
|
|
624
|
}
|
|
624
|
}
|
|
625
|
|
|
625
|
|
|
626
|
def archive(self, req, cnode, type_):
|
|
626
|
def archive(self, req, cnode, type_):
|
|
627
|
reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
|
|
627
|
reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
|
|
628
|
name = "%s-%s" % (reponame, short(cnode))
|
|
628
|
name = "%s-%s" % (reponame, short(cnode))
|
|
629
|
mimetype, artype, extension, encoding = self.archive_specs[type_]
|
|
629
|
mimetype, artype, extension, encoding = self.archive_specs[type_]
|
|
630
|
headers = [('Content-type', mimetype),
|
|
630
|
headers = [('Content-type', mimetype),
|
|
631
|
('Content-disposition', 'attachment; filename=%s%s' %
|
|
631
|
('Content-disposition', 'attachment; filename=%s%s' %
|
|
632
|
(name, extension))]
|
|
632
|
(name, extension))]
|
|
633
|
if encoding:
|
|
633
|
if encoding:
|
|
634
|
headers.append(('Content-encoding', encoding))
|
|
634
|
headers.append(('Content-encoding', encoding))
|
|
635
|
req.header(headers)
|
|
635
|
req.header(headers)
|
|
636
|
archival.archive(self.repo, req.out, cnode, artype, prefix=name)
|
|
636
|
archival.archive(self.repo, req.out, cnode, artype, prefix=name)
|
|
637
|
|
|
637
|
|
|
638
|
# add tags to things
|
|
638
|
# add tags to things
|
|
639
|
# tags -> list of changesets corresponding to tags
|
|
639
|
# tags -> list of changesets corresponding to tags
|
|
640
|
# find tag, changeset, file
|
|
640
|
# find tag, changeset, file
|
|
641
|
|
|
641
|
|
|
642
|
def cleanpath(self, path):
|
|
642
|
def cleanpath(self, path):
|
|
643
|
path = path.lstrip('/')
|
|
643
|
path = path.lstrip('/')
|
|
644
|
return util.canonpath(self.repo.root, '', path)
|
|
644
|
return util.canonpath(self.repo.root, '', path)
|
|
645
|
|
|
645
|
|
|
646
|
def run(self):
|
|
646
|
def run(self):
|
|
647
|
if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
|
|
647
|
if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
|
|
648
|
raise RuntimeError("This function is only intended to be called while running as a CGI script.")
|
|
648
|
raise RuntimeError("This function is only intended to be called while running as a CGI script.")
|
|
649
|
import mercurial.hgweb.wsgicgi as wsgicgi
|
|
649
|
import mercurial.hgweb.wsgicgi as wsgicgi
|
|
650
|
from request import wsgiapplication
|
|
650
|
from request import wsgiapplication
|
|
651
|
def make_web_app():
|
|
651
|
def make_web_app():
|
|
652
|
return self
|
|
652
|
return self
|
|
653
|
wsgicgi.launch(wsgiapplication(make_web_app))
|
|
653
|
wsgicgi.launch(wsgiapplication(make_web_app))
|
|
654
|
|
|
654
|
|
|
655
|
def run_wsgi(self, req):
|
|
655
|
def run_wsgi(self, req):
|
|
656
|
def header(**map):
|
|
656
|
def header(**map):
|
|
657
|
header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
|
|
657
|
header_file = cStringIO.StringIO(
|
|
|
|
|
658
|
''.join(self.t("header", encoding = util._encoding, **map)))
|
|
658
|
msg = mimetools.Message(header_file, 0)
|
|
659
|
msg = mimetools.Message(header_file, 0)
|
|
659
|
req.header(msg.items())
|
|
660
|
req.header(msg.items())
|
|
660
|
yield header_file.read()
|
|
661
|
yield header_file.read()
|
|
661
|
|
|
662
|
|
|
662
|
def rawfileheader(**map):
|
|
663
|
def rawfileheader(**map):
|
|
663
|
req.header([('Content-type', map['mimetype']),
|
|
664
|
req.header([('Content-type', map['mimetype']),
|
|
664
|
('Content-disposition', 'filename=%s' % map['file']),
|
|
665
|
('Content-disposition', 'filename=%s' % map['file']),
|
|
665
|
('Content-length', str(len(map['raw'])))])
|
|
666
|
('Content-length', str(len(map['raw'])))])
|
|
666
|
yield ''
|
|
667
|
yield ''
|
|
667
|
|
|
668
|
|
|
668
|
def footer(**map):
|
|
669
|
def footer(**map):
|
|
669
|
yield self.t("footer", **map)
|
|
670
|
yield self.t("footer", **map)
|
|
670
|
|
|
671
|
|
|
671
|
def motd(**map):
|
|
672
|
def motd(**map):
|
|
672
|
yield self.config("web", "motd", "")
|
|
673
|
yield self.config("web", "motd", "")
|
|
673
|
|
|
674
|
|
|
674
|
def expand_form(form):
|
|
675
|
def expand_form(form):
|
|
675
|
shortcuts = {
|
|
676
|
shortcuts = {
|
|
676
|
'cl': [('cmd', ['changelog']), ('rev', None)],
|
|
677
|
'cl': [('cmd', ['changelog']), ('rev', None)],
|
|
677
|
'sl': [('cmd', ['shortlog']), ('rev', None)],
|
|
678
|
'sl': [('cmd', ['shortlog']), ('rev', None)],
|
|
678
|
'cs': [('cmd', ['changeset']), ('node', None)],
|
|
679
|
'cs': [('cmd', ['changeset']), ('node', None)],
|
|
679
|
'f': [('cmd', ['file']), ('filenode', None)],
|
|
680
|
'f': [('cmd', ['file']), ('filenode', None)],
|
|
680
|
'fl': [('cmd', ['filelog']), ('filenode', None)],
|
|
681
|
'fl': [('cmd', ['filelog']), ('filenode', None)],
|
|
681
|
'fd': [('cmd', ['filediff']), ('node', None)],
|
|
682
|
'fd': [('cmd', ['filediff']), ('node', None)],
|
|
682
|
'fa': [('cmd', ['annotate']), ('filenode', None)],
|
|
683
|
'fa': [('cmd', ['annotate']), ('filenode', None)],
|
|
683
|
'mf': [('cmd', ['manifest']), ('manifest', None)],
|
|
684
|
'mf': [('cmd', ['manifest']), ('manifest', None)],
|
|
684
|
'ca': [('cmd', ['archive']), ('node', None)],
|
|
685
|
'ca': [('cmd', ['archive']), ('node', None)],
|
|
685
|
'tags': [('cmd', ['tags'])],
|
|
686
|
'tags': [('cmd', ['tags'])],
|
|
686
|
'tip': [('cmd', ['changeset']), ('node', ['tip'])],
|
|
687
|
'tip': [('cmd', ['changeset']), ('node', ['tip'])],
|
|
687
|
'static': [('cmd', ['static']), ('file', None)]
|
|
688
|
'static': [('cmd', ['static']), ('file', None)]
|
|
688
|
}
|
|
689
|
}
|
|
689
|
|
|
690
|
|
|
690
|
for k in shortcuts.iterkeys():
|
|
691
|
for k in shortcuts.iterkeys():
|
|
691
|
if form.has_key(k):
|
|
692
|
if form.has_key(k):
|
|
692
|
for name, value in shortcuts[k]:
|
|
693
|
for name, value in shortcuts[k]:
|
|
693
|
if value is None:
|
|
694
|
if value is None:
|
|
694
|
value = form[k]
|
|
695
|
value = form[k]
|
|
695
|
form[name] = value
|
|
696
|
form[name] = value
|
|
696
|
del form[k]
|
|
697
|
del form[k]
|
|
697
|
|
|
698
|
|
|
698
|
def rewrite_request(req):
|
|
699
|
def rewrite_request(req):
|
|
699
|
'''translate new web interface to traditional format'''
|
|
700
|
'''translate new web interface to traditional format'''
|
|
700
|
|
|
701
|
|
|
701
|
def spliturl(req):
|
|
702
|
def spliturl(req):
|
|
702
|
def firstitem(query):
|
|
703
|
def firstitem(query):
|
|
703
|
return query.split('&', 1)[0].split(';', 1)[0]
|
|
704
|
return query.split('&', 1)[0].split(';', 1)[0]
|
|
704
|
|
|
705
|
|
|
705
|
def normurl(url):
|
|
706
|
def normurl(url):
|
|
706
|
inner = '/'.join([x for x in url.split('/') if x])
|
|
707
|
inner = '/'.join([x for x in url.split('/') if x])
|
|
707
|
tl = len(url) > 1 and url.endswith('/') and '/' or ''
|
|
708
|
tl = len(url) > 1 and url.endswith('/') and '/' or ''
|
|
708
|
|
|
709
|
|
|
709
|
return '%s%s%s' % (url.startswith('/') and '/' or '',
|
|
710
|
return '%s%s%s' % (url.startswith('/') and '/' or '',
|
|
710
|
inner, tl)
|
|
711
|
inner, tl)
|
|
711
|
|
|
712
|
|
|
712
|
root = normurl(urllib.unquote(req.env.get('REQUEST_URI', '').split('?', 1)[0]))
|
|
713
|
root = normurl(urllib.unquote(req.env.get('REQUEST_URI', '').split('?', 1)[0]))
|
|
713
|
pi = normurl(req.env.get('PATH_INFO', ''))
|
|
714
|
pi = normurl(req.env.get('PATH_INFO', ''))
|
|
714
|
if pi:
|
|
715
|
if pi:
|
|
715
|
# strip leading /
|
|
716
|
# strip leading /
|
|
716
|
pi = pi[1:]
|
|
717
|
pi = pi[1:]
|
|
717
|
if pi:
|
|
718
|
if pi:
|
|
718
|
root = root[:-len(pi)]
|
|
719
|
root = root[:-len(pi)]
|
|
719
|
if req.env.has_key('REPO_NAME'):
|
|
720
|
if req.env.has_key('REPO_NAME'):
|
|
720
|
rn = req.env['REPO_NAME'] + '/'
|
|
721
|
rn = req.env['REPO_NAME'] + '/'
|
|
721
|
root += rn
|
|
722
|
root += rn
|
|
722
|
query = pi[len(rn):]
|
|
723
|
query = pi[len(rn):]
|
|
723
|
else:
|
|
724
|
else:
|
|
724
|
query = pi
|
|
725
|
query = pi
|
|
725
|
else:
|
|
726
|
else:
|
|
726
|
root += '?'
|
|
727
|
root += '?'
|
|
727
|
query = firstitem(req.env['QUERY_STRING'])
|
|
728
|
query = firstitem(req.env['QUERY_STRING'])
|
|
728
|
|
|
729
|
|
|
729
|
return (root, query)
|
|
730
|
return (root, query)
|
|
730
|
|
|
731
|
|
|
731
|
req.url, query = spliturl(req)
|
|
732
|
req.url, query = spliturl(req)
|
|
732
|
|
|
733
|
|
|
733
|
if req.form.has_key('cmd'):
|
|
734
|
if req.form.has_key('cmd'):
|
|
734
|
# old style
|
|
735
|
# old style
|
|
735
|
return
|
|
736
|
return
|
|
736
|
|
|
737
|
|
|
737
|
args = query.split('/', 2)
|
|
738
|
args = query.split('/', 2)
|
|
738
|
if not args or not args[0]:
|
|
739
|
if not args or not args[0]:
|
|
739
|
return
|
|
740
|
return
|
|
740
|
|
|
741
|
|
|
741
|
cmd = args.pop(0)
|
|
742
|
cmd = args.pop(0)
|
|
742
|
style = cmd.rfind('-')
|
|
743
|
style = cmd.rfind('-')
|
|
743
|
if style != -1:
|
|
744
|
if style != -1:
|
|
744
|
req.form['style'] = [cmd[:style]]
|
|
745
|
req.form['style'] = [cmd[:style]]
|
|
745
|
cmd = cmd[style+1:]
|
|
746
|
cmd = cmd[style+1:]
|
|
746
|
# avoid accepting e.g. style parameter as command
|
|
747
|
# avoid accepting e.g. style parameter as command
|
|
747
|
if hasattr(self, 'do_' + cmd):
|
|
748
|
if hasattr(self, 'do_' + cmd):
|
|
748
|
req.form['cmd'] = [cmd]
|
|
749
|
req.form['cmd'] = [cmd]
|
|
749
|
|
|
750
|
|
|
750
|
if args and args[0]:
|
|
751
|
if args and args[0]:
|
|
751
|
node = args.pop(0)
|
|
752
|
node = args.pop(0)
|
|
752
|
req.form['node'] = [node]
|
|
753
|
req.form['node'] = [node]
|
|
753
|
if args:
|
|
754
|
if args:
|
|
754
|
req.form['file'] = args
|
|
755
|
req.form['file'] = args
|
|
755
|
|
|
756
|
|
|
756
|
if cmd == 'static':
|
|
757
|
if cmd == 'static':
|
|
757
|
req.form['file'] = req.form['node']
|
|
758
|
req.form['file'] = req.form['node']
|
|
758
|
elif cmd == 'archive':
|
|
759
|
elif cmd == 'archive':
|
|
759
|
fn = req.form['node'][0]
|
|
760
|
fn = req.form['node'][0]
|
|
760
|
for type_, spec in self.archive_specs.iteritems():
|
|
761
|
for type_, spec in self.archive_specs.iteritems():
|
|
761
|
ext = spec[2]
|
|
762
|
ext = spec[2]
|
|
762
|
if fn.endswith(ext):
|
|
763
|
if fn.endswith(ext):
|
|
763
|
req.form['node'] = [fn[:-len(ext)]]
|
|
764
|
req.form['node'] = [fn[:-len(ext)]]
|
|
764
|
req.form['type'] = [type_]
|
|
765
|
req.form['type'] = [type_]
|
|
765
|
|
|
766
|
|
|
766
|
def sessionvars(**map):
|
|
767
|
def sessionvars(**map):
|
|
767
|
fields = []
|
|
768
|
fields = []
|
|
768
|
if req.form.has_key('style'):
|
|
769
|
if req.form.has_key('style'):
|
|
769
|
style = req.form['style'][0]
|
|
770
|
style = req.form['style'][0]
|
|
770
|
if style != self.config('web', 'style', ''):
|
|
771
|
if style != self.config('web', 'style', ''):
|
|
771
|
fields.append(('style', style))
|
|
772
|
fields.append(('style', style))
|
|
772
|
|
|
773
|
|
|
773
|
separator = req.url[-1] == '?' and ';' or '?'
|
|
774
|
separator = req.url[-1] == '?' and ';' or '?'
|
|
774
|
for name, value in fields:
|
|
775
|
for name, value in fields:
|
|
775
|
yield dict(name=name, value=value, separator=separator)
|
|
776
|
yield dict(name=name, value=value, separator=separator)
|
|
776
|
separator = ';'
|
|
777
|
separator = ';'
|
|
777
|
|
|
778
|
|
|
778
|
self.refresh()
|
|
779
|
self.refresh()
|
|
779
|
|
|
780
|
|
|
780
|
expand_form(req.form)
|
|
781
|
expand_form(req.form)
|
|
781
|
rewrite_request(req)
|
|
782
|
rewrite_request(req)
|
|
782
|
|
|
783
|
|
|
783
|
style = self.config("web", "style", "")
|
|
784
|
style = self.config("web", "style", "")
|
|
784
|
if req.form.has_key('style'):
|
|
785
|
if req.form.has_key('style'):
|
|
785
|
style = req.form['style'][0]
|
|
786
|
style = req.form['style'][0]
|
|
786
|
mapfile = style_map(self.templatepath, style)
|
|
787
|
mapfile = style_map(self.templatepath, style)
|
|
787
|
|
|
788
|
|
|
788
|
port = req.env["SERVER_PORT"]
|
|
789
|
port = req.env["SERVER_PORT"]
|
|
789
|
port = port != "80" and (":" + port) or ""
|
|
790
|
port = port != "80" and (":" + port) or ""
|
|
790
|
urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port)
|
|
791
|
urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port)
|
|
791
|
|
|
792
|
|
|
792
|
if not self.reponame:
|
|
793
|
if not self.reponame:
|
|
793
|
self.reponame = (self.config("web", "name")
|
|
794
|
self.reponame = (self.config("web", "name")
|
|
794
|
or req.env.get('REPO_NAME')
|
|
795
|
or req.env.get('REPO_NAME')
|
|
795
|
or req.url.strip('/') or self.repo.root)
|
|
796
|
or req.url.strip('/') or self.repo.root)
|
|
796
|
|
|
797
|
|
|
797
|
self.t = templater.templater(mapfile, templater.common_filters,
|
|
798
|
self.t = templater.templater(mapfile, templater.common_filters,
|
|
798
|
defaults={"url": req.url,
|
|
799
|
defaults={"url": req.url,
|
|
799
|
"urlbase": urlbase,
|
|
800
|
"urlbase": urlbase,
|
|
800
|
"repo": self.reponame,
|
|
801
|
"repo": self.reponame,
|
|
801
|
"header": header,
|
|
802
|
"header": header,
|
|
802
|
"footer": footer,
|
|
803
|
"footer": footer,
|
|
803
|
"motd": motd,
|
|
804
|
"motd": motd,
|
|
804
|
"rawfileheader": rawfileheader,
|
|
805
|
"rawfileheader": rawfileheader,
|
|
805
|
"sessionvars": sessionvars
|
|
806
|
"sessionvars": sessionvars
|
|
806
|
})
|
|
807
|
})
|
|
807
|
|
|
808
|
|
|
808
|
if not req.form.has_key('cmd'):
|
|
809
|
if not req.form.has_key('cmd'):
|
|
809
|
req.form['cmd'] = [self.t.cache['default']]
|
|
810
|
req.form['cmd'] = [self.t.cache['default']]
|
|
810
|
|
|
811
|
|
|
811
|
cmd = req.form['cmd'][0]
|
|
812
|
cmd = req.form['cmd'][0]
|
|
812
|
|
|
813
|
|
|
813
|
method = getattr(self, 'do_' + cmd, None)
|
|
814
|
method = getattr(self, 'do_' + cmd, None)
|
|
814
|
if method:
|
|
815
|
if method:
|
|
815
|
try:
|
|
816
|
try:
|
|
816
|
method(req)
|
|
817
|
method(req)
|
|
817
|
except (hg.RepoError, revlog.RevlogError), inst:
|
|
818
|
except (hg.RepoError, revlog.RevlogError), inst:
|
|
818
|
req.write(self.t("error", error=str(inst)))
|
|
819
|
req.write(self.t("error", error=str(inst)))
|
|
819
|
else:
|
|
820
|
else:
|
|
820
|
req.write(self.t("error", error='No such method: ' + cmd))
|
|
821
|
req.write(self.t("error", error='No such method: ' + cmd))
|
|
821
|
|
|
822
|
|
|
822
|
def changectx(self, req):
|
|
823
|
def changectx(self, req):
|
|
823
|
if req.form.has_key('node'):
|
|
824
|
if req.form.has_key('node'):
|
|
824
|
changeid = req.form['node'][0]
|
|
825
|
changeid = req.form['node'][0]
|
|
825
|
elif req.form.has_key('manifest'):
|
|
826
|
elif req.form.has_key('manifest'):
|
|
826
|
changeid = req.form['manifest'][0]
|
|
827
|
changeid = req.form['manifest'][0]
|
|
827
|
else:
|
|
828
|
else:
|
|
828
|
changeid = self.repo.changelog.count() - 1
|
|
829
|
changeid = self.repo.changelog.count() - 1
|
|
829
|
|
|
830
|
|
|
830
|
try:
|
|
831
|
try:
|
|
831
|
ctx = self.repo.changectx(changeid)
|
|
832
|
ctx = self.repo.changectx(changeid)
|
|
832
|
except hg.RepoError:
|
|
833
|
except hg.RepoError:
|
|
833
|
man = self.repo.manifest
|
|
834
|
man = self.repo.manifest
|
|
834
|
mn = man.lookup(changeid)
|
|
835
|
mn = man.lookup(changeid)
|
|
835
|
ctx = self.repo.changectx(man.linkrev(mn))
|
|
836
|
ctx = self.repo.changectx(man.linkrev(mn))
|
|
836
|
|
|
837
|
|
|
837
|
return ctx
|
|
838
|
return ctx
|
|
838
|
|
|
839
|
|
|
839
|
def filectx(self, req):
|
|
840
|
def filectx(self, req):
|
|
840
|
path = self.cleanpath(req.form['file'][0])
|
|
841
|
path = self.cleanpath(req.form['file'][0])
|
|
841
|
if req.form.has_key('node'):
|
|
842
|
if req.form.has_key('node'):
|
|
842
|
changeid = req.form['node'][0]
|
|
843
|
changeid = req.form['node'][0]
|
|
843
|
else:
|
|
844
|
else:
|
|
844
|
changeid = req.form['filenode'][0]
|
|
845
|
changeid = req.form['filenode'][0]
|
|
845
|
try:
|
|
846
|
try:
|
|
846
|
ctx = self.repo.changectx(changeid)
|
|
847
|
ctx = self.repo.changectx(changeid)
|
|
847
|
fctx = ctx.filectx(path)
|
|
848
|
fctx = ctx.filectx(path)
|
|
848
|
except hg.RepoError:
|
|
849
|
except hg.RepoError:
|
|
849
|
fctx = self.repo.filectx(path, fileid=changeid)
|
|
850
|
fctx = self.repo.filectx(path, fileid=changeid)
|
|
850
|
|
|
851
|
|
|
851
|
return fctx
|
|
852
|
return fctx
|
|
852
|
|
|
853
|
|
|
853
|
def stripes(self, parity):
|
|
854
|
def stripes(self, parity):
|
|
854
|
"make horizontal stripes for easier reading"
|
|
855
|
"make horizontal stripes for easier reading"
|
|
855
|
if self.stripecount:
|
|
856
|
if self.stripecount:
|
|
856
|
return (1 + parity / self.stripecount) & 1
|
|
857
|
return (1 + parity / self.stripecount) & 1
|
|
857
|
else:
|
|
858
|
else:
|
|
858
|
return 0
|
|
859
|
return 0
|
|
859
|
|
|
860
|
|
|
860
|
def do_log(self, req):
|
|
861
|
def do_log(self, req):
|
|
861
|
if req.form.has_key('file') and req.form['file'][0]:
|
|
862
|
if req.form.has_key('file') and req.form['file'][0]:
|
|
862
|
self.do_filelog(req)
|
|
863
|
self.do_filelog(req)
|
|
863
|
else:
|
|
864
|
else:
|
|
864
|
self.do_changelog(req)
|
|
865
|
self.do_changelog(req)
|
|
865
|
|
|
866
|
|
|
866
|
def do_rev(self, req):
|
|
867
|
def do_rev(self, req):
|
|
867
|
self.do_changeset(req)
|
|
868
|
self.do_changeset(req)
|
|
868
|
|
|
869
|
|
|
869
|
def do_file(self, req):
|
|
870
|
def do_file(self, req):
|
|
870
|
path = self.cleanpath(req.form.get('file', [''])[0])
|
|
871
|
path = self.cleanpath(req.form.get('file', [''])[0])
|
|
871
|
if path:
|
|
872
|
if path:
|
|
872
|
try:
|
|
873
|
try:
|
|
873
|
req.write(self.filerevision(self.filectx(req)))
|
|
874
|
req.write(self.filerevision(self.filectx(req)))
|
|
874
|
return
|
|
875
|
return
|
|
875
|
except hg.RepoError:
|
|
876
|
except hg.RepoError:
|
|
876
|
pass
|
|
877
|
pass
|
|
877
|
|
|
878
|
|
|
878
|
req.write(self.manifest(self.changectx(req), path))
|
|
879
|
req.write(self.manifest(self.changectx(req), path))
|
|
879
|
|
|
880
|
|
|
880
|
def do_diff(self, req):
|
|
881
|
def do_diff(self, req):
|
|
881
|
self.do_filediff(req)
|
|
882
|
self.do_filediff(req)
|
|
882
|
|
|
883
|
|
|
883
|
def do_changelog(self, req, shortlog = False):
|
|
884
|
def do_changelog(self, req, shortlog = False):
|
|
884
|
if req.form.has_key('node'):
|
|
885
|
if req.form.has_key('node'):
|
|
885
|
ctx = self.changectx(req)
|
|
886
|
ctx = self.changectx(req)
|
|
886
|
else:
|
|
887
|
else:
|
|
887
|
if req.form.has_key('rev'):
|
|
888
|
if req.form.has_key('rev'):
|
|
888
|
hi = req.form['rev'][0]
|
|
889
|
hi = req.form['rev'][0]
|
|
889
|
else:
|
|
890
|
else:
|
|
890
|
hi = self.repo.changelog.count() - 1
|
|
891
|
hi = self.repo.changelog.count() - 1
|
|
891
|
try:
|
|
892
|
try:
|
|
892
|
ctx = self.repo.changectx(hi)
|
|
893
|
ctx = self.repo.changectx(hi)
|
|
893
|
except hg.RepoError:
|
|
894
|
except hg.RepoError:
|
|
894
|
req.write(self.search(hi)) # XXX redirect to 404 page?
|
|
895
|
req.write(self.search(hi)) # XXX redirect to 404 page?
|
|
895
|
return
|
|
896
|
return
|
|
896
|
|
|
897
|
|
|
897
|
req.write(self.changelog(ctx, shortlog = shortlog))
|
|
898
|
req.write(self.changelog(ctx, shortlog = shortlog))
|
|
898
|
|
|
899
|
|
|
899
|
def do_shortlog(self, req):
|
|
900
|
def do_shortlog(self, req):
|
|
900
|
self.do_changelog(req, shortlog = True)
|
|
901
|
self.do_changelog(req, shortlog = True)
|
|
901
|
|
|
902
|
|
|
902
|
def do_changeset(self, req):
|
|
903
|
def do_changeset(self, req):
|
|
903
|
req.write(self.changeset(self.changectx(req)))
|
|
904
|
req.write(self.changeset(self.changectx(req)))
|
|
904
|
|
|
905
|
|
|
905
|
def do_manifest(self, req):
|
|
906
|
def do_manifest(self, req):
|
|
906
|
req.write(self.manifest(self.changectx(req),
|
|
907
|
req.write(self.manifest(self.changectx(req),
|
|
907
|
self.cleanpath(req.form['path'][0])))
|
|
908
|
self.cleanpath(req.form['path'][0])))
|
|
908
|
|
|
909
|
|
|
909
|
def do_tags(self, req):
|
|
910
|
def do_tags(self, req):
|
|
910
|
req.write(self.tags())
|
|
911
|
req.write(self.tags())
|
|
911
|
|
|
912
|
|
|
912
|
def do_summary(self, req):
|
|
913
|
def do_summary(self, req):
|
|
913
|
req.write(self.summary())
|
|
914
|
req.write(self.summary())
|
|
914
|
|
|
915
|
|
|
915
|
def do_filediff(self, req):
|
|
916
|
def do_filediff(self, req):
|
|
916
|
req.write(self.filediff(self.filectx(req)))
|
|
917
|
req.write(self.filediff(self.filectx(req)))
|
|
917
|
|
|
918
|
|
|
918
|
def do_annotate(self, req):
|
|
919
|
def do_annotate(self, req):
|
|
919
|
req.write(self.fileannotate(self.filectx(req)))
|
|
920
|
req.write(self.fileannotate(self.filectx(req)))
|
|
920
|
|
|
921
|
|
|
921
|
def do_filelog(self, req):
|
|
922
|
def do_filelog(self, req):
|
|
922
|
req.write(self.filelog(self.filectx(req)))
|
|
923
|
req.write(self.filelog(self.filectx(req)))
|
|
923
|
|
|
924
|
|
|
924
|
def do_lookup(self, req):
|
|
925
|
def do_lookup(self, req):
|
|
925
|
try:
|
|
926
|
try:
|
|
926
|
r = hex(self.repo.lookup(req.form['key'][0]))
|
|
927
|
r = hex(self.repo.lookup(req.form['key'][0]))
|
|
927
|
success = 1
|
|
928
|
success = 1
|
|
928
|
except Exception,inst:
|
|
929
|
except Exception,inst:
|
|
929
|
r = str(inst)
|
|
930
|
r = str(inst)
|
|
930
|
success = 0
|
|
931
|
success = 0
|
|
931
|
resp = "%s %s\n" % (success, r)
|
|
932
|
resp = "%s %s\n" % (success, r)
|
|
932
|
req.httphdr("application/mercurial-0.1", length=len(resp))
|
|
933
|
req.httphdr("application/mercurial-0.1", length=len(resp))
|
|
933
|
req.write(resp)
|
|
934
|
req.write(resp)
|
|
934
|
|
|
935
|
|
|
935
|
def do_heads(self, req):
|
|
936
|
def do_heads(self, req):
|
|
936
|
resp = " ".join(map(hex, self.repo.heads())) + "\n"
|
|
937
|
resp = " ".join(map(hex, self.repo.heads())) + "\n"
|
|
937
|
req.httphdr("application/mercurial-0.1", length=len(resp))
|
|
938
|
req.httphdr("application/mercurial-0.1", length=len(resp))
|
|
938
|
req.write(resp)
|
|
939
|
req.write(resp)
|
|
939
|
|
|
940
|
|
|
940
|
def do_branches(self, req):
|
|
941
|
def do_branches(self, req):
|
|
941
|
nodes = []
|
|
942
|
nodes = []
|
|
942
|
if req.form.has_key('nodes'):
|
|
943
|
if req.form.has_key('nodes'):
|
|
943
|
nodes = map(bin, req.form['nodes'][0].split(" "))
|
|
944
|
nodes = map(bin, req.form['nodes'][0].split(" "))
|
|
944
|
resp = cStringIO.StringIO()
|
|
945
|
resp = cStringIO.StringIO()
|
|
945
|
for b in self.repo.branches(nodes):
|
|
946
|
for b in self.repo.branches(nodes):
|
|
946
|
resp.write(" ".join(map(hex, b)) + "\n")
|
|
947
|
resp.write(" ".join(map(hex, b)) + "\n")
|
|
947
|
resp = resp.getvalue()
|
|
948
|
resp = resp.getvalue()
|
|
948
|
req.httphdr("application/mercurial-0.1", length=len(resp))
|
|
949
|
req.httphdr("application/mercurial-0.1", length=len(resp))
|
|
949
|
req.write(resp)
|
|
950
|
req.write(resp)
|
|
950
|
|
|
951
|
|
|
951
|
def do_between(self, req):
|
|
952
|
def do_between(self, req):
|
|
952
|
if req.form.has_key('pairs'):
|
|
953
|
if req.form.has_key('pairs'):
|
|
953
|
pairs = [map(bin, p.split("-"))
|
|
954
|
pairs = [map(bin, p.split("-"))
|
|
954
|
for p in req.form['pairs'][0].split(" ")]
|
|
955
|
for p in req.form['pairs'][0].split(" ")]
|
|
955
|
resp = cStringIO.StringIO()
|
|
956
|
resp = cStringIO.StringIO()
|
|
956
|
for b in self.repo.between(pairs):
|
|
957
|
for b in self.repo.between(pairs):
|
|
957
|
resp.write(" ".join(map(hex, b)) + "\n")
|
|
958
|
resp.write(" ".join(map(hex, b)) + "\n")
|
|
958
|
resp = resp.getvalue()
|
|
959
|
resp = resp.getvalue()
|
|
959
|
req.httphdr("application/mercurial-0.1", length=len(resp))
|
|
960
|
req.httphdr("application/mercurial-0.1", length=len(resp))
|
|
960
|
req.write(resp)
|
|
961
|
req.write(resp)
|
|
961
|
|
|
962
|
|
|
962
|
def do_changegroup(self, req):
|
|
963
|
def do_changegroup(self, req):
|
|
963
|
req.httphdr("application/mercurial-0.1")
|
|
964
|
req.httphdr("application/mercurial-0.1")
|
|
964
|
nodes = []
|
|
965
|
nodes = []
|
|
965
|
if not self.allowpull:
|
|
966
|
if not self.allowpull:
|
|
966
|
return
|
|
967
|
return
|
|
967
|
|
|
968
|
|
|
968
|
if req.form.has_key('roots'):
|
|
969
|
if req.form.has_key('roots'):
|
|
969
|
nodes = map(bin, req.form['roots'][0].split(" "))
|
|
970
|
nodes = map(bin, req.form['roots'][0].split(" "))
|
|
970
|
|
|
971
|
|
|
971
|
z = zlib.compressobj()
|
|
972
|
z = zlib.compressobj()
|
|
972
|
f = self.repo.changegroup(nodes, 'serve')
|
|
973
|
f = self.repo.changegroup(nodes, 'serve')
|
|
973
|
while 1:
|
|
974
|
while 1:
|
|
974
|
chunk = f.read(4096)
|
|
975
|
chunk = f.read(4096)
|
|
975
|
if not chunk:
|
|
976
|
if not chunk:
|
|
976
|
break
|
|
977
|
break
|
|
977
|
req.write(z.compress(chunk))
|
|
978
|
req.write(z.compress(chunk))
|
|
978
|
|
|
979
|
|
|
979
|
req.write(z.flush())
|
|
980
|
req.write(z.flush())
|
|
980
|
|
|
981
|
|
|
981
|
def do_changegroupsubset(self, req):
|
|
982
|
def do_changegroupsubset(self, req):
|
|
982
|
req.httphdr("application/mercurial-0.1")
|
|
983
|
req.httphdr("application/mercurial-0.1")
|
|
983
|
bases = []
|
|
984
|
bases = []
|
|
984
|
heads = []
|
|
985
|
heads = []
|
|
985
|
if not self.allowpull:
|
|
986
|
if not self.allowpull:
|
|
986
|
return
|
|
987
|
return
|
|
987
|
|
|
988
|
|
|
988
|
if req.form.has_key('bases'):
|
|
989
|
if req.form.has_key('bases'):
|
|
989
|
bases = [bin(x) for x in req.form['bases'][0].split(' ')]
|
|
990
|
bases = [bin(x) for x in req.form['bases'][0].split(' ')]
|
|
990
|
if req.form.has_key('heads'):
|
|
991
|
if req.form.has_key('heads'):
|
|
991
|
heads = [bin(x) for x in req.form['heads'][0].split(' ')]
|
|
992
|
heads = [bin(x) for x in req.form['heads'][0].split(' ')]
|
|
992
|
|
|
993
|
|
|
993
|
z = zlib.compressobj()
|
|
994
|
z = zlib.compressobj()
|
|
994
|
f = self.repo.changegroupsubset(bases, heads, 'serve')
|
|
995
|
f = self.repo.changegroupsubset(bases, heads, 'serve')
|
|
995
|
while 1:
|
|
996
|
while 1:
|
|
996
|
chunk = f.read(4096)
|
|
997
|
chunk = f.read(4096)
|
|
997
|
if not chunk:
|
|
998
|
if not chunk:
|
|
998
|
break
|
|
999
|
break
|
|
999
|
req.write(z.compress(chunk))
|
|
1000
|
req.write(z.compress(chunk))
|
|
1000
|
|
|
1001
|
|
|
1001
|
req.write(z.flush())
|
|
1002
|
req.write(z.flush())
|
|
1002
|
|
|
1003
|
|
|
1003
|
def do_archive(self, req):
|
|
1004
|
def do_archive(self, req):
|
|
1004
|
changeset = self.repo.lookup(req.form['node'][0])
|
|
1005
|
changeset = self.repo.lookup(req.form['node'][0])
|
|
1005
|
type_ = req.form['type'][0]
|
|
1006
|
type_ = req.form['type'][0]
|
|
1006
|
allowed = self.configlist("web", "allow_archive")
|
|
1007
|
allowed = self.configlist("web", "allow_archive")
|
|
1007
|
if (type_ in self.archives and (type_ in allowed or
|
|
1008
|
if (type_ in self.archives and (type_ in allowed or
|
|
1008
|
self.configbool("web", "allow" + type_, False))):
|
|
1009
|
self.configbool("web", "allow" + type_, False))):
|
|
1009
|
self.archive(req, changeset, type_)
|
|
1010
|
self.archive(req, changeset, type_)
|
|
1010
|
return
|
|
1011
|
return
|
|
1011
|
|
|
1012
|
|
|
1012
|
req.write(self.t("error"))
|
|
1013
|
req.write(self.t("error"))
|
|
1013
|
|
|
1014
|
|
|
1014
|
def do_static(self, req):
|
|
1015
|
def do_static(self, req):
|
|
1015
|
fname = req.form['file'][0]
|
|
1016
|
fname = req.form['file'][0]
|
|
1016
|
# a repo owner may set web.static in .hg/hgrc to get any file
|
|
1017
|
# a repo owner may set web.static in .hg/hgrc to get any file
|
|
1017
|
# readable by the user running the CGI script
|
|
1018
|
# readable by the user running the CGI script
|
|
1018
|
static = self.config("web", "static",
|
|
1019
|
static = self.config("web", "static",
|
|
1019
|
os.path.join(self.templatepath, "static"),
|
|
1020
|
os.path.join(self.templatepath, "static"),
|
|
1020
|
untrusted=False)
|
|
1021
|
untrusted=False)
|
|
1021
|
req.write(staticfile(static, fname, req)
|
|
1022
|
req.write(staticfile(static, fname, req)
|
|
1022
|
or self.t("error", error="%r not found" % fname))
|
|
1023
|
or self.t("error", error="%r not found" % fname))
|
|
1023
|
|
|
1024
|
|
|
1024
|
def do_capabilities(self, req):
|
|
1025
|
def do_capabilities(self, req):
|
|
1025
|
caps = ['lookup', 'changegroupsubset']
|
|
1026
|
caps = ['lookup', 'changegroupsubset']
|
|
1026
|
if self.configbool('server', 'uncompressed'):
|
|
1027
|
if self.configbool('server', 'uncompressed'):
|
|
1027
|
caps.append('stream=%d' % self.repo.revlogversion)
|
|
1028
|
caps.append('stream=%d' % self.repo.revlogversion)
|
|
1028
|
# XXX: make configurable and/or share code with do_unbundle:
|
|
1029
|
# XXX: make configurable and/or share code with do_unbundle:
|
|
1029
|
unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
|
|
1030
|
unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
|
|
1030
|
if unbundleversions:
|
|
1031
|
if unbundleversions:
|
|
1031
|
caps.append('unbundle=%s' % ','.join(unbundleversions))
|
|
1032
|
caps.append('unbundle=%s' % ','.join(unbundleversions))
|
|
1032
|
resp = ' '.join(caps)
|
|
1033
|
resp = ' '.join(caps)
|
|
1033
|
req.httphdr("application/mercurial-0.1", length=len(resp))
|
|
1034
|
req.httphdr("application/mercurial-0.1", length=len(resp))
|
|
1034
|
req.write(resp)
|
|
1035
|
req.write(resp)
|
|
1035
|
|
|
1036
|
|
|
1036
|
def check_perm(self, req, op, default):
|
|
1037
|
def check_perm(self, req, op, default):
|
|
1037
|
'''check permission for operation based on user auth.
|
|
1038
|
'''check permission for operation based on user auth.
|
|
1038
|
return true if op allowed, else false.
|
|
1039
|
return true if op allowed, else false.
|
|
1039
|
default is policy to use if no config given.'''
|
|
1040
|
default is policy to use if no config given.'''
|
|
1040
|
|
|
1041
|
|
|
1041
|
user = req.env.get('REMOTE_USER')
|
|
1042
|
user = req.env.get('REMOTE_USER')
|
|
1042
|
|
|
1043
|
|
|
1043
|
deny = self.configlist('web', 'deny_' + op)
|
|
1044
|
deny = self.configlist('web', 'deny_' + op)
|
|
1044
|
if deny and (not user or deny == ['*'] or user in deny):
|
|
1045
|
if deny and (not user or deny == ['*'] or user in deny):
|
|
1045
|
return False
|
|
1046
|
return False
|
|
1046
|
|
|
1047
|
|
|
1047
|
allow = self.configlist('web', 'allow_' + op)
|
|
1048
|
allow = self.configlist('web', 'allow_' + op)
|
|
1048
|
return (allow and (allow == ['*'] or user in allow)) or default
|
|
1049
|
return (allow and (allow == ['*'] or user in allow)) or default
|
|
1049
|
|
|
1050
|
|
|
1050
|
def do_unbundle(self, req):
|
|
1051
|
def do_unbundle(self, req):
|
|
1051
|
def bail(response, headers={}):
|
|
1052
|
def bail(response, headers={}):
|
|
1052
|
length = int(req.env['CONTENT_LENGTH'])
|
|
1053
|
length = int(req.env['CONTENT_LENGTH'])
|
|
1053
|
for s in util.filechunkiter(req, limit=length):
|
|
1054
|
for s in util.filechunkiter(req, limit=length):
|
|
1054
|
# drain incoming bundle, else client will not see
|
|
1055
|
# drain incoming bundle, else client will not see
|
|
1055
|
# response when run outside cgi script
|
|
1056
|
# response when run outside cgi script
|
|
1056
|
pass
|
|
1057
|
pass
|
|
1057
|
req.httphdr("application/mercurial-0.1", headers=headers)
|
|
1058
|
req.httphdr("application/mercurial-0.1", headers=headers)
|
|
1058
|
req.write('0\n')
|
|
1059
|
req.write('0\n')
|
|
1059
|
req.write(response)
|
|
1060
|
req.write(response)
|
|
1060
|
|
|
1061
|
|
|
1061
|
# require ssl by default, auth info cannot be sniffed and
|
|
1062
|
# require ssl by default, auth info cannot be sniffed and
|
|
1062
|
# replayed
|
|
1063
|
# replayed
|
|
1063
|
ssl_req = self.configbool('web', 'push_ssl', True)
|
|
1064
|
ssl_req = self.configbool('web', 'push_ssl', True)
|
|
1064
|
if ssl_req:
|
|
1065
|
if ssl_req:
|
|
1065
|
if not req.env.get('HTTPS'):
|
|
1066
|
if not req.env.get('HTTPS'):
|
|
1066
|
bail(_('ssl required\n'))
|
|
1067
|
bail(_('ssl required\n'))
|
|
1067
|
return
|
|
1068
|
return
|
|
1068
|
proto = 'https'
|
|
1069
|
proto = 'https'
|
|
1069
|
else:
|
|
1070
|
else:
|
|
1070
|
proto = 'http'
|
|
1071
|
proto = 'http'
|
|
1071
|
|
|
1072
|
|
|
1072
|
# do not allow push unless explicitly allowed
|
|
1073
|
# do not allow push unless explicitly allowed
|
|
1073
|
if not self.check_perm(req, 'push', False):
|
|
1074
|
if not self.check_perm(req, 'push', False):
|
|
1074
|
bail(_('push not authorized\n'),
|
|
1075
|
bail(_('push not authorized\n'),
|
|
1075
|
headers={'status': '401 Unauthorized'})
|
|
1076
|
headers={'status': '401 Unauthorized'})
|
|
1076
|
return
|
|
1077
|
return
|
|
1077
|
|
|
1078
|
|
|
1078
|
req.httphdr("application/mercurial-0.1")
|
|
1079
|
req.httphdr("application/mercurial-0.1")
|
|
1079
|
|
|
1080
|
|
|
1080
|
their_heads = req.form['heads'][0].split(' ')
|
|
1081
|
their_heads = req.form['heads'][0].split(' ')
|
|
1081
|
|
|
1082
|
|
|
1082
|
def check_heads():
|
|
1083
|
def check_heads():
|
|
1083
|
heads = map(hex, self.repo.heads())
|
|
1084
|
heads = map(hex, self.repo.heads())
|
|
1084
|
return their_heads == [hex('force')] or their_heads == heads
|
|
1085
|
return their_heads == [hex('force')] or their_heads == heads
|
|
1085
|
|
|
1086
|
|
|
1086
|
# fail early if possible
|
|
1087
|
# fail early if possible
|
|
1087
|
if not check_heads():
|
|
1088
|
if not check_heads():
|
|
1088
|
bail(_('unsynced changes\n'))
|
|
1089
|
bail(_('unsynced changes\n'))
|
|
1089
|
return
|
|
1090
|
return
|
|
1090
|
|
|
1091
|
|
|
1091
|
# do not lock repo until all changegroup data is
|
|
1092
|
# do not lock repo until all changegroup data is
|
|
1092
|
# streamed. save to temporary file.
|
|
1093
|
# streamed. save to temporary file.
|
|
1093
|
|
|
1094
|
|
|
1094
|
fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
|
|
1095
|
fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
|
|
1095
|
fp = os.fdopen(fd, 'wb+')
|
|
1096
|
fp = os.fdopen(fd, 'wb+')
|
|
1096
|
try:
|
|
1097
|
try:
|
|
1097
|
length = int(req.env['CONTENT_LENGTH'])
|
|
1098
|
length = int(req.env['CONTENT_LENGTH'])
|
|
1098
|
for s in util.filechunkiter(req, limit=length):
|
|
1099
|
for s in util.filechunkiter(req, limit=length):
|
|
1099
|
fp.write(s)
|
|
1100
|
fp.write(s)
|
|
1100
|
|
|
1101
|
|
|
1101
|
lock = self.repo.lock()
|
|
1102
|
lock = self.repo.lock()
|
|
1102
|
try:
|
|
1103
|
try:
|
|
1103
|
if not check_heads():
|
|
1104
|
if not check_heads():
|
|
1104
|
req.write('0\n')
|
|
1105
|
req.write('0\n')
|
|
1105
|
req.write(_('unsynced changes\n'))
|
|
1106
|
req.write(_('unsynced changes\n'))
|
|
1106
|
return
|
|
1107
|
return
|
|
1107
|
|
|
1108
|
|
|
1108
|
fp.seek(0)
|
|
1109
|
fp.seek(0)
|
|
1109
|
header = fp.read(6)
|
|
1110
|
header = fp.read(6)
|
|
1110
|
if not header.startswith("HG"):
|
|
1111
|
if not header.startswith("HG"):
|
|
1111
|
# old client with uncompressed bundle
|
|
1112
|
# old client with uncompressed bundle
|
|
1112
|
def generator(f):
|
|
1113
|
def generator(f):
|
|
1113
|
yield header
|
|
1114
|
yield header
|
|
1114
|
for chunk in f:
|
|
1115
|
for chunk in f:
|
|
1115
|
yield chunk
|
|
1116
|
yield chunk
|
|
1116
|
elif not header.startswith("HG10"):
|
|
1117
|
elif not header.startswith("HG10"):
|
|
1117
|
req.write("0\n")
|
|
1118
|
req.write("0\n")
|
|
1118
|
req.write(_("unknown bundle version\n"))
|
|
1119
|
req.write(_("unknown bundle version\n"))
|
|
1119
|
return
|
|
1120
|
return
|
|
1120
|
elif header == "HG10GZ":
|
|
1121
|
elif header == "HG10GZ":
|
|
1121
|
def generator(f):
|
|
1122
|
def generator(f):
|
|
1122
|
zd = zlib.decompressobj()
|
|
1123
|
zd = zlib.decompressobj()
|
|
1123
|
for chunk in f:
|
|
1124
|
for chunk in f:
|
|
1124
|
yield zd.decompress(chunk)
|
|
1125
|
yield zd.decompress(chunk)
|
|
1125
|
elif header == "HG10BZ":
|
|
1126
|
elif header == "HG10BZ":
|
|
1126
|
def generator(f):
|
|
1127
|
def generator(f):
|
|
1127
|
zd = bz2.BZ2Decompressor()
|
|
1128
|
zd = bz2.BZ2Decompressor()
|
|
1128
|
zd.decompress("BZ")
|
|
1129
|
zd.decompress("BZ")
|
|
1129
|
for chunk in f:
|
|
1130
|
for chunk in f:
|
|
1130
|
yield zd.decompress(chunk)
|
|
1131
|
yield zd.decompress(chunk)
|
|
1131
|
elif header == "HG10UN":
|
|
1132
|
elif header == "HG10UN":
|
|
1132
|
def generator(f):
|
|
1133
|
def generator(f):
|
|
1133
|
for chunk in f:
|
|
1134
|
for chunk in f:
|
|
1134
|
yield chunk
|
|
1135
|
yield chunk
|
|
1135
|
else:
|
|
1136
|
else:
|
|
1136
|
req.write("0\n")
|
|
1137
|
req.write("0\n")
|
|
1137
|
req.write(_("unknown bundle compression type\n"))
|
|
1138
|
req.write(_("unknown bundle compression type\n"))
|
|
1138
|
return
|
|
1139
|
return
|
|
1139
|
gen = generator(util.filechunkiter(fp, 4096))
|
|
1140
|
gen = generator(util.filechunkiter(fp, 4096))
|
|
1140
|
|
|
1141
|
|
|
1141
|
# send addchangegroup output to client
|
|
1142
|
# send addchangegroup output to client
|
|
1142
|
|
|
1143
|
|
|
1143
|
old_stdout = sys.stdout
|
|
1144
|
old_stdout = sys.stdout
|
|
1144
|
sys.stdout = cStringIO.StringIO()
|
|
1145
|
sys.stdout = cStringIO.StringIO()
|
|
1145
|
|
|
1146
|
|
|
1146
|
try:
|
|
1147
|
try:
|
|
1147
|
url = 'remote:%s:%s' % (proto,
|
|
1148
|
url = 'remote:%s:%s' % (proto,
|
|
1148
|
req.env.get('REMOTE_HOST', ''))
|
|
1149
|
req.env.get('REMOTE_HOST', ''))
|
|
1149
|
ret = self.repo.addchangegroup(util.chunkbuffer(gen),
|
|
1150
|
ret = self.repo.addchangegroup(util.chunkbuffer(gen),
|
|
1150
|
'serve', url)
|
|
1151
|
'serve', url)
|
|
1151
|
finally:
|
|
1152
|
finally:
|
|
1152
|
val = sys.stdout.getvalue()
|
|
1153
|
val = sys.stdout.getvalue()
|
|
1153
|
sys.stdout = old_stdout
|
|
1154
|
sys.stdout = old_stdout
|
|
1154
|
req.write('%d\n' % ret)
|
|
1155
|
req.write('%d\n' % ret)
|
|
1155
|
req.write(val)
|
|
1156
|
req.write(val)
|
|
1156
|
finally:
|
|
1157
|
finally:
|
|
1157
|
lock.release()
|
|
1158
|
lock.release()
|
|
1158
|
finally:
|
|
1159
|
finally:
|
|
1159
|
fp.close()
|
|
1160
|
fp.close()
|
|
1160
|
os.unlink(tempname)
|
|
1161
|
os.unlink(tempname)
|
|
1161
|
|
|
1162
|
|
|
1162
|
def do_stream_out(self, req):
|
|
1163
|
def do_stream_out(self, req):
|
|
1163
|
req.httphdr("application/mercurial-0.1")
|
|
1164
|
req.httphdr("application/mercurial-0.1")
|
|
1164
|
streamclone.stream_out(self.repo, req)
|
|
1165
|
streamclone.stream_out(self.repo, req)
|