##// END OF EJS Templates
use repr() instead of backticks
Benoit Boissinot -
r7470:1d58c049 default
parent child Browse files
Show More
@@ -1,196 +1,196 b''
1 1 # changelog.py - changelog class for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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 from node import bin, hex, nullid
9 9 from revlog import revlog, RevlogError
10 10 from i18n import _
11 11 import util
12 12
13 13 def _string_escape(text):
14 14 """
15 15 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
16 16 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
17 17 >>> s
18 18 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
19 19 >>> res = _string_escape(s)
20 20 >>> s == res.decode('string_escape')
21 21 True
22 22 """
23 23 # subset of the string_escape codec
24 24 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
25 25 return text.replace('\0', '\\0')
26 26
27 27 class appender:
28 28 '''the changelog index must be update last on disk, so we use this class
29 29 to delay writes to it'''
30 30 def __init__(self, fp, buf):
31 31 self.data = buf
32 32 self.fp = fp
33 33 self.offset = fp.tell()
34 34 self.size = util.fstat(fp).st_size
35 35
36 36 def end(self):
37 37 return self.size + len("".join(self.data))
38 38 def tell(self):
39 39 return self.offset
40 40 def flush(self):
41 41 pass
42 42 def close(self):
43 43 self.fp.close()
44 44
45 45 def seek(self, offset, whence=0):
46 46 '''virtual file offset spans real file and data'''
47 47 if whence == 0:
48 48 self.offset = offset
49 49 elif whence == 1:
50 50 self.offset += offset
51 51 elif whence == 2:
52 52 self.offset = self.end() + offset
53 53 if self.offset < self.size:
54 54 self.fp.seek(self.offset)
55 55
56 56 def read(self, count=-1):
57 57 '''only trick here is reads that span real file and data'''
58 58 ret = ""
59 59 if self.offset < self.size:
60 60 s = self.fp.read(count)
61 61 ret = s
62 62 self.offset += len(s)
63 63 if count > 0:
64 64 count -= len(s)
65 65 if count != 0:
66 66 doff = self.offset - self.size
67 67 self.data.insert(0, "".join(self.data))
68 68 del self.data[1:]
69 69 s = self.data[0][doff:doff+count]
70 70 self.offset += len(s)
71 71 ret += s
72 72 return ret
73 73
74 74 def write(self, s):
75 75 self.data.append(str(s))
76 76 self.offset += len(s)
77 77
78 78 class changelog(revlog):
79 79 def __init__(self, opener):
80 80 revlog.__init__(self, opener, "00changelog.i")
81 81
82 82 def delayupdate(self):
83 83 "delay visibility of index updates to other readers"
84 84 self._realopener = self.opener
85 85 self.opener = self._delayopener
86 86 self._delaycount = len(self)
87 87 self._delaybuf = []
88 88 self._delayname = None
89 89
90 90 def finalize(self, tr):
91 91 "finalize index updates"
92 92 self.opener = self._realopener
93 93 # move redirected index data back into place
94 94 if self._delayname:
95 95 util.rename(self._delayname + ".a", self._delayname)
96 96 elif self._delaybuf:
97 97 fp = self.opener(self.indexfile, 'a')
98 98 fp.write("".join(self._delaybuf))
99 99 fp.close()
100 100 del self._delaybuf
101 101 # split when we're done
102 102 self.checkinlinesize(tr)
103 103
104 104 def _delayopener(self, name, mode='r'):
105 105 fp = self._realopener(name, mode)
106 106 # only divert the index
107 107 if not name == self.indexfile:
108 108 return fp
109 109 # if we're doing an initial clone, divert to another file
110 110 if self._delaycount == 0:
111 111 self._delayname = fp.name
112 112 if not len(self):
113 113 # make sure to truncate the file
114 114 mode = mode.replace('a', 'w')
115 115 return self._realopener(name + ".a", mode)
116 116 # otherwise, divert to memory
117 117 return appender(fp, self._delaybuf)
118 118
119 119 def checkinlinesize(self, tr, fp=None):
120 120 if self.opener == self._delayopener:
121 121 return
122 122 return revlog.checkinlinesize(self, tr, fp)
123 123
124 124 def decode_extra(self, text):
125 125 extra = {}
126 126 for l in text.split('\0'):
127 127 if l:
128 128 k, v = l.decode('string_escape').split(':', 1)
129 129 extra[k] = v
130 130 return extra
131 131
132 132 def encode_extra(self, d):
133 133 # keys must be sorted to produce a deterministic changelog entry
134 134 items = [_string_escape('%s:%s' % (k, d[k])) for k in util.sort(d)]
135 135 return "\0".join(items)
136 136
137 137 def read(self, node):
138 138 """
139 139 format used:
140 140 nodeid\n : manifest node in ascii
141 141 user\n : user, no \n or \r allowed
142 142 time tz extra\n : date (time is int or float, timezone is int)
143 143 : extra is metadatas, encoded and separated by '\0'
144 144 : older versions ignore it
145 145 files\n\n : files modified by the cset, no \n or \r allowed
146 146 (.*) : comment (free text, ideally utf-8)
147 147
148 148 changelog v0 doesn't use extra
149 149 """
150 150 text = self.revision(node)
151 151 if not text:
152 152 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
153 153 last = text.index("\n\n")
154 154 desc = util.tolocal(text[last + 2:])
155 155 l = text[:last].split('\n')
156 156 manifest = bin(l[0])
157 157 user = util.tolocal(l[1])
158 158
159 159 extra_data = l[2].split(' ', 2)
160 160 if len(extra_data) != 3:
161 161 time = float(extra_data.pop(0))
162 162 try:
163 163 # various tools did silly things with the time zone field.
164 164 timezone = int(extra_data[0])
165 165 except:
166 166 timezone = 0
167 167 extra = {}
168 168 else:
169 169 time, timezone, extra = extra_data
170 170 time, timezone = float(time), int(timezone)
171 171 extra = self.decode_extra(extra)
172 172 if not extra.get('branch'):
173 173 extra['branch'] = 'default'
174 174 files = l[3:]
175 175 return (manifest, user, (time, timezone), files, desc, extra)
176 176
177 177 def add(self, manifest, files, desc, transaction, p1=None, p2=None,
178 178 user=None, date=None, extra={}):
179 179
180 180 user = user.strip()
181 181 if "\n" in user:
182 raise RevlogError(_("username %s contains a newline") % `user`)
182 raise RevlogError(_("username %s contains a newline") % repr(user))
183 183 user, desc = util.fromlocal(user), util.fromlocal(desc)
184 184
185 185 if date:
186 186 parseddate = "%d %d" % util.parsedate(date)
187 187 else:
188 188 parseddate = "%d %d" % util.makedate()
189 189 if extra and extra.get("branch") in ("default", ""):
190 190 del extra["branch"]
191 191 if extra:
192 192 extra = self.encode_extra(extra)
193 193 parseddate = "%s %s" % (parseddate, extra)
194 194 l = [hex(manifest), user, parseddate] + util.sort(files) + ["", desc]
195 195 text = "\n".join(l)
196 196 return self.addrevision(text, transaction, len(self), p1, p2)
@@ -1,486 +1,486 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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 from i18n import _
9 9 import errno, getpass, os, re, socket, sys, tempfile
10 10 import ConfigParser, traceback, util
11 11
12 12 def dupconfig(orig):
13 13 new = util.configparser(orig.defaults())
14 14 updateconfig(orig, new)
15 15 return new
16 16
17 17 def updateconfig(source, dest, sections=None):
18 18 if not sections:
19 19 sections = source.sections()
20 20 for section in sections:
21 21 if not dest.has_section(section):
22 22 dest.add_section(section)
23 23 for name, value in source.items(section, raw=True):
24 24 dest.set(section, name, value)
25 25
26 26 class ui(object):
27 27 _isatty = None
28 28
29 29 def __init__(self, verbose=False, debug=False, quiet=False,
30 30 interactive=True, traceback=False, report_untrusted=True,
31 31 parentui=None):
32 32 self.overlay = None
33 33 self.buffers = []
34 34 if parentui is None:
35 35 # this is the parent of all ui children
36 36 self.parentui = None
37 37 self.quiet = quiet
38 38 self.verbose = verbose
39 39 self.debugflag = debug
40 40 self.interactive = interactive
41 41 self.traceback = traceback
42 42 self.report_untrusted = report_untrusted
43 43 self.trusted_users = {}
44 44 self.trusted_groups = {}
45 45 # if ucdata is not None, its keys must be a superset of cdata's
46 46 self.cdata = util.configparser()
47 47 self.ucdata = None
48 48 # we always trust global config files
49 49 self.check_trusted = False
50 50 self.readconfig(util.rcpath())
51 51 self.check_trusted = True
52 52 self.updateopts(verbose, debug, quiet, interactive)
53 53 else:
54 54 # parentui may point to an ui object which is already a child
55 55 self.parentui = parentui.parentui or parentui
56 56 self.trusted_users = parentui.trusted_users.copy()
57 57 self.trusted_groups = parentui.trusted_groups.copy()
58 58 self.cdata = dupconfig(self.parentui.cdata)
59 59 if self.parentui.ucdata:
60 60 self.ucdata = dupconfig(self.parentui.ucdata)
61 61 if self.parentui.overlay:
62 62 self.overlay = dupconfig(self.parentui.overlay)
63 63 if self.parentui is not parentui and parentui.overlay is not None:
64 64 if self.overlay is None:
65 65 self.overlay = util.configparser()
66 66 updateconfig(parentui.overlay, self.overlay)
67 67 self.buffers = parentui.buffers
68 68
69 69 def __getattr__(self, key):
70 70 return getattr(self.parentui, key)
71 71
72 72 def isatty(self):
73 73 if ui._isatty is None:
74 74 ui._isatty = sys.stdin.isatty()
75 75 return ui._isatty
76 76
77 77 def updateopts(self, verbose=False, debug=False, quiet=False,
78 78 interactive=True, traceback=False, config=[]):
79 79 for section, name, value in config:
80 80 self.setconfig(section, name, value)
81 81
82 82 if quiet or verbose or debug:
83 83 self.setconfig('ui', 'quiet', str(bool(quiet)))
84 84 self.setconfig('ui', 'verbose', str(bool(verbose)))
85 85 self.setconfig('ui', 'debug', str(bool(debug)))
86 86
87 87 self.verbosity_constraints()
88 88
89 89 if not interactive:
90 90 self.setconfig('ui', 'interactive', 'False')
91 91 self.interactive = False
92 92
93 93 self.traceback = self.traceback or traceback
94 94
95 95 def verbosity_constraints(self):
96 96 self.quiet = self.configbool('ui', 'quiet')
97 97 self.verbose = self.configbool('ui', 'verbose')
98 98 self.debugflag = self.configbool('ui', 'debug')
99 99
100 100 if self.debugflag:
101 101 self.verbose = True
102 102 self.quiet = False
103 103 elif self.verbose and self.quiet:
104 104 self.quiet = self.verbose = False
105 105
106 106 def _is_trusted(self, fp, f, warn=True):
107 107 if not self.check_trusted:
108 108 return True
109 109 st = util.fstat(fp)
110 110 if util.isowner(fp, st):
111 111 return True
112 112 tusers = self.trusted_users
113 113 tgroups = self.trusted_groups
114 114 if not tusers:
115 115 user = util.username()
116 116 if user is not None:
117 117 self.trusted_users[user] = 1
118 118 self.fixconfig(section='trusted')
119 119 if (tusers or tgroups) and '*' not in tusers and '*' not in tgroups:
120 120 user = util.username(st.st_uid)
121 121 group = util.groupname(st.st_gid)
122 122 if user not in tusers and group not in tgroups:
123 123 if warn and self.report_untrusted:
124 124 self.warn(_('Not trusting file %s from untrusted '
125 125 'user %s, group %s\n') % (f, user, group))
126 126 return False
127 127 return True
128 128
129 129 def readconfig(self, fn, root=None):
130 130 if isinstance(fn, basestring):
131 131 fn = [fn]
132 132 for f in fn:
133 133 try:
134 134 fp = open(f)
135 135 except IOError:
136 136 continue
137 137 cdata = self.cdata
138 138 trusted = self._is_trusted(fp, f)
139 139 if not trusted:
140 140 if self.ucdata is None:
141 141 self.ucdata = dupconfig(self.cdata)
142 142 cdata = self.ucdata
143 143 elif self.ucdata is not None:
144 144 # use a separate configparser, so that we don't accidentally
145 145 # override ucdata settings later on.
146 146 cdata = util.configparser()
147 147
148 148 try:
149 149 cdata.readfp(fp, f)
150 150 except ConfigParser.ParsingError, inst:
151 151 msg = _("Failed to parse %s\n%s") % (f, inst)
152 152 if trusted:
153 153 raise util.Abort(msg)
154 154 self.warn(_("Ignored: %s\n") % msg)
155 155
156 156 if trusted:
157 157 if cdata != self.cdata:
158 158 updateconfig(cdata, self.cdata)
159 159 if self.ucdata is not None:
160 160 updateconfig(cdata, self.ucdata)
161 161 # override data from config files with data set with ui.setconfig
162 162 if self.overlay:
163 163 updateconfig(self.overlay, self.cdata)
164 164 if root is None:
165 165 root = os.path.expanduser('~')
166 166 self.fixconfig(root=root)
167 167
168 168 def readsections(self, filename, *sections):
169 169 """Read filename and add only the specified sections to the config data
170 170
171 171 The settings are added to the trusted config data.
172 172 """
173 173 if not sections:
174 174 return
175 175
176 176 cdata = util.configparser()
177 177 try:
178 178 try:
179 179 fp = open(filename)
180 180 except IOError, inst:
181 181 raise util.Abort(_("unable to open %s: %s") %
182 182 (filename, getattr(inst, "strerror", inst)))
183 183 try:
184 184 cdata.readfp(fp, filename)
185 185 finally:
186 186 fp.close()
187 187 except ConfigParser.ParsingError, inst:
188 188 raise util.Abort(_("failed to parse %s\n%s") % (filename, inst))
189 189
190 190 for section in sections:
191 191 if not cdata.has_section(section):
192 192 cdata.add_section(section)
193 193
194 194 updateconfig(cdata, self.cdata, sections)
195 195 if self.ucdata:
196 196 updateconfig(cdata, self.ucdata, sections)
197 197
198 198 def fixconfig(self, section=None, name=None, value=None, root=None):
199 199 # translate paths relative to root (or home) into absolute paths
200 200 if section is None or section == 'paths':
201 201 if root is None:
202 202 root = os.getcwd()
203 203 items = section and [(name, value)] or []
204 204 for cdata in self.cdata, self.ucdata, self.overlay:
205 205 if not cdata: continue
206 206 if not items and cdata.has_section('paths'):
207 207 pathsitems = cdata.items('paths')
208 208 else:
209 209 pathsitems = items
210 210 for n, path in pathsitems:
211 211 if path and "://" not in path and not os.path.isabs(path):
212 212 cdata.set("paths", n,
213 213 os.path.normpath(os.path.join(root, path)))
214 214
215 215 # update verbosity/interactive/report_untrusted settings
216 216 if section is None or section == 'ui':
217 217 if name is None or name in ('quiet', 'verbose', 'debug'):
218 218 self.verbosity_constraints()
219 219 if name is None or name == 'interactive':
220 220 interactive = self.configbool("ui", "interactive", None)
221 221 if interactive is None and self.interactive:
222 222 self.interactive = self.isatty()
223 223 else:
224 224 self.interactive = interactive
225 225 if name is None or name == 'report_untrusted':
226 226 self.report_untrusted = (
227 227 self.configbool("ui", "report_untrusted", True))
228 228
229 229 # update trust information
230 230 if (section is None or section == 'trusted') and self.trusted_users:
231 231 for user in self.configlist('trusted', 'users'):
232 232 self.trusted_users[user] = 1
233 233 for group in self.configlist('trusted', 'groups'):
234 234 self.trusted_groups[group] = 1
235 235
236 236 def setconfig(self, section, name, value):
237 237 if not self.overlay:
238 238 self.overlay = util.configparser()
239 239 for cdata in (self.overlay, self.cdata, self.ucdata):
240 240 if not cdata: continue
241 241 if not cdata.has_section(section):
242 242 cdata.add_section(section)
243 243 cdata.set(section, name, value)
244 244 self.fixconfig(section, name, value)
245 245
246 246 def _get_cdata(self, untrusted):
247 247 if untrusted and self.ucdata:
248 248 return self.ucdata
249 249 return self.cdata
250 250
251 251 def _config(self, section, name, default, funcname, untrusted, abort):
252 252 cdata = self._get_cdata(untrusted)
253 253 if cdata.has_option(section, name):
254 254 try:
255 255 func = getattr(cdata, funcname)
256 256 return func(section, name)
257 257 except (ConfigParser.InterpolationError, ValueError), inst:
258 258 msg = _("Error in configuration section [%s] "
259 259 "parameter '%s':\n%s") % (section, name, inst)
260 260 if abort:
261 261 raise util.Abort(msg)
262 262 self.warn(_("Ignored: %s\n") % msg)
263 263 return default
264 264
265 265 def _configcommon(self, section, name, default, funcname, untrusted):
266 266 value = self._config(section, name, default, funcname,
267 267 untrusted, abort=True)
268 268 if self.debugflag and not untrusted and self.ucdata:
269 269 uvalue = self._config(section, name, None, funcname,
270 270 untrusted=True, abort=False)
271 271 if uvalue is not None and uvalue != value:
272 272 self.warn(_("Ignoring untrusted configuration option "
273 273 "%s.%s = %s\n") % (section, name, uvalue))
274 274 return value
275 275
276 276 def config(self, section, name, default=None, untrusted=False):
277 277 return self._configcommon(section, name, default, 'get', untrusted)
278 278
279 279 def configbool(self, section, name, default=False, untrusted=False):
280 280 return self._configcommon(section, name, default, 'getboolean',
281 281 untrusted)
282 282
283 283 def configlist(self, section, name, default=None, untrusted=False):
284 284 """Return a list of comma/space separated strings"""
285 285 result = self.config(section, name, untrusted=untrusted)
286 286 if result is None:
287 287 result = default or []
288 288 if isinstance(result, basestring):
289 289 result = result.replace(",", " ").split()
290 290 return result
291 291
292 292 def has_section(self, section, untrusted=False):
293 293 '''tell whether section exists in config.'''
294 294 cdata = self._get_cdata(untrusted)
295 295 return cdata.has_section(section)
296 296
297 297 def _configitems(self, section, untrusted, abort):
298 298 items = {}
299 299 cdata = self._get_cdata(untrusted)
300 300 if cdata.has_section(section):
301 301 try:
302 302 items.update(dict(cdata.items(section)))
303 303 except ConfigParser.InterpolationError, inst:
304 304 msg = _("Error in configuration section [%s]:\n"
305 305 "%s") % (section, inst)
306 306 if abort:
307 307 raise util.Abort(msg)
308 308 self.warn(_("Ignored: %s\n") % msg)
309 309 return items
310 310
311 311 def configitems(self, section, untrusted=False):
312 312 items = self._configitems(section, untrusted=untrusted, abort=True)
313 313 if self.debugflag and not untrusted and self.ucdata:
314 314 uitems = self._configitems(section, untrusted=True, abort=False)
315 315 for k in util.sort(uitems):
316 316 if uitems[k] != items.get(k):
317 317 self.warn(_("Ignoring untrusted configuration option "
318 318 "%s.%s = %s\n") % (section, k, uitems[k]))
319 319 return util.sort(items.items())
320 320
321 321 def walkconfig(self, untrusted=False):
322 322 cdata = self._get_cdata(untrusted)
323 323 sections = cdata.sections()
324 324 sections.sort()
325 325 for section in sections:
326 326 for name, value in self.configitems(section, untrusted):
327 327 yield section, name, str(value).replace('\n', '\\n')
328 328
329 329 def username(self):
330 330 """Return default username to be used in commits.
331 331
332 332 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
333 333 and stop searching if one of these is set.
334 334 If not found and ui.askusername is True, ask the user, else use
335 335 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
336 336 """
337 337 user = os.environ.get("HGUSER")
338 338 if user is None:
339 339 user = self.config("ui", "username")
340 340 if user is None:
341 341 user = os.environ.get("EMAIL")
342 342 if user is None and self.configbool("ui", "askusername"):
343 343 user = self.prompt(_("Enter a commit username:"), default=None)
344 344 if user is None:
345 345 try:
346 346 user = '%s@%s' % (util.getuser(), socket.getfqdn())
347 347 self.warn(_("No username found, using '%s' instead\n") % user)
348 348 except KeyError:
349 349 pass
350 350 if not user:
351 351 raise util.Abort(_("Please specify a username."))
352 352 if "\n" in user:
353 raise util.Abort(_("username %s contains a newline\n") % `user`)
353 raise util.Abort(_("username %s contains a newline\n") % repr(user))
354 354 return user
355 355
356 356 def shortuser(self, user):
357 357 """Return a short representation of a user name or email address."""
358 358 if not self.verbose: user = util.shortuser(user)
359 359 return user
360 360
361 361 def expandpath(self, loc, default=None):
362 362 """Return repository location relative to cwd or from [paths]"""
363 363 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
364 364 return loc
365 365
366 366 path = self.config("paths", loc)
367 367 if not path and default is not None:
368 368 path = self.config("paths", default)
369 369 return path or loc
370 370
371 371 def pushbuffer(self):
372 372 self.buffers.append([])
373 373
374 374 def popbuffer(self):
375 375 return "".join(self.buffers.pop())
376 376
377 377 def write(self, *args):
378 378 if self.buffers:
379 379 self.buffers[-1].extend([str(a) for a in args])
380 380 else:
381 381 for a in args:
382 382 sys.stdout.write(str(a))
383 383
384 384 def write_err(self, *args):
385 385 try:
386 386 if not sys.stdout.closed: sys.stdout.flush()
387 387 for a in args:
388 388 sys.stderr.write(str(a))
389 389 # stderr may be buffered under win32 when redirected to files,
390 390 # including stdout.
391 391 if not sys.stderr.closed: sys.stderr.flush()
392 392 except IOError, inst:
393 393 if inst.errno != errno.EPIPE:
394 394 raise
395 395
396 396 def flush(self):
397 397 try: sys.stdout.flush()
398 398 except: pass
399 399 try: sys.stderr.flush()
400 400 except: pass
401 401
402 402 def _readline(self, prompt=''):
403 403 if self.isatty():
404 404 try:
405 405 # magically add command line editing support, where
406 406 # available
407 407 import readline
408 408 # force demandimport to really load the module
409 409 readline.read_history_file
410 410 except ImportError:
411 411 pass
412 412 line = raw_input(prompt)
413 413 # When stdin is in binary mode on Windows, it can cause
414 414 # raw_input() to emit an extra trailing carriage return
415 415 if os.linesep == '\r\n' and line and line[-1] == '\r':
416 416 line = line[:-1]
417 417 return line
418 418
419 419 def prompt(self, msg, pat=None, default="y"):
420 420 """Prompt user with msg, read response, and ensure it matches pat
421 421
422 422 If not interactive -- the default is returned
423 423 """
424 424 if not self.interactive:
425 425 self.note(msg, ' ', default, "\n")
426 426 return default
427 427 while True:
428 428 try:
429 429 r = self._readline(msg + ' ')
430 430 if not r:
431 431 return default
432 432 if not pat or re.match(pat, r):
433 433 return r
434 434 else:
435 435 self.write(_("unrecognized response\n"))
436 436 except EOFError:
437 437 raise util.Abort(_('response expected'))
438 438
439 439 def getpass(self, prompt=None, default=None):
440 440 if not self.interactive: return default
441 441 return getpass.getpass(prompt or _('password: '))
442 442 def status(self, *msg):
443 443 if not self.quiet: self.write(*msg)
444 444 def warn(self, *msg):
445 445 self.write_err(*msg)
446 446 def note(self, *msg):
447 447 if self.verbose: self.write(*msg)
448 448 def debug(self, *msg):
449 449 if self.debugflag: self.write(*msg)
450 450 def edit(self, text, user):
451 451 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
452 452 text=True)
453 453 try:
454 454 f = os.fdopen(fd, "w")
455 455 f.write(text)
456 456 f.close()
457 457
458 458 editor = self.geteditor()
459 459
460 460 util.system("%s \"%s\"" % (editor, name),
461 461 environ={'HGUSER': user},
462 462 onerr=util.Abort, errprefix=_("edit failed"))
463 463
464 464 f = open(name)
465 465 t = f.read()
466 466 f.close()
467 467 t = re.sub("(?m)^HG:.*\n", "", t)
468 468 finally:
469 469 os.unlink(name)
470 470
471 471 return t
472 472
473 473 def print_exc(self):
474 474 '''print exception traceback if traceback printing enabled.
475 475 only to call in exception handler. returns true if traceback
476 476 printed.'''
477 477 if self.traceback:
478 478 traceback.print_exc()
479 479 return self.traceback
480 480
481 481 def geteditor(self):
482 482 '''return editor to use'''
483 483 return (os.environ.get("HGEDITOR") or
484 484 self.config("ui", "editor") or
485 485 os.environ.get("VISUAL") or
486 486 os.environ.get("EDITOR", "vi"))
General Comments 0
You need to be logged in to leave comments. Login now