##// END OF EJS Templates
define standard name for base url to use when printing hgweb urls....
Vadim Gelfer -
r2197:5de8b44f default
parent child Browse files
Show More
@@ -1,340 +1,344 b''
1 1 HGRC(5)
2 2 =======
3 3 Bryan O'Sullivan <bos@serpentine.com>
4 4
5 5 NAME
6 6 ----
7 7 hgrc - configuration files for Mercurial
8 8
9 9 SYNOPSIS
10 10 --------
11 11
12 12 The Mercurial system uses a set of configuration files to control
13 13 aspects of its behaviour.
14 14
15 15 FILES
16 16 -----
17 17
18 18 Mercurial reads configuration data from several files, if they exist.
19 19 The names of these files depend on the system on which Mercurial is
20 20 installed.
21 21
22 22 (Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
23 23 (Unix) <install-root>/etc/mercurial/hgrc::
24 24 Per-installation configuration files, searched for in the
25 25 directory where Mercurial is installed. For example, if installed
26 26 in /shared/tools, Mercurial will look in
27 27 /shared/tools/etc/mercurial/hgrc. Options in these files apply to
28 28 all Mercurial commands executed by any user in any directory.
29 29
30 30 (Unix) /etc/mercurial/hgrc.d/*.rc::
31 31 (Unix) /etc/mercurial/hgrc::
32 32 (Windows) C:\Mercurial\Mercurial.ini::
33 33 Per-system configuration files, for the system on which Mercurial
34 34 is running. Options in these files apply to all Mercurial
35 35 commands executed by any user in any directory. Options in these
36 36 files override per-installation options.
37 37
38 38 (Unix) $HOME/.hgrc::
39 39 (Windows) C:\Documents and Settings\USERNAME\Mercurial.ini
40 40 Per-user configuration file, for the user running Mercurial.
41 41 Options in this file apply to all Mercurial commands executed by
42 42 any user in any directory. Options in this file override
43 43 per-installation and per-system options.
44 44
45 45 (Unix, Windows) <repo>/.hg/hgrc::
46 46 Per-repository configuration options that only apply in a
47 47 particular repository. This file is not version-controlled, and
48 48 will not get transferred during a "clone" operation. Options in
49 49 this file override options in all other configuration files.
50 50
51 51 SYNTAX
52 52 ------
53 53
54 54 A configuration file consists of sections, led by a "[section]" header
55 55 and followed by "name: value" entries; "name=value" is also accepted.
56 56
57 57 [spam]
58 58 eggs=ham
59 59 green=
60 60 eggs
61 61
62 62 Each line contains one entry. If the lines that follow are indented,
63 63 they are treated as continuations of that entry.
64 64
65 65 Leading whitespace is removed from values. Empty lines are skipped.
66 66
67 67 The optional values can contain format strings which refer to other
68 68 values in the same section, or values in a special DEFAULT section.
69 69
70 70 Lines beginning with "#" or ";" are ignored and may be used to provide
71 71 comments.
72 72
73 73 SECTIONS
74 74 --------
75 75
76 76 This section describes the different sections that may appear in a
77 77 Mercurial "hgrc" file, the purpose of each section, its possible
78 78 keys, and their possible values.
79 79
80 80 decode/encode::
81 81 Filters for transforming files on checkout/checkin. This would
82 82 typically be used for newline processing or other
83 83 localization/canonicalization of files.
84 84
85 85 Filters consist of a filter pattern followed by a filter command.
86 86 Filter patterns are globs by default, rooted at the repository
87 87 root. For example, to match any file ending in ".txt" in the root
88 88 directory only, use the pattern "*.txt". To match any file ending
89 89 in ".c" anywhere in the repository, use the pattern "**.c".
90 90
91 91 The filter command can start with a specifier, either "pipe:" or
92 92 "tempfile:". If no specifier is given, "pipe:" is used by default.
93 93
94 94 A "pipe:" command must accept data on stdin and return the
95 95 transformed data on stdout.
96 96
97 97 Pipe example:
98 98
99 99 [encode]
100 100 # uncompress gzip files on checkin to improve delta compression
101 101 # note: not necessarily a good idea, just an example
102 102 *.gz = pipe: gunzip
103 103
104 104 [decode]
105 105 # recompress gzip files when writing them to the working dir (we
106 106 # can safely omit "pipe:", because it's the default)
107 107 *.gz = gzip
108 108
109 109 A "tempfile:" command is a template. The string INFILE is replaced
110 110 with the name of a temporary file that contains the data to be
111 111 filtered by the command. The string OUTFILE is replaced with the
112 112 name of an empty temporary file, where the filtered data must be
113 113 written by the command.
114 114
115 115 NOTE: the tempfile mechanism is recommended for Windows systems,
116 116 where the standard shell I/O redirection operators often have
117 117 strange effects. In particular, if you are doing line ending
118 118 conversion on Windows using the popular dos2unix and unix2dos
119 119 programs, you *must* use the tempfile mechanism, as using pipes will
120 120 corrupt the contents of your files.
121 121
122 122 Tempfile example:
123 123
124 124 [encode]
125 125 # convert files to unix line ending conventions on checkin
126 126 **.txt = tempfile: dos2unix -n INFILE OUTFILE
127 127
128 128 [decode]
129 129 # convert files to windows line ending conventions when writing
130 130 # them to the working dir
131 131 **.txt = tempfile: unix2dos -n INFILE OUTFILE
132 132
133 133 hooks::
134 134 Commands or Python functions that get automatically executed by
135 135 various actions such as starting or finishing a commit. Multiple
136 136 hooks can be run for the same action by appending a suffix to the
137 137 action. Overriding a site-wide hook can be done by changing its
138 138 value or setting it to an empty string.
139 139
140 140 Example .hg/hgrc:
141 141
142 142 [hooks]
143 143 # do not use the site-wide hook
144 144 incoming =
145 145 incoming.email = /my/email/hook
146 146 incoming.autobuild = /my/build/hook
147 147
148 148 Most hooks are run with environment variables set that give added
149 149 useful information. For each hook below, the environment variables
150 150 it is passed are listed with names of the form "$HG_foo".
151 151
152 152 changegroup;;
153 153 Run after a changegroup has been added via push, pull or
154 154 unbundle. ID of the first new changeset is in $HG_NODE.
155 155 commit;;
156 156 Run after a changeset has been created in the local repository.
157 157 ID of the newly created changeset is in $HG_NODE. Parent
158 158 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
159 159 incoming;;
160 160 Run after a changeset has been pulled, pushed, or unbundled into
161 161 the local repository. The ID of the newly arrived changeset is in
162 162 $HG_NODE.
163 163 outgoing;;
164 164 Run after sending changes from local repository to another. ID of
165 165 first changeset sent is in $HG_NODE. Source of operation is in
166 166 $HG_SOURCE; see "preoutgoing" hook for description.
167 167 prechangegroup;;
168 168 Run before a changegroup is added via push, pull or unbundle.
169 169 Exit status 0 allows the changegroup to proceed. Non-zero status
170 170 will cause the push, pull or unbundle to fail.
171 171 precommit;;
172 172 Run before starting a local commit. Exit status 0 allows the
173 173 commit to proceed. Non-zero status will cause the commit to fail.
174 174 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
175 175 preoutgoing;;
176 176 Run before computing changes to send from the local repository to
177 177 another. Non-zero status will cause failure. This lets you
178 178 prevent pull over http or ssh. Also prevents against local pull,
179 179 push (outbound) or bundle commands, but not effective, since you
180 180 can just copy files instead then. Source of operation is in
181 181 $HG_SOURCE. If "serve", operation is happening on behalf of
182 182 remote ssh or http repository. If "push", "pull" or "bundle",
183 183 operation is happening on behalf of repository on same system.
184 184 pretag;;
185 185 Run before creating a tag. Exit status 0 allows the tag to be
186 186 created. Non-zero status will cause the tag to fail. ID of
187 187 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
188 188 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
189 189 pretxnchangegroup;;
190 190 Run after a changegroup has been added via push, pull or unbundle,
191 191 but before the transaction has been committed. Changegroup is
192 192 visible to hook program. This lets you validate incoming changes
193 193 before accepting them. Passed the ID of the first new changeset
194 194 in $HG_NODE. Exit status 0 allows the transaction to commit.
195 195 Non-zero status will cause the transaction to be rolled back and
196 196 the push, pull or unbundle will fail.
197 197 pretxncommit;;
198 198 Run after a changeset has been created but the transaction not yet
199 199 committed. Changeset is visible to hook program. This lets you
200 200 validate commit message and changes. Exit status 0 allows the
201 201 commit to proceed. Non-zero status will cause the transaction to
202 202 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
203 203 IDs are in $HG_PARENT1 and $HG_PARENT2.
204 204 tag;;
205 205 Run after a tag is created. ID of tagged changeset is in
206 206 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
207 207 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
208 208
209 209 In earlier releases, the names of hook environment variables did not
210 210 have a "HG_" prefix. These unprefixed names are still provided in
211 211 the environment for backwards compatibility, but their use is
212 212 deprecated, and they will be removed in a future release.
213 213
214 214 The syntax for Python hooks is as follows:
215 215
216 216 hookname = python:modulename.submodule.callable
217 217
218 218 Python hooks are run within the Mercurial process. Each hook is
219 219 called with at least three keyword arguments: a ui object (keyword
220 220 "ui"), a repository object (keyword "repo"), and a "hooktype"
221 221 keyword that tells what kind of hook is used. Arguments listed as
222 222 environment variables above are passed as keyword arguments, with no
223 223 "HG_" prefix, and names in lower case.
224 224
225 225 A Python hook must return a "true" value to succeed. Returning a
226 226 "false" value or raising an exception is treated as failure of the
227 227 hook.
228 228
229 229 http_proxy::
230 230 Used to access web-based Mercurial repositories through a HTTP
231 231 proxy.
232 232 host;;
233 233 Host name and (optional) port of the proxy server, for example
234 234 "myproxy:8000".
235 235 no;;
236 236 Optional. Comma-separated list of host names that should bypass
237 237 the proxy.
238 238 passwd;;
239 239 Optional. Password to authenticate with at the proxy server.
240 240 user;;
241 241 Optional. User name to authenticate with at the proxy server.
242 242
243 243 paths::
244 244 Assigns symbolic names to repositories. The left side is the
245 245 symbolic name, and the right gives the directory or URL that is the
246 246 location of the repository.
247 247
248 248 ui::
249 249 User interface controls.
250 250 debug;;
251 251 Print debugging information. True or False. Default is False.
252 252 editor;;
253 253 The editor to use during a commit. Default is $EDITOR or "vi".
254 254 ignore;;
255 255 A file to read per-user ignore patterns from. This file should be in
256 256 the same format as a repository-wide .hgignore file. This option
257 257 supports hook syntax, so if you want to specify multiple ignore
258 258 files, you can do so by setting something like
259 259 "ignore.other = ~/.hgignore2".
260 260 interactive;;
261 261 Allow to prompt the user. True or False. Default is True.
262 262 logtemplate;;
263 263 Template string for commands that print changesets.
264 264 style;;
265 265 Name of style to use for command output.
266 266 merge;;
267 267 The conflict resolution program to use during a manual merge.
268 268 Default is "hgmerge".
269 269 quiet;;
270 270 Reduce the amount of output printed. True or False. Default is False.
271 271 remotecmd;;
272 272 remote command to use for clone/push/pull operations. Default is 'hg'.
273 273 ssh;;
274 274 command to use for SSH connections. Default is 'ssh'.
275 275 timeout;;
276 276 The timeout used when a lock is held (in seconds), a negative value
277 277 means no timeout. Default is 600.
278 278 username;;
279 279 The committer of a changeset created when running "commit".
280 280 Typically a person's name and email address, e.g. "Fred Widget
281 281 <fred@example.com>". Default is $EMAIL or username@hostname, unless
282 282 username is set to an empty string, which enforces specifying the
283 283 username manually.
284 284 verbose;;
285 285 Increase the amount of output printed. True or False. Default is False.
286 286
287 287
288 288 web::
289 289 Web interface configuration.
290 290 accesslog;;
291 291 Where to output the access log. Default is stdout.
292 292 address;;
293 293 Interface address to bind to. Default is all.
294 294 allowbz2;;
295 295 Whether to allow .tar.bz2 downloading of repo revisions. Default is false.
296 296 allowgz;;
297 297 Whether to allow .tar.gz downloading of repo revisions. Default is false.
298 298 allowpull;;
299 299 Whether to allow pulling from the repository. Default is true.
300 300 allowzip;;
301 301 Whether to allow .zip downloading of repo revisions. Default is false.
302 302 This feature creates temporary files.
303 baseurl;;
304 Base URL to use when publishing URLs in other locations, so
305 third-party tools like email notification hooks can construct URLs.
306 Example: "http://hgserver/repos/"
303 307 description;;
304 308 Textual description of the repository's purpose or contents.
305 309 Default is "unknown".
306 310 errorlog;;
307 311 Where to output the error log. Default is stderr.
308 312 ipv6;;
309 313 Whether to use IPv6. Default is false.
310 314 name;;
311 315 Repository name to use in the web interface. Default is current
312 316 working directory.
313 317 maxchanges;;
314 318 Maximum number of changes to list on the changelog. Default is 10.
315 319 maxfiles;;
316 320 Maximum number of files to list per changeset. Default is 10.
317 321 port;;
318 322 Port to listen on. Default is 8000.
319 323 style;;
320 324 Which template map style to use.
321 325 templates;;
322 326 Where to find the HTML templates. Default is install path.
323 327
324 328
325 329 AUTHOR
326 330 ------
327 331 Bryan O'Sullivan <bos@serpentine.com>.
328 332
329 333 Mercurial was written by Matt Mackall <mpm@selenic.com>.
330 334
331 335 SEE ALSO
332 336 --------
333 337 hg(1)
334 338
335 339 COPYING
336 340 -------
337 341 This manual page is copyright 2005 Bryan O'Sullivan.
338 342 Mercurial is copyright 2005 Matt Mackall.
339 343 Free use of this software is granted under the terms of the GNU General
340 344 Public License (GPL).
@@ -1,293 +1,294 b''
1 1 # bugzilla.py - bugzilla integration for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7 #
8 8 # hook extension to update comments of bugzilla bugs when changesets
9 9 # that refer to bugs by id are seen. this hook does not change bug
10 10 # status, only comments.
11 11 #
12 12 # to configure, add items to '[bugzilla]' section of hgrc.
13 13 #
14 14 # to use, configure bugzilla extension and enable like this:
15 15 #
16 16 # [extensions]
17 17 # hgext.bugzilla =
18 18 #
19 19 # [hooks]
20 20 # # run bugzilla hook on every change pulled or pushed in here
21 21 # incoming.bugzilla = python:hgext.bugzilla.hook
22 22 #
23 23 # config items:
24 24 #
25 25 # REQUIRED:
26 26 # host = bugzilla # mysql server where bugzilla database lives
27 27 # password = ** # user's password
28 28 # version = 2.16 # version of bugzilla installed
29 29 #
30 30 # OPTIONAL:
31 31 # bzuser = ... # bugzilla user id to record comments with
32 32 # db = bugs # database to connect to
33 # hgweb = http:// # root of hg web site for browsing commits
34 33 # notify = ... # command to run to get bugzilla to send mail
35 34 # regexp = ... # regexp to match bug ids (must contain one "()" group)
36 35 # strip = 0 # number of slashes to strip for url paths
37 36 # style = ... # style file to use when formatting comments
38 37 # template = ... # template to use when formatting comments
39 38 # timeout = 5 # database connection timeout (seconds)
40 39 # user = bugs # user to connect to database as
40 # [web]
41 # baseurl = http://hgserver/... # root of hg web site for browsing commits
41 42
42 43 from mercurial.demandload import *
43 44 from mercurial.i18n import gettext as _
44 45 from mercurial.node import *
45 46 demandload(globals(), 'cStringIO mercurial:templater,util os re time')
46 47
47 48 try:
48 49 import MySQLdb
49 50 except ImportError:
50 51 raise util.Abort(_('python mysql support not available'))
51 52
52 53 def buglist(ids):
53 54 return '(' + ','.join(map(str, ids)) + ')'
54 55
55 56 class bugzilla_2_16(object):
56 57 '''support for bugzilla version 2.16.'''
57 58
58 59 def __init__(self, ui):
59 60 self.ui = ui
60 61 host = self.ui.config('bugzilla', 'host', 'localhost')
61 62 user = self.ui.config('bugzilla', 'user', 'bugs')
62 63 passwd = self.ui.config('bugzilla', 'password')
63 64 db = self.ui.config('bugzilla', 'db', 'bugs')
64 65 timeout = int(self.ui.config('bugzilla', 'timeout', 5))
65 66 self.ui.note(_('connecting to %s:%s as %s, password %s\n') %
66 67 (host, db, user, '*' * len(passwd)))
67 68 self.conn = MySQLdb.connect(host=host, user=user, passwd=passwd,
68 69 db=db, connect_timeout=timeout)
69 70 self.cursor = self.conn.cursor()
70 71 self.run('select fieldid from fielddefs where name = "longdesc"')
71 72 ids = self.cursor.fetchall()
72 73 if len(ids) != 1:
73 74 raise util.Abort(_('unknown database schema'))
74 75 self.longdesc_id = ids[0][0]
75 76 self.user_ids = {}
76 77
77 78 def run(self, *args, **kwargs):
78 79 '''run a query.'''
79 80 self.ui.note(_('query: %s %s\n') % (args, kwargs))
80 81 try:
81 82 self.cursor.execute(*args, **kwargs)
82 83 except MySQLdb.MySQLError, err:
83 84 self.ui.note(_('failed query: %s %s\n') % (args, kwargs))
84 85 raise
85 86
86 87 def filter_real_bug_ids(self, ids):
87 88 '''filter not-existing bug ids from list.'''
88 89 self.run('select bug_id from bugs where bug_id in %s' % buglist(ids))
89 90 ids = [c[0] for c in self.cursor.fetchall()]
90 91 ids.sort()
91 92 return ids
92 93
93 94 def filter_unknown_bug_ids(self, node, ids):
94 95 '''filter bug ids from list that already refer to this changeset.'''
95 96
96 97 self.run('''select bug_id from longdescs where
97 98 bug_id in %s and thetext like "%%%s%%"''' %
98 99 (buglist(ids), short(node)))
99 100 unknown = dict.fromkeys(ids)
100 101 for (id,) in self.cursor.fetchall():
101 102 self.ui.status(_('bug %d already knows about changeset %s\n') %
102 103 (id, short(node)))
103 104 unknown.pop(id, None)
104 105 ids = unknown.keys()
105 106 ids.sort()
106 107 return ids
107 108
108 109 def notify(self, ids):
109 110 '''tell bugzilla to send mail.'''
110 111
111 112 self.ui.status(_('telling bugzilla to send mail:\n'))
112 113 for id in ids:
113 114 self.ui.status(_(' bug %s\n') % id)
114 115 cmd = self.ui.config('bugzilla', 'notify',
115 116 'cd /var/www/html/bugzilla && '
116 117 './processmail %s nobody@nowhere.com') % id
117 118 fp = os.popen('(%s) 2>&1' % cmd)
118 119 out = fp.read()
119 120 ret = fp.close()
120 121 if ret:
121 122 self.ui.warn(out)
122 123 raise util.Abort(_('bugzilla notify command %s') %
123 124 util.explain_exit(ret)[0])
124 125 self.ui.status(_('done\n'))
125 126
126 127 def get_user_id(self, user):
127 128 '''look up numeric bugzilla user id.'''
128 129 try:
129 130 return self.user_ids[user]
130 131 except KeyError:
131 132 try:
132 133 userid = int(user)
133 134 except ValueError:
134 135 self.ui.note(_('looking up user %s\n') % user)
135 136 self.run('''select userid from profiles
136 137 where login_name like %s''', user)
137 138 all = self.cursor.fetchall()
138 139 if len(all) != 1:
139 140 raise KeyError(user)
140 141 userid = int(all[0][0])
141 142 self.user_ids[user] = userid
142 143 return userid
143 144
144 145 def add_comment(self, bugid, text, prefuser):
145 146 '''add comment to bug. try adding comment as committer of
146 147 changeset, otherwise as default bugzilla user.'''
147 148 try:
148 149 userid = self.get_user_id(prefuser)
149 150 except KeyError:
150 151 try:
151 152 defaultuser = self.ui.config('bugzilla', 'bzuser')
152 153 userid = self.get_user_id(defaultuser)
153 154 except KeyError:
154 155 raise util.Abort(_('cannot find user id for %s or %s') %
155 156 (prefuser, defaultuser))
156 157 now = time.strftime('%Y-%m-%d %H:%M:%S')
157 158 self.run('''insert into longdescs
158 159 (bug_id, who, bug_when, thetext)
159 160 values (%s, %s, %s, %s)''',
160 161 (bugid, userid, now, text))
161 162 self.run('''insert into bugs_activity (bug_id, who, bug_when, fieldid)
162 163 values (%s, %s, %s, %s)''',
163 164 (bugid, userid, now, self.longdesc_id))
164 165
165 166 class bugzilla(object):
166 167 # supported versions of bugzilla. different versions have
167 168 # different schemas.
168 169 _versions = {
169 170 '2.16': bugzilla_2_16,
170 171 }
171 172
172 173 _default_bug_re = (r'bugs?\s*,?\s*(?:#|nos?\.?|num(?:ber)?s?)?\s*'
173 174 r'((?:\d+\s*(?:,?\s*(?:and)?)?\s*)+)')
174 175
175 176 _bz = None
176 177
177 178 def __init__(self, ui, repo):
178 179 self.ui = ui
179 180 self.repo = repo
180 181
181 182 def bz(self):
182 183 '''return object that knows how to talk to bugzilla version in
183 184 use.'''
184 185
185 186 if bugzilla._bz is None:
186 187 bzversion = self.ui.config('bugzilla', 'version')
187 188 try:
188 189 bzclass = bugzilla._versions[bzversion]
189 190 except KeyError:
190 191 raise util.Abort(_('bugzilla version %s not supported') %
191 192 bzversion)
192 193 bugzilla._bz = bzclass(self.ui)
193 194 return bugzilla._bz
194 195
195 196 def __getattr__(self, key):
196 197 return getattr(self.bz(), key)
197 198
198 199 _bug_re = None
199 200 _split_re = None
200 201
201 202 def find_bug_ids(self, node, desc):
202 203 '''find valid bug ids that are referred to in changeset
203 204 comments and that do not already have references to this
204 205 changeset.'''
205 206
206 207 if bugzilla._bug_re is None:
207 208 bugzilla._bug_re = re.compile(
208 209 self.ui.config('bugzilla', 'regexp', bugzilla._default_bug_re),
209 210 re.IGNORECASE)
210 211 bugzilla._split_re = re.compile(r'\D+')
211 212 start = 0
212 213 ids = {}
213 214 while True:
214 215 m = bugzilla._bug_re.search(desc, start)
215 216 if not m:
216 217 break
217 218 start = m.end()
218 219 for id in bugzilla._split_re.split(m.group(1)):
219 220 ids[int(id)] = 1
220 221 ids = ids.keys()
221 222 if ids:
222 223 ids = self.filter_real_bug_ids(ids)
223 224 if ids:
224 225 ids = self.filter_unknown_bug_ids(node, ids)
225 226 return ids
226 227
227 228 def update(self, bugid, node, changes):
228 229 '''update bugzilla bug with reference to changeset.'''
229 230
230 231 def webroot(root):
231 232 '''strip leading prefix of repo root and turn into
232 233 url-safe path.'''
233 234 count = int(self.ui.config('bugzilla', 'strip', 0))
234 235 root = util.pconvert(root)
235 236 while count > 0:
236 237 c = root.find('/')
237 238 if c == -1:
238 239 break
239 240 root = root[c+1:]
240 241 count -= 1
241 242 return root
242 243
243 244 class stringio(object):
244 245 '''wrap cStringIO.'''
245 246 def __init__(self):
246 247 self.fp = cStringIO.StringIO()
247 248
248 249 def write(self, *args):
249 250 for a in args:
250 251 self.fp.write(a)
251 252
252 253 write_header = write
253 254
254 255 def getvalue(self):
255 256 return self.fp.getvalue()
256 257
257 258 mapfile = self.ui.config('bugzilla', 'style')
258 259 tmpl = self.ui.config('bugzilla', 'template')
259 260 sio = stringio()
260 261 t = templater.changeset_templater(self.ui, self.repo, mapfile, sio)
261 262 if not mapfile and not tmpl:
262 263 tmpl = _('changeset {node|short} in repo {root} refers '
263 264 'to bug {bug}.\ndetails:\n\t{desc|tabindent}')
264 265 if tmpl:
265 266 tmpl = templater.parsestring(tmpl, quoted=False)
266 267 t.use_template(tmpl)
267 268 t.show(changenode=node, changes=changes,
268 269 bug=str(bugid),
269 hgweb=self.ui.config('bugzilla', 'hgweb'),
270 hgweb=self.ui.config('web', 'baseurl'),
270 271 root=self.repo.root,
271 272 webroot=webroot(self.repo.root))
272 273 self.add_comment(bugid, sio.getvalue(), templater.email(changes[1]))
273 274
274 275 def hook(ui, repo, hooktype, node=None, **kwargs):
275 276 '''add comment to bugzilla for each changeset that refers to a
276 277 bugzilla bug id. only add a comment once per bug, so same change
277 278 seen multiple times does not fill bug with duplicate data.'''
278 279 if node is None:
279 280 raise util.Abort(_('hook type %s does not pass a changeset id') %
280 281 hooktype)
281 282 try:
282 283 bz = bugzilla(ui, repo)
283 284 bin_node = bin(node)
284 285 changes = repo.changelog.read(bin_node)
285 286 ids = bz.find_bug_ids(bin_node, changes[4])
286 287 if ids:
287 288 for id in ids:
288 289 bz.update(id, bin_node, changes)
289 290 bz.notify(ids)
290 291 return True
291 292 except MySQLdb.MySQLError, err:
292 293 raise util.Abort(_('database error: %s') % err[1])
293 294
General Comments 0
You need to be logged in to leave comments. Login now