##// END OF EJS Templates
ui: display progress with decimal notation
Patrick Mezard -
r10220:500d09be stable
parent child Browse files
Show More
@@ -1,386 +1,386 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, util, error
10 import config, util, error
11
11
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
13 '0': False, 'no': False, 'false': False, 'off': False}
13 '0': False, 'no': False, 'false': False, 'off': False}
14
14
15 class ui(object):
15 class ui(object):
16 def __init__(self, src=None):
16 def __init__(self, src=None):
17 self._buffers = []
17 self._buffers = []
18 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
18 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
19 self._reportuntrusted = True
19 self._reportuntrusted = True
20 self._ocfg = config.config() # overlay
20 self._ocfg = config.config() # overlay
21 self._tcfg = config.config() # trusted
21 self._tcfg = config.config() # trusted
22 self._ucfg = config.config() # untrusted
22 self._ucfg = config.config() # untrusted
23 self._trustusers = set()
23 self._trustusers = set()
24 self._trustgroups = set()
24 self._trustgroups = set()
25
25
26 if src:
26 if src:
27 self._tcfg = src._tcfg.copy()
27 self._tcfg = src._tcfg.copy()
28 self._ucfg = src._ucfg.copy()
28 self._ucfg = src._ucfg.copy()
29 self._ocfg = src._ocfg.copy()
29 self._ocfg = src._ocfg.copy()
30 self._trustusers = src._trustusers.copy()
30 self._trustusers = src._trustusers.copy()
31 self._trustgroups = src._trustgroups.copy()
31 self._trustgroups = src._trustgroups.copy()
32 self.fixconfig()
32 self.fixconfig()
33 else:
33 else:
34 # we always trust global config files
34 # we always trust global config files
35 for f in util.rcpath():
35 for f in util.rcpath():
36 self.readconfig(f, trust=True)
36 self.readconfig(f, trust=True)
37
37
38 def copy(self):
38 def copy(self):
39 return self.__class__(self)
39 return self.__class__(self)
40
40
41 def _is_trusted(self, fp, f):
41 def _is_trusted(self, fp, f):
42 st = util.fstat(fp)
42 st = util.fstat(fp)
43 if util.isowner(st):
43 if util.isowner(st):
44 return True
44 return True
45
45
46 tusers, tgroups = self._trustusers, self._trustgroups
46 tusers, tgroups = self._trustusers, self._trustgroups
47 if '*' in tusers or '*' in tgroups:
47 if '*' in tusers or '*' in tgroups:
48 return True
48 return True
49
49
50 user = util.username(st.st_uid)
50 user = util.username(st.st_uid)
51 group = util.groupname(st.st_gid)
51 group = util.groupname(st.st_gid)
52 if user in tusers or group in tgroups or user == util.username():
52 if user in tusers or group in tgroups or user == util.username():
53 return True
53 return True
54
54
55 if self._reportuntrusted:
55 if self._reportuntrusted:
56 self.warn(_('Not trusting file %s from untrusted '
56 self.warn(_('Not trusting file %s from untrusted '
57 'user %s, group %s\n') % (f, user, group))
57 'user %s, group %s\n') % (f, user, group))
58 return False
58 return False
59
59
60 def readconfig(self, filename, root=None, trust=False,
60 def readconfig(self, filename, root=None, trust=False,
61 sections=None, remap=None):
61 sections=None, remap=None):
62 try:
62 try:
63 fp = open(filename)
63 fp = open(filename)
64 except IOError:
64 except IOError:
65 if not sections: # ignore unless we were looking for something
65 if not sections: # ignore unless we were looking for something
66 return
66 return
67 raise
67 raise
68
68
69 cfg = config.config()
69 cfg = config.config()
70 trusted = sections or trust or self._is_trusted(fp, filename)
70 trusted = sections or trust or self._is_trusted(fp, filename)
71
71
72 try:
72 try:
73 cfg.read(filename, fp, sections=sections, remap=remap)
73 cfg.read(filename, fp, sections=sections, remap=remap)
74 except error.ConfigError, inst:
74 except error.ConfigError, inst:
75 if trusted:
75 if trusted:
76 raise
76 raise
77 self.warn(_("Ignored: %s\n") % str(inst))
77 self.warn(_("Ignored: %s\n") % str(inst))
78
78
79 if trusted:
79 if trusted:
80 self._tcfg.update(cfg)
80 self._tcfg.update(cfg)
81 self._tcfg.update(self._ocfg)
81 self._tcfg.update(self._ocfg)
82 self._ucfg.update(cfg)
82 self._ucfg.update(cfg)
83 self._ucfg.update(self._ocfg)
83 self._ucfg.update(self._ocfg)
84
84
85 if root is None:
85 if root is None:
86 root = os.path.expanduser('~')
86 root = os.path.expanduser('~')
87 self.fixconfig(root=root)
87 self.fixconfig(root=root)
88
88
89 def fixconfig(self, root=None):
89 def fixconfig(self, root=None):
90 # translate paths relative to root (or home) into absolute paths
90 # translate paths relative to root (or home) into absolute paths
91 root = root or os.getcwd()
91 root = root or os.getcwd()
92 for c in self._tcfg, self._ucfg, self._ocfg:
92 for c in self._tcfg, self._ucfg, self._ocfg:
93 for n, p in c.items('paths'):
93 for n, p in c.items('paths'):
94 if p and "://" not in p and not os.path.isabs(p):
94 if p and "://" not in p and not os.path.isabs(p):
95 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
95 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
96
96
97 # update ui options
97 # update ui options
98 self.debugflag = self.configbool('ui', 'debug')
98 self.debugflag = self.configbool('ui', 'debug')
99 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
99 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
100 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
100 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
101 if self.verbose and self.quiet:
101 if self.verbose and self.quiet:
102 self.quiet = self.verbose = False
102 self.quiet = self.verbose = False
103 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
103 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
104 self.tracebackflag = self.configbool('ui', 'traceback', False)
104 self.tracebackflag = self.configbool('ui', 'traceback', False)
105
105
106 # update trust information
106 # update trust information
107 self._trustusers.update(self.configlist('trusted', 'users'))
107 self._trustusers.update(self.configlist('trusted', 'users'))
108 self._trustgroups.update(self.configlist('trusted', 'groups'))
108 self._trustgroups.update(self.configlist('trusted', 'groups'))
109
109
110 def setconfig(self, section, name, value):
110 def setconfig(self, section, name, value):
111 for cfg in (self._ocfg, self._tcfg, self._ucfg):
111 for cfg in (self._ocfg, self._tcfg, self._ucfg):
112 cfg.set(section, name, value)
112 cfg.set(section, name, value)
113 self.fixconfig()
113 self.fixconfig()
114
114
115 def _data(self, untrusted):
115 def _data(self, untrusted):
116 return untrusted and self._ucfg or self._tcfg
116 return untrusted and self._ucfg or self._tcfg
117
117
118 def configsource(self, section, name, untrusted=False):
118 def configsource(self, section, name, untrusted=False):
119 return self._data(untrusted).source(section, name) or 'none'
119 return self._data(untrusted).source(section, name) or 'none'
120
120
121 def config(self, section, name, default=None, untrusted=False):
121 def config(self, section, name, default=None, untrusted=False):
122 value = self._data(untrusted).get(section, name, default)
122 value = self._data(untrusted).get(section, name, default)
123 if self.debugflag and not untrusted and self._reportuntrusted:
123 if self.debugflag and not untrusted and self._reportuntrusted:
124 uvalue = self._ucfg.get(section, name)
124 uvalue = self._ucfg.get(section, name)
125 if uvalue is not None and uvalue != value:
125 if uvalue is not None and uvalue != value:
126 self.debug(_("ignoring untrusted configuration option "
126 self.debug(_("ignoring untrusted configuration option "
127 "%s.%s = %s\n") % (section, name, uvalue))
127 "%s.%s = %s\n") % (section, name, uvalue))
128 return value
128 return value
129
129
130 def configbool(self, section, name, default=False, untrusted=False):
130 def configbool(self, section, name, default=False, untrusted=False):
131 v = self.config(section, name, None, untrusted)
131 v = self.config(section, name, None, untrusted)
132 if v is None:
132 if v is None:
133 return default
133 return default
134 if v.lower() not in _booleans:
134 if v.lower() not in _booleans:
135 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
135 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
136 % (section, name, v))
136 % (section, name, v))
137 return _booleans[v.lower()]
137 return _booleans[v.lower()]
138
138
139 def configlist(self, section, name, default=None, untrusted=False):
139 def configlist(self, section, name, default=None, untrusted=False):
140 """Return a list of comma/space separated strings"""
140 """Return a list of comma/space separated strings"""
141 result = self.config(section, name, untrusted=untrusted)
141 result = self.config(section, name, untrusted=untrusted)
142 if result is None:
142 if result is None:
143 result = default or []
143 result = default or []
144 if isinstance(result, basestring):
144 if isinstance(result, basestring):
145 result = result.replace(",", " ").split()
145 result = result.replace(",", " ").split()
146 return result
146 return result
147
147
148 def has_section(self, section, untrusted=False):
148 def has_section(self, section, untrusted=False):
149 '''tell whether section exists in config.'''
149 '''tell whether section exists in config.'''
150 return section in self._data(untrusted)
150 return section in self._data(untrusted)
151
151
152 def configitems(self, section, untrusted=False):
152 def configitems(self, section, untrusted=False):
153 items = self._data(untrusted).items(section)
153 items = self._data(untrusted).items(section)
154 if self.debugflag and not untrusted and self._reportuntrusted:
154 if self.debugflag and not untrusted and self._reportuntrusted:
155 for k, v in self._ucfg.items(section):
155 for k, v in self._ucfg.items(section):
156 if self._tcfg.get(section, k) != v:
156 if self._tcfg.get(section, k) != v:
157 self.debug(_("ignoring untrusted configuration option "
157 self.debug(_("ignoring untrusted configuration option "
158 "%s.%s = %s\n") % (section, k, v))
158 "%s.%s = %s\n") % (section, k, v))
159 return items
159 return items
160
160
161 def walkconfig(self, untrusted=False):
161 def walkconfig(self, untrusted=False):
162 cfg = self._data(untrusted)
162 cfg = self._data(untrusted)
163 for section in cfg.sections():
163 for section in cfg.sections():
164 for name, value in self.configitems(section, untrusted):
164 for name, value in self.configitems(section, untrusted):
165 yield section, name, str(value).replace('\n', '\\n')
165 yield section, name, str(value).replace('\n', '\\n')
166
166
167 def username(self):
167 def username(self):
168 """Return default username to be used in commits.
168 """Return default username to be used in commits.
169
169
170 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
170 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
171 and stop searching if one of these is set.
171 and stop searching if one of these is set.
172 If not found and ui.askusername is True, ask the user, else use
172 If not found and ui.askusername is True, ask the user, else use
173 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
173 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
174 """
174 """
175 user = os.environ.get("HGUSER")
175 user = os.environ.get("HGUSER")
176 if user is None:
176 if user is None:
177 user = self.config("ui", "username")
177 user = self.config("ui", "username")
178 if user is None:
178 if user is None:
179 user = os.environ.get("EMAIL")
179 user = os.environ.get("EMAIL")
180 if user is None and self.configbool("ui", "askusername"):
180 if user is None and self.configbool("ui", "askusername"):
181 user = self.prompt(_("enter a commit username:"), default=None)
181 user = self.prompt(_("enter a commit username:"), default=None)
182 if user is None and not self.interactive():
182 if user is None and not self.interactive():
183 try:
183 try:
184 user = '%s@%s' % (util.getuser(), socket.getfqdn())
184 user = '%s@%s' % (util.getuser(), socket.getfqdn())
185 self.warn(_("No username found, using '%s' instead\n") % user)
185 self.warn(_("No username found, using '%s' instead\n") % user)
186 except KeyError:
186 except KeyError:
187 pass
187 pass
188 if not user:
188 if not user:
189 raise util.Abort(_('no username supplied (see "hg help config")'))
189 raise util.Abort(_('no username supplied (see "hg help config")'))
190 if "\n" in user:
190 if "\n" in user:
191 raise util.Abort(_("username %s contains a newline\n") % repr(user))
191 raise util.Abort(_("username %s contains a newline\n") % repr(user))
192 return user
192 return user
193
193
194 def shortuser(self, user):
194 def shortuser(self, user):
195 """Return a short representation of a user name or email address."""
195 """Return a short representation of a user name or email address."""
196 if not self.verbose: user = util.shortuser(user)
196 if not self.verbose: user = util.shortuser(user)
197 return user
197 return user
198
198
199 def _path(self, loc):
199 def _path(self, loc):
200 p = self.config('paths', loc)
200 p = self.config('paths', loc)
201 if p:
201 if p:
202 if '%%' in p:
202 if '%%' in p:
203 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
203 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
204 (loc, p, self.configsource('paths', loc)))
204 (loc, p, self.configsource('paths', loc)))
205 p = p.replace('%%', '%')
205 p = p.replace('%%', '%')
206 p = util.expandpath(p)
206 p = util.expandpath(p)
207 return p
207 return p
208
208
209 def expandpath(self, loc, default=None):
209 def expandpath(self, loc, default=None):
210 """Return repository location relative to cwd or from [paths]"""
210 """Return repository location relative to cwd or from [paths]"""
211 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
211 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
212 return loc
212 return loc
213
213
214 path = self._path(loc)
214 path = self._path(loc)
215 if not path and default is not None:
215 if not path and default is not None:
216 path = self._path(default)
216 path = self._path(default)
217 return path or loc
217 return path or loc
218
218
219 def pushbuffer(self):
219 def pushbuffer(self):
220 self._buffers.append([])
220 self._buffers.append([])
221
221
222 def popbuffer(self):
222 def popbuffer(self):
223 return "".join(self._buffers.pop())
223 return "".join(self._buffers.pop())
224
224
225 def write(self, *args):
225 def write(self, *args):
226 if self._buffers:
226 if self._buffers:
227 self._buffers[-1].extend([str(a) for a in args])
227 self._buffers[-1].extend([str(a) for a in args])
228 else:
228 else:
229 for a in args:
229 for a in args:
230 sys.stdout.write(str(a))
230 sys.stdout.write(str(a))
231
231
232 def write_err(self, *args):
232 def write_err(self, *args):
233 try:
233 try:
234 if not sys.stdout.closed: sys.stdout.flush()
234 if not sys.stdout.closed: sys.stdout.flush()
235 for a in args:
235 for a in args:
236 sys.stderr.write(str(a))
236 sys.stderr.write(str(a))
237 # stderr may be buffered under win32 when redirected to files,
237 # stderr may be buffered under win32 when redirected to files,
238 # including stdout.
238 # including stdout.
239 if not sys.stderr.closed: sys.stderr.flush()
239 if not sys.stderr.closed: sys.stderr.flush()
240 except IOError, inst:
240 except IOError, inst:
241 if inst.errno != errno.EPIPE:
241 if inst.errno != errno.EPIPE:
242 raise
242 raise
243
243
244 def flush(self):
244 def flush(self):
245 try: sys.stdout.flush()
245 try: sys.stdout.flush()
246 except: pass
246 except: pass
247 try: sys.stderr.flush()
247 try: sys.stderr.flush()
248 except: pass
248 except: pass
249
249
250 def interactive(self):
250 def interactive(self):
251 i = self.configbool("ui", "interactive", None)
251 i = self.configbool("ui", "interactive", None)
252 if i is None:
252 if i is None:
253 return sys.stdin.isatty()
253 return sys.stdin.isatty()
254 return i
254 return i
255
255
256 def _readline(self, prompt=''):
256 def _readline(self, prompt=''):
257 if sys.stdin.isatty():
257 if sys.stdin.isatty():
258 try:
258 try:
259 # magically add command line editing support, where
259 # magically add command line editing support, where
260 # available
260 # available
261 import readline
261 import readline
262 # force demandimport to really load the module
262 # force demandimport to really load the module
263 readline.read_history_file
263 readline.read_history_file
264 # windows sometimes raises something other than ImportError
264 # windows sometimes raises something other than ImportError
265 except Exception:
265 except Exception:
266 pass
266 pass
267 line = raw_input(prompt)
267 line = raw_input(prompt)
268 # When stdin is in binary mode on Windows, it can cause
268 # When stdin is in binary mode on Windows, it can cause
269 # raw_input() to emit an extra trailing carriage return
269 # raw_input() to emit an extra trailing carriage return
270 if os.linesep == '\r\n' and line and line[-1] == '\r':
270 if os.linesep == '\r\n' and line and line[-1] == '\r':
271 line = line[:-1]
271 line = line[:-1]
272 return line
272 return line
273
273
274 def prompt(self, msg, default="y"):
274 def prompt(self, msg, default="y"):
275 """Prompt user with msg, read response.
275 """Prompt user with msg, read response.
276 If ui is not interactive, the default is returned.
276 If ui is not interactive, the default is returned.
277 """
277 """
278 if not self.interactive():
278 if not self.interactive():
279 self.write(msg, ' ', default, "\n")
279 self.write(msg, ' ', default, "\n")
280 return default
280 return default
281 try:
281 try:
282 r = self._readline(msg + ' ')
282 r = self._readline(msg + ' ')
283 if not r:
283 if not r:
284 return default
284 return default
285 return r
285 return r
286 except EOFError:
286 except EOFError:
287 raise util.Abort(_('response expected'))
287 raise util.Abort(_('response expected'))
288
288
289 def promptchoice(self, msg, choices, default=0):
289 def promptchoice(self, msg, choices, default=0):
290 """Prompt user with msg, read response, and ensure it matches
290 """Prompt user with msg, read response, and ensure it matches
291 one of the provided choices. The index of the choice is returned.
291 one of the provided choices. The index of the choice is returned.
292 choices is a sequence of acceptable responses with the format:
292 choices is a sequence of acceptable responses with the format:
293 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
293 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
294 If ui is not interactive, the default is returned.
294 If ui is not interactive, the default is returned.
295 """
295 """
296 resps = [s[s.index('&')+1].lower() for s in choices]
296 resps = [s[s.index('&')+1].lower() for s in choices]
297 while True:
297 while True:
298 r = self.prompt(msg, resps[default])
298 r = self.prompt(msg, resps[default])
299 if r.lower() in resps:
299 if r.lower() in resps:
300 return resps.index(r.lower())
300 return resps.index(r.lower())
301 self.write(_("unrecognized response\n"))
301 self.write(_("unrecognized response\n"))
302
302
303
303
304 def getpass(self, prompt=None, default=None):
304 def getpass(self, prompt=None, default=None):
305 if not self.interactive(): return default
305 if not self.interactive(): return default
306 try:
306 try:
307 return getpass.getpass(prompt or _('password: '))
307 return getpass.getpass(prompt or _('password: '))
308 except EOFError:
308 except EOFError:
309 raise util.Abort(_('response expected'))
309 raise util.Abort(_('response expected'))
310 def status(self, *msg):
310 def status(self, *msg):
311 if not self.quiet: self.write(*msg)
311 if not self.quiet: self.write(*msg)
312 def warn(self, *msg):
312 def warn(self, *msg):
313 self.write_err(*msg)
313 self.write_err(*msg)
314 def note(self, *msg):
314 def note(self, *msg):
315 if self.verbose: self.write(*msg)
315 if self.verbose: self.write(*msg)
316 def debug(self, *msg):
316 def debug(self, *msg):
317 if self.debugflag: self.write(*msg)
317 if self.debugflag: self.write(*msg)
318 def edit(self, text, user):
318 def edit(self, text, user):
319 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
319 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
320 text=True)
320 text=True)
321 try:
321 try:
322 f = os.fdopen(fd, "w")
322 f = os.fdopen(fd, "w")
323 f.write(text)
323 f.write(text)
324 f.close()
324 f.close()
325
325
326 editor = self.geteditor()
326 editor = self.geteditor()
327
327
328 util.system("%s \"%s\"" % (editor, name),
328 util.system("%s \"%s\"" % (editor, name),
329 environ={'HGUSER': user},
329 environ={'HGUSER': user},
330 onerr=util.Abort, errprefix=_("edit failed"))
330 onerr=util.Abort, errprefix=_("edit failed"))
331
331
332 f = open(name)
332 f = open(name)
333 t = f.read()
333 t = f.read()
334 f.close()
334 f.close()
335 finally:
335 finally:
336 os.unlink(name)
336 os.unlink(name)
337
337
338 return t
338 return t
339
339
340 def traceback(self, exc=None):
340 def traceback(self, exc=None):
341 '''print exception traceback if traceback printing enabled.
341 '''print exception traceback if traceback printing enabled.
342 only to call in exception handler. returns true if traceback
342 only to call in exception handler. returns true if traceback
343 printed.'''
343 printed.'''
344 if self.tracebackflag:
344 if self.tracebackflag:
345 if exc:
345 if exc:
346 traceback.print_exception(exc[0], exc[1], exc[2])
346 traceback.print_exception(exc[0], exc[1], exc[2])
347 else:
347 else:
348 traceback.print_exc()
348 traceback.print_exc()
349 return self.tracebackflag
349 return self.tracebackflag
350
350
351 def geteditor(self):
351 def geteditor(self):
352 '''return editor to use'''
352 '''return editor to use'''
353 return (os.environ.get("HGEDITOR") or
353 return (os.environ.get("HGEDITOR") or
354 self.config("ui", "editor") or
354 self.config("ui", "editor") or
355 os.environ.get("VISUAL") or
355 os.environ.get("VISUAL") or
356 os.environ.get("EDITOR", "vi"))
356 os.environ.get("EDITOR", "vi"))
357
357
358 def progress(self, topic, pos, item="", unit="", total=None):
358 def progress(self, topic, pos, item="", unit="", total=None):
359 '''show a progress message
359 '''show a progress message
360
360
361 With stock hg, this is simply a debug message that is hidden
361 With stock hg, this is simply a debug message that is hidden
362 by default, but with extensions or GUI tools it may be
362 by default, but with extensions or GUI tools it may be
363 visible. 'topic' is the current operation, 'item' is a
363 visible. 'topic' is the current operation, 'item' is a
364 non-numeric marker of the current position (ie the currently
364 non-numeric marker of the current position (ie the currently
365 in-process file), 'pos' is the current numeric position (ie
365 in-process file), 'pos' is the current numeric position (ie
366 revision, bytes, etc.), unit is a corresponding unit label,
366 revision, bytes, etc.), unit is a corresponding unit label,
367 and total is the highest expected pos.
367 and total is the highest expected pos.
368
368
369 Multiple nested topics may be active at a time. All topics
369 Multiple nested topics may be active at a time. All topics
370 should be marked closed by setting pos to None at termination.
370 should be marked closed by setting pos to None at termination.
371 '''
371 '''
372
372
373 if pos == None or not self.debugflag:
373 if pos == None or not self.debugflag:
374 return
374 return
375
375
376 if unit:
376 if unit:
377 unit = ' ' + unit
377 unit = ' ' + unit
378 if item:
378 if item:
379 item = ' ' + item
379 item = ' ' + item
380
380
381 if total:
381 if total:
382 pct = 100.0 * pos / total
382 pct = 100.0 * pos / total
383 self.debug('%s:%s %s/%s%s (%4.2g%%)\n'
383 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
384 % (topic, item, pos, total, unit, pct))
384 % (topic, item, pos, total, unit, pct))
385 else:
385 else:
386 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
386 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
@@ -1,25 +1,25 b''
1 % create source repository
1 % create source repository
2 adding a
2 adding a
3 adding b
3 adding b
4 % clone and pull to break links
4 % clone and pull to break links
5 requesting all changes
5 requesting all changes
6 adding changesets
6 adding changesets
7 adding manifests
7 adding manifests
8 adding file changes
8 adding file changes
9 added 1 changesets with 2 changes to 2 files
9 added 1 changesets with 2 changes to 2 files
10 updating to branch default
10 updating to branch default
11 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
11 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 created new head
12 created new head
13 % relink
13 % relink
14 relinking .hg/store
14 relinking .hg/store
15 collected 5 candidate storage files
15 collected 5 candidate storage files
16 not linkable: 00changelog.i
16 not linkable: 00changelog.i
17 not linkable: 00manifest.i
17 not linkable: 00manifest.i
18 not linkable: data/b.i
18 not linkable: data/b.i
19 pruned down to 2 probably relinkable files
19 pruned down to 2 probably relinkable files
20 relink: data/a.i 1/2 files ( 50%)
20 relink: data/a.i 1/2 files (50.00%)
21 not linkable: data/dummy.i
21 not linkable: data/dummy.i
22 relinked 1 files (136 bytes reclaimed)
22 relinked 1 files (136 bytes reclaimed)
23 % check hardlinks
23 % check hardlinks
24 repo/.hg/store/data/a.i == clone/.hg/store/data/a.i
24 repo/.hg/store/data/a.i == clone/.hg/store/data/a.i
25 repo/.hg/store/data/b.i != clone/.hg/store/data/b.i
25 repo/.hg/store/data/b.i != clone/.hg/store/data/b.i
General Comments 0
You need to be logged in to leave comments. Login now