##// END OF EJS Templates
progress: move all logic altering the ui object logic in mercurial.ui...
Pierre-Yves David -
r25499:0fa964d6 default
parent child Browse files
Show More
@@ -1,77 +1,43 b''
1 # progress.py show progress bars for some actions
1 # progress.py show progress bars for some actions
2 #
2 #
3 # Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
3 # Copyright (C) 2010 Augie Fackler <durin42@gmail.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 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """show progress bars for some actions
8 """show progress bars for some actions
9
9
10 This extension uses the progress information logged by hg commands
10 This extension uses the progress information logged by hg commands
11 to draw progress bars that are as informative as possible. Some progress
11 to draw progress bars that are as informative as possible. Some progress
12 bars only offer indeterminate information, while others have a definite
12 bars only offer indeterminate information, while others have a definite
13 end point.
13 end point.
14
14
15 The following settings are available::
15 The following settings are available::
16
16
17 [progress]
17 [progress]
18 delay = 3 # number of seconds (float) before showing the progress bar
18 delay = 3 # number of seconds (float) before showing the progress bar
19 changedelay = 1 # changedelay: minimum delay before showing a new topic.
19 changedelay = 1 # changedelay: minimum delay before showing a new topic.
20 # If set to less than 3 * refresh, that value will
20 # If set to less than 3 * refresh, that value will
21 # be used instead.
21 # be used instead.
22 refresh = 0.1 # time in seconds between refreshes of the progress bar
22 refresh = 0.1 # time in seconds between refreshes of the progress bar
23 format = topic bar number estimate # format of the progress bar
23 format = topic bar number estimate # format of the progress bar
24 width = <none> # if set, the maximum width of the progress information
24 width = <none> # if set, the maximum width of the progress information
25 # (that is, min(width, term width) will be used)
25 # (that is, min(width, term width) will be used)
26 clear-complete = True # clear the progress bar after it's done
26 clear-complete = True # clear the progress bar after it's done
27 disable = False # if true, don't show a progress bar
27 disable = False # if true, don't show a progress bar
28 assume-tty = False # if true, ALWAYS show a progress bar, unless
28 assume-tty = False # if true, ALWAYS show a progress bar, unless
29 # disable is given
29 # disable is given
30
30
31 Valid entries for the format field are topic, bar, number, unit,
31 Valid entries for the format field are topic, bar, number, unit,
32 estimate, speed, and item. item defaults to the last 20 characters of
32 estimate, speed, and item. item defaults to the last 20 characters of
33 the item, but this can be changed by adding either ``-<num>`` which
33 the item, but this can be changed by adding either ``-<num>`` which
34 would take the last num characters, or ``+<num>`` for the first num
34 would take the last num characters, or ``+<num>`` for the first num
35 characters.
35 characters.
36 """
36 """
37
37
38 from mercurial import progress
39 from mercurial import ui as uimod
40
41 def uisetup(ui):
38 def uisetup(ui):
42 class progressui(ui.__class__):
39 if ui.config('progress', 'disable', None) is None:
43 _progbar = None
40 ui.setconfig('progress', 'disable', 'False', 'hgext-progress')
44
45 def _quiet(self):
46 return self.debugflag or self.quiet
47
48 def progress(self, *args, **opts):
49 if not self._quiet():
50 self._progbar.progress(*args, **opts)
51 return super(progressui, self).progress(*args, **opts)
52
53 def write(self, *args, **opts):
54 if not self._quiet() and self._progbar.printed:
55 self._progbar.clear()
56 return super(progressui, self).write(*args, **opts)
57
58 def write_err(self, *args, **opts):
59 if not self._quiet() and self._progbar.printed:
60 self._progbar.clear()
61 return super(progressui, self).write_err(*args, **opts)
62
63 # Apps that derive a class from ui.ui() can use
64 # setconfig('progress', 'disable', 'True') to disable this extension
65 if ui.configbool('progress', 'disable'):
66 return
67 if progress.shouldprint(ui) and not ui.debugflag and not ui.quiet:
68 dval = object()
69 if getattr(ui, '_progbar', dval) is dval:
70 ui.__class__ = progressui
71 # we instantiate one globally-shared progress bar to avoid
72 # competing progress bars when multiple UI objects get created
73 if not progressui._progbar:
74 progressui._progbar = uimod.getprogbar(ui)
75
41
76 def reposetup(ui, repo):
42 def reposetup(ui, repo):
77 uisetup(repo.ui)
43 uisetup(repo.ui)
@@ -1,997 +1,1017 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 or any later version.
6 # GNU General Public License version 2 or any later version.
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, scmutil, util, error, formatter, progress
10 import config, scmutil, util, error, formatter, progress
11 from node import hex
11 from node import hex
12
12
13 samplehgrcs = {
13 samplehgrcs = {
14 'user':
14 'user':
15 """# example user config (see "hg help config" for more info)
15 """# example user config (see "hg help config" for more info)
16 [ui]
16 [ui]
17 # name and email, e.g.
17 # name and email, e.g.
18 # username = Jane Doe <jdoe@example.com>
18 # username = Jane Doe <jdoe@example.com>
19 username =
19 username =
20
20
21 [extensions]
21 [extensions]
22 # uncomment these lines to enable some popular extensions
22 # uncomment these lines to enable some popular extensions
23 # (see "hg help extensions" for more info)
23 # (see "hg help extensions" for more info)
24 #
24 #
25 # pager =
25 # pager =
26 # progress =
26 # progress =
27 # color =""",
27 # color =""",
28
28
29 'cloned':
29 'cloned':
30 """# example repository config (see "hg help config" for more info)
30 """# example repository config (see "hg help config" for more info)
31 [paths]
31 [paths]
32 default = %s
32 default = %s
33
33
34 # path aliases to other clones of this repo in URLs or filesystem paths
34 # path aliases to other clones of this repo in URLs or filesystem paths
35 # (see "hg help config.paths" for more info)
35 # (see "hg help config.paths" for more info)
36 #
36 #
37 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
37 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
38 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
38 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
39 # my-clone = /home/jdoe/jdoes-clone
39 # my-clone = /home/jdoe/jdoes-clone
40
40
41 [ui]
41 [ui]
42 # name and email (local to this repository, optional), e.g.
42 # name and email (local to this repository, optional), e.g.
43 # username = Jane Doe <jdoe@example.com>
43 # username = Jane Doe <jdoe@example.com>
44 """,
44 """,
45
45
46 'local':
46 'local':
47 """# example repository config (see "hg help config" for more info)
47 """# example repository config (see "hg help config" for more info)
48 [paths]
48 [paths]
49 # path aliases to other clones of this repo in URLs or filesystem paths
49 # path aliases to other clones of this repo in URLs or filesystem paths
50 # (see "hg help config.paths" for more info)
50 # (see "hg help config.paths" for more info)
51 #
51 #
52 # default = http://example.com/hg/example-repo
52 # default = http://example.com/hg/example-repo
53 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
53 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
54 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
54 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
55 # my-clone = /home/jdoe/jdoes-clone
55 # my-clone = /home/jdoe/jdoes-clone
56
56
57 [ui]
57 [ui]
58 # name and email (local to this repository, optional), e.g.
58 # name and email (local to this repository, optional), e.g.
59 # username = Jane Doe <jdoe@example.com>
59 # username = Jane Doe <jdoe@example.com>
60 """,
60 """,
61
61
62 'global':
62 'global':
63 """# example system-wide hg config (see "hg help config" for more info)
63 """# example system-wide hg config (see "hg help config" for more info)
64
64
65 [extensions]
65 [extensions]
66 # uncomment these lines to enable some popular extensions
66 # uncomment these lines to enable some popular extensions
67 # (see "hg help extensions" for more info)
67 # (see "hg help extensions" for more info)
68 #
68 #
69 # blackbox =
69 # blackbox =
70 # progress =
70 # progress =
71 # color =
71 # color =
72 # pager =""",
72 # pager =""",
73 }
73 }
74
74
75 class ui(object):
75 class ui(object):
76 def __init__(self, src=None):
76 def __init__(self, src=None):
77 # _buffers: used for temporary capture of output
77 # _buffers: used for temporary capture of output
78 self._buffers = []
78 self._buffers = []
79 # _bufferstates:
79 # _bufferstates:
80 # should the temporary capture include stderr and subprocess output
80 # should the temporary capture include stderr and subprocess output
81 self._bufferstates = []
81 self._bufferstates = []
82 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
82 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
83 self._reportuntrusted = True
83 self._reportuntrusted = True
84 self._ocfg = config.config() # overlay
84 self._ocfg = config.config() # overlay
85 self._tcfg = config.config() # trusted
85 self._tcfg = config.config() # trusted
86 self._ucfg = config.config() # untrusted
86 self._ucfg = config.config() # untrusted
87 self._trustusers = set()
87 self._trustusers = set()
88 self._trustgroups = set()
88 self._trustgroups = set()
89 self.callhooks = True
89 self.callhooks = True
90
90
91 if src:
91 if src:
92 self.fout = src.fout
92 self.fout = src.fout
93 self.ferr = src.ferr
93 self.ferr = src.ferr
94 self.fin = src.fin
94 self.fin = src.fin
95
95
96 self._tcfg = src._tcfg.copy()
96 self._tcfg = src._tcfg.copy()
97 self._ucfg = src._ucfg.copy()
97 self._ucfg = src._ucfg.copy()
98 self._ocfg = src._ocfg.copy()
98 self._ocfg = src._ocfg.copy()
99 self._trustusers = src._trustusers.copy()
99 self._trustusers = src._trustusers.copy()
100 self._trustgroups = src._trustgroups.copy()
100 self._trustgroups = src._trustgroups.copy()
101 self.environ = src.environ
101 self.environ = src.environ
102 self.callhooks = src.callhooks
102 self.callhooks = src.callhooks
103 self.fixconfig()
103 self.fixconfig()
104 else:
104 else:
105 self.fout = sys.stdout
105 self.fout = sys.stdout
106 self.ferr = sys.stderr
106 self.ferr = sys.stderr
107 self.fin = sys.stdin
107 self.fin = sys.stdin
108
108
109 # shared read-only environment
109 # shared read-only environment
110 self.environ = os.environ
110 self.environ = os.environ
111 # we always trust global config files
111 # we always trust global config files
112 for f in scmutil.rcpath():
112 for f in scmutil.rcpath():
113 self.readconfig(f, trust=True)
113 self.readconfig(f, trust=True)
114
114
115 def copy(self):
115 def copy(self):
116 return self.__class__(self)
116 return self.__class__(self)
117
117
118 def formatter(self, topic, opts):
118 def formatter(self, topic, opts):
119 return formatter.formatter(self, topic, opts)
119 return formatter.formatter(self, topic, opts)
120
120
121 def _trusted(self, fp, f):
121 def _trusted(self, fp, f):
122 st = util.fstat(fp)
122 st = util.fstat(fp)
123 if util.isowner(st):
123 if util.isowner(st):
124 return True
124 return True
125
125
126 tusers, tgroups = self._trustusers, self._trustgroups
126 tusers, tgroups = self._trustusers, self._trustgroups
127 if '*' in tusers or '*' in tgroups:
127 if '*' in tusers or '*' in tgroups:
128 return True
128 return True
129
129
130 user = util.username(st.st_uid)
130 user = util.username(st.st_uid)
131 group = util.groupname(st.st_gid)
131 group = util.groupname(st.st_gid)
132 if user in tusers or group in tgroups or user == util.username():
132 if user in tusers or group in tgroups or user == util.username():
133 return True
133 return True
134
134
135 if self._reportuntrusted:
135 if self._reportuntrusted:
136 self.warn(_('not trusting file %s from untrusted '
136 self.warn(_('not trusting file %s from untrusted '
137 'user %s, group %s\n') % (f, user, group))
137 'user %s, group %s\n') % (f, user, group))
138 return False
138 return False
139
139
140 def readconfig(self, filename, root=None, trust=False,
140 def readconfig(self, filename, root=None, trust=False,
141 sections=None, remap=None):
141 sections=None, remap=None):
142 try:
142 try:
143 fp = open(filename)
143 fp = open(filename)
144 except IOError:
144 except IOError:
145 if not sections: # ignore unless we were looking for something
145 if not sections: # ignore unless we were looking for something
146 return
146 return
147 raise
147 raise
148
148
149 cfg = config.config()
149 cfg = config.config()
150 trusted = sections or trust or self._trusted(fp, filename)
150 trusted = sections or trust or self._trusted(fp, filename)
151
151
152 try:
152 try:
153 cfg.read(filename, fp, sections=sections, remap=remap)
153 cfg.read(filename, fp, sections=sections, remap=remap)
154 fp.close()
154 fp.close()
155 except error.ConfigError, inst:
155 except error.ConfigError, inst:
156 if trusted:
156 if trusted:
157 raise
157 raise
158 self.warn(_("ignored: %s\n") % str(inst))
158 self.warn(_("ignored: %s\n") % str(inst))
159
159
160 if self.plain():
160 if self.plain():
161 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
161 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
162 'logtemplate', 'statuscopies', 'style',
162 'logtemplate', 'statuscopies', 'style',
163 'traceback', 'verbose'):
163 'traceback', 'verbose'):
164 if k in cfg['ui']:
164 if k in cfg['ui']:
165 del cfg['ui'][k]
165 del cfg['ui'][k]
166 for k, v in cfg.items('defaults'):
166 for k, v in cfg.items('defaults'):
167 del cfg['defaults'][k]
167 del cfg['defaults'][k]
168 # Don't remove aliases from the configuration if in the exceptionlist
168 # Don't remove aliases from the configuration if in the exceptionlist
169 if self.plain('alias'):
169 if self.plain('alias'):
170 for k, v in cfg.items('alias'):
170 for k, v in cfg.items('alias'):
171 del cfg['alias'][k]
171 del cfg['alias'][k]
172 if self.plain('revsetalias'):
172 if self.plain('revsetalias'):
173 for k, v in cfg.items('revsetalias'):
173 for k, v in cfg.items('revsetalias'):
174 del cfg['revsetalias'][k]
174 del cfg['revsetalias'][k]
175
175
176 if trusted:
176 if trusted:
177 self._tcfg.update(cfg)
177 self._tcfg.update(cfg)
178 self._tcfg.update(self._ocfg)
178 self._tcfg.update(self._ocfg)
179 self._ucfg.update(cfg)
179 self._ucfg.update(cfg)
180 self._ucfg.update(self._ocfg)
180 self._ucfg.update(self._ocfg)
181
181
182 if root is None:
182 if root is None:
183 root = os.path.expanduser('~')
183 root = os.path.expanduser('~')
184 self.fixconfig(root=root)
184 self.fixconfig(root=root)
185
185
186 def fixconfig(self, root=None, section=None):
186 def fixconfig(self, root=None, section=None):
187 if section in (None, 'paths'):
187 if section in (None, 'paths'):
188 # expand vars and ~
188 # expand vars and ~
189 # translate paths relative to root (or home) into absolute paths
189 # translate paths relative to root (or home) into absolute paths
190 root = root or os.getcwd()
190 root = root or os.getcwd()
191 for c in self._tcfg, self._ucfg, self._ocfg:
191 for c in self._tcfg, self._ucfg, self._ocfg:
192 for n, p in c.items('paths'):
192 for n, p in c.items('paths'):
193 if not p:
193 if not p:
194 continue
194 continue
195 if '%%' in p:
195 if '%%' in p:
196 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
196 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
197 % (n, p, self.configsource('paths', n)))
197 % (n, p, self.configsource('paths', n)))
198 p = p.replace('%%', '%')
198 p = p.replace('%%', '%')
199 p = util.expandpath(p)
199 p = util.expandpath(p)
200 if not util.hasscheme(p) and not os.path.isabs(p):
200 if not util.hasscheme(p) and not os.path.isabs(p):
201 p = os.path.normpath(os.path.join(root, p))
201 p = os.path.normpath(os.path.join(root, p))
202 c.set("paths", n, p)
202 c.set("paths", n, p)
203
203
204 if section in (None, 'ui'):
204 if section in (None, 'ui'):
205 # update ui options
205 # update ui options
206 self.debugflag = self.configbool('ui', 'debug')
206 self.debugflag = self.configbool('ui', 'debug')
207 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
207 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
208 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
208 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
209 if self.verbose and self.quiet:
209 if self.verbose and self.quiet:
210 self.quiet = self.verbose = False
210 self.quiet = self.verbose = False
211 self._reportuntrusted = self.debugflag or self.configbool("ui",
211 self._reportuntrusted = self.debugflag or self.configbool("ui",
212 "report_untrusted", True)
212 "report_untrusted", True)
213 self.tracebackflag = self.configbool('ui', 'traceback', False)
213 self.tracebackflag = self.configbool('ui', 'traceback', False)
214
214
215 if section in (None, 'trusted'):
215 if section in (None, 'trusted'):
216 # update trust information
216 # update trust information
217 self._trustusers.update(self.configlist('trusted', 'users'))
217 self._trustusers.update(self.configlist('trusted', 'users'))
218 self._trustgroups.update(self.configlist('trusted', 'groups'))
218 self._trustgroups.update(self.configlist('trusted', 'groups'))
219
219
220 def backupconfig(self, section, item):
220 def backupconfig(self, section, item):
221 return (self._ocfg.backup(section, item),
221 return (self._ocfg.backup(section, item),
222 self._tcfg.backup(section, item),
222 self._tcfg.backup(section, item),
223 self._ucfg.backup(section, item),)
223 self._ucfg.backup(section, item),)
224 def restoreconfig(self, data):
224 def restoreconfig(self, data):
225 self._ocfg.restore(data[0])
225 self._ocfg.restore(data[0])
226 self._tcfg.restore(data[1])
226 self._tcfg.restore(data[1])
227 self._ucfg.restore(data[2])
227 self._ucfg.restore(data[2])
228
228
229 def setconfig(self, section, name, value, source=''):
229 def setconfig(self, section, name, value, source=''):
230 for cfg in (self._ocfg, self._tcfg, self._ucfg):
230 for cfg in (self._ocfg, self._tcfg, self._ucfg):
231 cfg.set(section, name, value, source)
231 cfg.set(section, name, value, source)
232 self.fixconfig(section=section)
232 self.fixconfig(section=section)
233
233
234 def _data(self, untrusted):
234 def _data(self, untrusted):
235 return untrusted and self._ucfg or self._tcfg
235 return untrusted and self._ucfg or self._tcfg
236
236
237 def configsource(self, section, name, untrusted=False):
237 def configsource(self, section, name, untrusted=False):
238 return self._data(untrusted).source(section, name) or 'none'
238 return self._data(untrusted).source(section, name) or 'none'
239
239
240 def config(self, section, name, default=None, untrusted=False):
240 def config(self, section, name, default=None, untrusted=False):
241 if isinstance(name, list):
241 if isinstance(name, list):
242 alternates = name
242 alternates = name
243 else:
243 else:
244 alternates = [name]
244 alternates = [name]
245
245
246 for n in alternates:
246 for n in alternates:
247 value = self._data(untrusted).get(section, n, None)
247 value = self._data(untrusted).get(section, n, None)
248 if value is not None:
248 if value is not None:
249 name = n
249 name = n
250 break
250 break
251 else:
251 else:
252 value = default
252 value = default
253
253
254 if self.debugflag and not untrusted and self._reportuntrusted:
254 if self.debugflag and not untrusted and self._reportuntrusted:
255 for n in alternates:
255 for n in alternates:
256 uvalue = self._ucfg.get(section, n)
256 uvalue = self._ucfg.get(section, n)
257 if uvalue is not None and uvalue != value:
257 if uvalue is not None and uvalue != value:
258 self.debug("ignoring untrusted configuration option "
258 self.debug("ignoring untrusted configuration option "
259 "%s.%s = %s\n" % (section, n, uvalue))
259 "%s.%s = %s\n" % (section, n, uvalue))
260 return value
260 return value
261
261
262 def configpath(self, section, name, default=None, untrusted=False):
262 def configpath(self, section, name, default=None, untrusted=False):
263 'get a path config item, expanded relative to repo root or config file'
263 'get a path config item, expanded relative to repo root or config file'
264 v = self.config(section, name, default, untrusted)
264 v = self.config(section, name, default, untrusted)
265 if v is None:
265 if v is None:
266 return None
266 return None
267 if not os.path.isabs(v) or "://" not in v:
267 if not os.path.isabs(v) or "://" not in v:
268 src = self.configsource(section, name, untrusted)
268 src = self.configsource(section, name, untrusted)
269 if ':' in src:
269 if ':' in src:
270 base = os.path.dirname(src.rsplit(':')[0])
270 base = os.path.dirname(src.rsplit(':')[0])
271 v = os.path.join(base, os.path.expanduser(v))
271 v = os.path.join(base, os.path.expanduser(v))
272 return v
272 return v
273
273
274 def configbool(self, section, name, default=False, untrusted=False):
274 def configbool(self, section, name, default=False, untrusted=False):
275 """parse a configuration element as a boolean
275 """parse a configuration element as a boolean
276
276
277 >>> u = ui(); s = 'foo'
277 >>> u = ui(); s = 'foo'
278 >>> u.setconfig(s, 'true', 'yes')
278 >>> u.setconfig(s, 'true', 'yes')
279 >>> u.configbool(s, 'true')
279 >>> u.configbool(s, 'true')
280 True
280 True
281 >>> u.setconfig(s, 'false', 'no')
281 >>> u.setconfig(s, 'false', 'no')
282 >>> u.configbool(s, 'false')
282 >>> u.configbool(s, 'false')
283 False
283 False
284 >>> u.configbool(s, 'unknown')
284 >>> u.configbool(s, 'unknown')
285 False
285 False
286 >>> u.configbool(s, 'unknown', True)
286 >>> u.configbool(s, 'unknown', True)
287 True
287 True
288 >>> u.setconfig(s, 'invalid', 'somevalue')
288 >>> u.setconfig(s, 'invalid', 'somevalue')
289 >>> u.configbool(s, 'invalid')
289 >>> u.configbool(s, 'invalid')
290 Traceback (most recent call last):
290 Traceback (most recent call last):
291 ...
291 ...
292 ConfigError: foo.invalid is not a boolean ('somevalue')
292 ConfigError: foo.invalid is not a boolean ('somevalue')
293 """
293 """
294
294
295 v = self.config(section, name, None, untrusted)
295 v = self.config(section, name, None, untrusted)
296 if v is None:
296 if v is None:
297 return default
297 return default
298 if isinstance(v, bool):
298 if isinstance(v, bool):
299 return v
299 return v
300 b = util.parsebool(v)
300 b = util.parsebool(v)
301 if b is None:
301 if b is None:
302 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
302 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
303 % (section, name, v))
303 % (section, name, v))
304 return b
304 return b
305
305
306 def configint(self, section, name, default=None, untrusted=False):
306 def configint(self, section, name, default=None, untrusted=False):
307 """parse a configuration element as an integer
307 """parse a configuration element as an integer
308
308
309 >>> u = ui(); s = 'foo'
309 >>> u = ui(); s = 'foo'
310 >>> u.setconfig(s, 'int1', '42')
310 >>> u.setconfig(s, 'int1', '42')
311 >>> u.configint(s, 'int1')
311 >>> u.configint(s, 'int1')
312 42
312 42
313 >>> u.setconfig(s, 'int2', '-42')
313 >>> u.setconfig(s, 'int2', '-42')
314 >>> u.configint(s, 'int2')
314 >>> u.configint(s, 'int2')
315 -42
315 -42
316 >>> u.configint(s, 'unknown', 7)
316 >>> u.configint(s, 'unknown', 7)
317 7
317 7
318 >>> u.setconfig(s, 'invalid', 'somevalue')
318 >>> u.setconfig(s, 'invalid', 'somevalue')
319 >>> u.configint(s, 'invalid')
319 >>> u.configint(s, 'invalid')
320 Traceback (most recent call last):
320 Traceback (most recent call last):
321 ...
321 ...
322 ConfigError: foo.invalid is not an integer ('somevalue')
322 ConfigError: foo.invalid is not an integer ('somevalue')
323 """
323 """
324
324
325 v = self.config(section, name, None, untrusted)
325 v = self.config(section, name, None, untrusted)
326 if v is None:
326 if v is None:
327 return default
327 return default
328 try:
328 try:
329 return int(v)
329 return int(v)
330 except ValueError:
330 except ValueError:
331 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
331 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
332 % (section, name, v))
332 % (section, name, v))
333
333
334 def configbytes(self, section, name, default=0, untrusted=False):
334 def configbytes(self, section, name, default=0, untrusted=False):
335 """parse a configuration element as a quantity in bytes
335 """parse a configuration element as a quantity in bytes
336
336
337 Units can be specified as b (bytes), k or kb (kilobytes), m or
337 Units can be specified as b (bytes), k or kb (kilobytes), m or
338 mb (megabytes), g or gb (gigabytes).
338 mb (megabytes), g or gb (gigabytes).
339
339
340 >>> u = ui(); s = 'foo'
340 >>> u = ui(); s = 'foo'
341 >>> u.setconfig(s, 'val1', '42')
341 >>> u.setconfig(s, 'val1', '42')
342 >>> u.configbytes(s, 'val1')
342 >>> u.configbytes(s, 'val1')
343 42
343 42
344 >>> u.setconfig(s, 'val2', '42.5 kb')
344 >>> u.setconfig(s, 'val2', '42.5 kb')
345 >>> u.configbytes(s, 'val2')
345 >>> u.configbytes(s, 'val2')
346 43520
346 43520
347 >>> u.configbytes(s, 'unknown', '7 MB')
347 >>> u.configbytes(s, 'unknown', '7 MB')
348 7340032
348 7340032
349 >>> u.setconfig(s, 'invalid', 'somevalue')
349 >>> u.setconfig(s, 'invalid', 'somevalue')
350 >>> u.configbytes(s, 'invalid')
350 >>> u.configbytes(s, 'invalid')
351 Traceback (most recent call last):
351 Traceback (most recent call last):
352 ...
352 ...
353 ConfigError: foo.invalid is not a byte quantity ('somevalue')
353 ConfigError: foo.invalid is not a byte quantity ('somevalue')
354 """
354 """
355
355
356 value = self.config(section, name)
356 value = self.config(section, name)
357 if value is None:
357 if value is None:
358 if not isinstance(default, str):
358 if not isinstance(default, str):
359 return default
359 return default
360 value = default
360 value = default
361 try:
361 try:
362 return util.sizetoint(value)
362 return util.sizetoint(value)
363 except error.ParseError:
363 except error.ParseError:
364 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
364 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
365 % (section, name, value))
365 % (section, name, value))
366
366
367 def configlist(self, section, name, default=None, untrusted=False):
367 def configlist(self, section, name, default=None, untrusted=False):
368 """parse a configuration element as a list of comma/space separated
368 """parse a configuration element as a list of comma/space separated
369 strings
369 strings
370
370
371 >>> u = ui(); s = 'foo'
371 >>> u = ui(); s = 'foo'
372 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
372 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
373 >>> u.configlist(s, 'list1')
373 >>> u.configlist(s, 'list1')
374 ['this', 'is', 'a small', 'test']
374 ['this', 'is', 'a small', 'test']
375 """
375 """
376
376
377 def _parse_plain(parts, s, offset):
377 def _parse_plain(parts, s, offset):
378 whitespace = False
378 whitespace = False
379 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
379 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
380 whitespace = True
380 whitespace = True
381 offset += 1
381 offset += 1
382 if offset >= len(s):
382 if offset >= len(s):
383 return None, parts, offset
383 return None, parts, offset
384 if whitespace:
384 if whitespace:
385 parts.append('')
385 parts.append('')
386 if s[offset] == '"' and not parts[-1]:
386 if s[offset] == '"' and not parts[-1]:
387 return _parse_quote, parts, offset + 1
387 return _parse_quote, parts, offset + 1
388 elif s[offset] == '"' and parts[-1][-1] == '\\':
388 elif s[offset] == '"' and parts[-1][-1] == '\\':
389 parts[-1] = parts[-1][:-1] + s[offset]
389 parts[-1] = parts[-1][:-1] + s[offset]
390 return _parse_plain, parts, offset + 1
390 return _parse_plain, parts, offset + 1
391 parts[-1] += s[offset]
391 parts[-1] += s[offset]
392 return _parse_plain, parts, offset + 1
392 return _parse_plain, parts, offset + 1
393
393
394 def _parse_quote(parts, s, offset):
394 def _parse_quote(parts, s, offset):
395 if offset < len(s) and s[offset] == '"': # ""
395 if offset < len(s) and s[offset] == '"': # ""
396 parts.append('')
396 parts.append('')
397 offset += 1
397 offset += 1
398 while offset < len(s) and (s[offset].isspace() or
398 while offset < len(s) and (s[offset].isspace() or
399 s[offset] == ','):
399 s[offset] == ','):
400 offset += 1
400 offset += 1
401 return _parse_plain, parts, offset
401 return _parse_plain, parts, offset
402
402
403 while offset < len(s) and s[offset] != '"':
403 while offset < len(s) and s[offset] != '"':
404 if (s[offset] == '\\' and offset + 1 < len(s)
404 if (s[offset] == '\\' and offset + 1 < len(s)
405 and s[offset + 1] == '"'):
405 and s[offset + 1] == '"'):
406 offset += 1
406 offset += 1
407 parts[-1] += '"'
407 parts[-1] += '"'
408 else:
408 else:
409 parts[-1] += s[offset]
409 parts[-1] += s[offset]
410 offset += 1
410 offset += 1
411
411
412 if offset >= len(s):
412 if offset >= len(s):
413 real_parts = _configlist(parts[-1])
413 real_parts = _configlist(parts[-1])
414 if not real_parts:
414 if not real_parts:
415 parts[-1] = '"'
415 parts[-1] = '"'
416 else:
416 else:
417 real_parts[0] = '"' + real_parts[0]
417 real_parts[0] = '"' + real_parts[0]
418 parts = parts[:-1]
418 parts = parts[:-1]
419 parts.extend(real_parts)
419 parts.extend(real_parts)
420 return None, parts, offset
420 return None, parts, offset
421
421
422 offset += 1
422 offset += 1
423 while offset < len(s) and s[offset] in [' ', ',']:
423 while offset < len(s) and s[offset] in [' ', ',']:
424 offset += 1
424 offset += 1
425
425
426 if offset < len(s):
426 if offset < len(s):
427 if offset + 1 == len(s) and s[offset] == '"':
427 if offset + 1 == len(s) and s[offset] == '"':
428 parts[-1] += '"'
428 parts[-1] += '"'
429 offset += 1
429 offset += 1
430 else:
430 else:
431 parts.append('')
431 parts.append('')
432 else:
432 else:
433 return None, parts, offset
433 return None, parts, offset
434
434
435 return _parse_plain, parts, offset
435 return _parse_plain, parts, offset
436
436
437 def _configlist(s):
437 def _configlist(s):
438 s = s.rstrip(' ,')
438 s = s.rstrip(' ,')
439 if not s:
439 if not s:
440 return []
440 return []
441 parser, parts, offset = _parse_plain, [''], 0
441 parser, parts, offset = _parse_plain, [''], 0
442 while parser:
442 while parser:
443 parser, parts, offset = parser(parts, s, offset)
443 parser, parts, offset = parser(parts, s, offset)
444 return parts
444 return parts
445
445
446 result = self.config(section, name, untrusted=untrusted)
446 result = self.config(section, name, untrusted=untrusted)
447 if result is None:
447 if result is None:
448 result = default or []
448 result = default or []
449 if isinstance(result, basestring):
449 if isinstance(result, basestring):
450 result = _configlist(result.lstrip(' ,\n'))
450 result = _configlist(result.lstrip(' ,\n'))
451 if result is None:
451 if result is None:
452 result = default or []
452 result = default or []
453 return result
453 return result
454
454
455 def has_section(self, section, untrusted=False):
455 def has_section(self, section, untrusted=False):
456 '''tell whether section exists in config.'''
456 '''tell whether section exists in config.'''
457 return section in self._data(untrusted)
457 return section in self._data(untrusted)
458
458
459 def configitems(self, section, untrusted=False):
459 def configitems(self, section, untrusted=False):
460 items = self._data(untrusted).items(section)
460 items = self._data(untrusted).items(section)
461 if self.debugflag and not untrusted and self._reportuntrusted:
461 if self.debugflag and not untrusted and self._reportuntrusted:
462 for k, v in self._ucfg.items(section):
462 for k, v in self._ucfg.items(section):
463 if self._tcfg.get(section, k) != v:
463 if self._tcfg.get(section, k) != v:
464 self.debug("ignoring untrusted configuration option "
464 self.debug("ignoring untrusted configuration option "
465 "%s.%s = %s\n" % (section, k, v))
465 "%s.%s = %s\n" % (section, k, v))
466 return items
466 return items
467
467
468 def walkconfig(self, untrusted=False):
468 def walkconfig(self, untrusted=False):
469 cfg = self._data(untrusted)
469 cfg = self._data(untrusted)
470 for section in cfg.sections():
470 for section in cfg.sections():
471 for name, value in self.configitems(section, untrusted):
471 for name, value in self.configitems(section, untrusted):
472 yield section, name, value
472 yield section, name, value
473
473
474 def plain(self, feature=None):
474 def plain(self, feature=None):
475 '''is plain mode active?
475 '''is plain mode active?
476
476
477 Plain mode means that all configuration variables which affect
477 Plain mode means that all configuration variables which affect
478 the behavior and output of Mercurial should be
478 the behavior and output of Mercurial should be
479 ignored. Additionally, the output should be stable,
479 ignored. Additionally, the output should be stable,
480 reproducible and suitable for use in scripts or applications.
480 reproducible and suitable for use in scripts or applications.
481
481
482 The only way to trigger plain mode is by setting either the
482 The only way to trigger plain mode is by setting either the
483 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
483 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
484
484
485 The return value can either be
485 The return value can either be
486 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
486 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
487 - True otherwise
487 - True otherwise
488 '''
488 '''
489 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
489 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
490 return False
490 return False
491 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
491 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
492 if feature and exceptions:
492 if feature and exceptions:
493 return feature not in exceptions
493 return feature not in exceptions
494 return True
494 return True
495
495
496 def username(self):
496 def username(self):
497 """Return default username to be used in commits.
497 """Return default username to be used in commits.
498
498
499 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
499 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
500 and stop searching if one of these is set.
500 and stop searching if one of these is set.
501 If not found and ui.askusername is True, ask the user, else use
501 If not found and ui.askusername is True, ask the user, else use
502 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
502 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
503 """
503 """
504 user = os.environ.get("HGUSER")
504 user = os.environ.get("HGUSER")
505 if user is None:
505 if user is None:
506 user = self.config("ui", ["username", "user"])
506 user = self.config("ui", ["username", "user"])
507 if user is not None:
507 if user is not None:
508 user = os.path.expandvars(user)
508 user = os.path.expandvars(user)
509 if user is None:
509 if user is None:
510 user = os.environ.get("EMAIL")
510 user = os.environ.get("EMAIL")
511 if user is None and self.configbool("ui", "askusername"):
511 if user is None and self.configbool("ui", "askusername"):
512 user = self.prompt(_("enter a commit username:"), default=None)
512 user = self.prompt(_("enter a commit username:"), default=None)
513 if user is None and not self.interactive():
513 if user is None and not self.interactive():
514 try:
514 try:
515 user = '%s@%s' % (util.getuser(), socket.getfqdn())
515 user = '%s@%s' % (util.getuser(), socket.getfqdn())
516 self.warn(_("no username found, using '%s' instead\n") % user)
516 self.warn(_("no username found, using '%s' instead\n") % user)
517 except KeyError:
517 except KeyError:
518 pass
518 pass
519 if not user:
519 if not user:
520 raise util.Abort(_('no username supplied'),
520 raise util.Abort(_('no username supplied'),
521 hint=_('use "hg config --edit" '
521 hint=_('use "hg config --edit" '
522 'to set your username'))
522 'to set your username'))
523 if "\n" in user:
523 if "\n" in user:
524 raise util.Abort(_("username %s contains a newline\n") % repr(user))
524 raise util.Abort(_("username %s contains a newline\n") % repr(user))
525 return user
525 return user
526
526
527 def shortuser(self, user):
527 def shortuser(self, user):
528 """Return a short representation of a user name or email address."""
528 """Return a short representation of a user name or email address."""
529 if not self.verbose:
529 if not self.verbose:
530 user = util.shortuser(user)
530 user = util.shortuser(user)
531 return user
531 return user
532
532
533 def expandpath(self, loc, default=None):
533 def expandpath(self, loc, default=None):
534 """Return repository location relative to cwd or from [paths]"""
534 """Return repository location relative to cwd or from [paths]"""
535 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
535 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
536 return loc
536 return loc
537
537
538 p = self.paths.getpath(loc, default=default)
538 p = self.paths.getpath(loc, default=default)
539 if p:
539 if p:
540 return p.loc
540 return p.loc
541 return loc
541 return loc
542
542
543 @util.propertycache
543 @util.propertycache
544 def paths(self):
544 def paths(self):
545 return paths(self)
545 return paths(self)
546
546
547 def pushbuffer(self, error=False, subproc=False):
547 def pushbuffer(self, error=False, subproc=False):
548 """install a buffer to capture standard output of the ui object
548 """install a buffer to capture standard output of the ui object
549
549
550 If error is True, the error output will be captured too.
550 If error is True, the error output will be captured too.
551
551
552 If subproc is True, output from subprocesses (typically hooks) will be
552 If subproc is True, output from subprocesses (typically hooks) will be
553 captured too."""
553 captured too."""
554 self._buffers.append([])
554 self._buffers.append([])
555 self._bufferstates.append((error, subproc))
555 self._bufferstates.append((error, subproc))
556
556
557 def popbuffer(self, labeled=False):
557 def popbuffer(self, labeled=False):
558 '''pop the last buffer and return the buffered output
558 '''pop the last buffer and return the buffered output
559
559
560 If labeled is True, any labels associated with buffered
560 If labeled is True, any labels associated with buffered
561 output will be handled. By default, this has no effect
561 output will be handled. By default, this has no effect
562 on the output returned, but extensions and GUI tools may
562 on the output returned, but extensions and GUI tools may
563 handle this argument and returned styled output. If output
563 handle this argument and returned styled output. If output
564 is being buffered so it can be captured and parsed or
564 is being buffered so it can be captured and parsed or
565 processed, labeled should not be set to True.
565 processed, labeled should not be set to True.
566 '''
566 '''
567 self._bufferstates.pop()
567 self._bufferstates.pop()
568 return "".join(self._buffers.pop())
568 return "".join(self._buffers.pop())
569
569
570 def write(self, *args, **opts):
570 def write(self, *args, **opts):
571 '''write args to output
571 '''write args to output
572
572
573 By default, this method simply writes to the buffer or stdout,
573 By default, this method simply writes to the buffer or stdout,
574 but extensions or GUI tools may override this method,
574 but extensions or GUI tools may override this method,
575 write_err(), popbuffer(), and label() to style output from
575 write_err(), popbuffer(), and label() to style output from
576 various parts of hg.
576 various parts of hg.
577
577
578 An optional keyword argument, "label", can be passed in.
578 An optional keyword argument, "label", can be passed in.
579 This should be a string containing label names separated by
579 This should be a string containing label names separated by
580 space. Label names take the form of "topic.type". For example,
580 space. Label names take the form of "topic.type". For example,
581 ui.debug() issues a label of "ui.debug".
581 ui.debug() issues a label of "ui.debug".
582
582
583 When labeling output for a specific command, a label of
583 When labeling output for a specific command, a label of
584 "cmdname.type" is recommended. For example, status issues
584 "cmdname.type" is recommended. For example, status issues
585 a label of "status.modified" for modified files.
585 a label of "status.modified" for modified files.
586 '''
586 '''
587 self._progclear()
587 if self._buffers:
588 if self._buffers:
588 self._buffers[-1].extend([str(a) for a in args])
589 self._buffers[-1].extend([str(a) for a in args])
589 else:
590 else:
590 for a in args:
591 for a in args:
591 self.fout.write(str(a))
592 self.fout.write(str(a))
592
593
593 def write_err(self, *args, **opts):
594 def write_err(self, *args, **opts):
595 self._progclear()
594 try:
596 try:
595 if self._bufferstates and self._bufferstates[-1][0]:
597 if self._bufferstates and self._bufferstates[-1][0]:
596 return self.write(*args, **opts)
598 return self.write(*args, **opts)
597 if not getattr(self.fout, 'closed', False):
599 if not getattr(self.fout, 'closed', False):
598 self.fout.flush()
600 self.fout.flush()
599 for a in args:
601 for a in args:
600 self.ferr.write(str(a))
602 self.ferr.write(str(a))
601 # stderr may be buffered under win32 when redirected to files,
603 # stderr may be buffered under win32 when redirected to files,
602 # including stdout.
604 # including stdout.
603 if not getattr(self.ferr, 'closed', False):
605 if not getattr(self.ferr, 'closed', False):
604 self.ferr.flush()
606 self.ferr.flush()
605 except IOError, inst:
607 except IOError, inst:
606 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
608 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
607 raise
609 raise
608
610
609 def flush(self):
611 def flush(self):
610 try: self.fout.flush()
612 try: self.fout.flush()
611 except (IOError, ValueError): pass
613 except (IOError, ValueError): pass
612 try: self.ferr.flush()
614 try: self.ferr.flush()
613 except (IOError, ValueError): pass
615 except (IOError, ValueError): pass
614
616
615 def _isatty(self, fh):
617 def _isatty(self, fh):
616 if self.configbool('ui', 'nontty', False):
618 if self.configbool('ui', 'nontty', False):
617 return False
619 return False
618 return util.isatty(fh)
620 return util.isatty(fh)
619
621
620 def interactive(self):
622 def interactive(self):
621 '''is interactive input allowed?
623 '''is interactive input allowed?
622
624
623 An interactive session is a session where input can be reasonably read
625 An interactive session is a session where input can be reasonably read
624 from `sys.stdin'. If this function returns false, any attempt to read
626 from `sys.stdin'. If this function returns false, any attempt to read
625 from stdin should fail with an error, unless a sensible default has been
627 from stdin should fail with an error, unless a sensible default has been
626 specified.
628 specified.
627
629
628 Interactiveness is triggered by the value of the `ui.interactive'
630 Interactiveness is triggered by the value of the `ui.interactive'
629 configuration variable or - if it is unset - when `sys.stdin' points
631 configuration variable or - if it is unset - when `sys.stdin' points
630 to a terminal device.
632 to a terminal device.
631
633
632 This function refers to input only; for output, see `ui.formatted()'.
634 This function refers to input only; for output, see `ui.formatted()'.
633 '''
635 '''
634 i = self.configbool("ui", "interactive", None)
636 i = self.configbool("ui", "interactive", None)
635 if i is None:
637 if i is None:
636 # some environments replace stdin without implementing isatty
638 # some environments replace stdin without implementing isatty
637 # usually those are non-interactive
639 # usually those are non-interactive
638 return self._isatty(self.fin)
640 return self._isatty(self.fin)
639
641
640 return i
642 return i
641
643
642 def termwidth(self):
644 def termwidth(self):
643 '''how wide is the terminal in columns?
645 '''how wide is the terminal in columns?
644 '''
646 '''
645 if 'COLUMNS' in os.environ:
647 if 'COLUMNS' in os.environ:
646 try:
648 try:
647 return int(os.environ['COLUMNS'])
649 return int(os.environ['COLUMNS'])
648 except ValueError:
650 except ValueError:
649 pass
651 pass
650 return util.termwidth()
652 return util.termwidth()
651
653
652 def formatted(self):
654 def formatted(self):
653 '''should formatted output be used?
655 '''should formatted output be used?
654
656
655 It is often desirable to format the output to suite the output medium.
657 It is often desirable to format the output to suite the output medium.
656 Examples of this are truncating long lines or colorizing messages.
658 Examples of this are truncating long lines or colorizing messages.
657 However, this is not often not desirable when piping output into other
659 However, this is not often not desirable when piping output into other
658 utilities, e.g. `grep'.
660 utilities, e.g. `grep'.
659
661
660 Formatted output is triggered by the value of the `ui.formatted'
662 Formatted output is triggered by the value of the `ui.formatted'
661 configuration variable or - if it is unset - when `sys.stdout' points
663 configuration variable or - if it is unset - when `sys.stdout' points
662 to a terminal device. Please note that `ui.formatted' should be
664 to a terminal device. Please note that `ui.formatted' should be
663 considered an implementation detail; it is not intended for use outside
665 considered an implementation detail; it is not intended for use outside
664 Mercurial or its extensions.
666 Mercurial or its extensions.
665
667
666 This function refers to output only; for input, see `ui.interactive()'.
668 This function refers to output only; for input, see `ui.interactive()'.
667 This function always returns false when in plain mode, see `ui.plain()'.
669 This function always returns false when in plain mode, see `ui.plain()'.
668 '''
670 '''
669 if self.plain():
671 if self.plain():
670 return False
672 return False
671
673
672 i = self.configbool("ui", "formatted", None)
674 i = self.configbool("ui", "formatted", None)
673 if i is None:
675 if i is None:
674 # some environments replace stdout without implementing isatty
676 # some environments replace stdout without implementing isatty
675 # usually those are non-interactive
677 # usually those are non-interactive
676 return self._isatty(self.fout)
678 return self._isatty(self.fout)
677
679
678 return i
680 return i
679
681
680 def _readline(self, prompt=''):
682 def _readline(self, prompt=''):
681 if self._isatty(self.fin):
683 if self._isatty(self.fin):
682 try:
684 try:
683 # magically add command line editing support, where
685 # magically add command line editing support, where
684 # available
686 # available
685 import readline
687 import readline
686 # force demandimport to really load the module
688 # force demandimport to really load the module
687 readline.read_history_file
689 readline.read_history_file
688 # windows sometimes raises something other than ImportError
690 # windows sometimes raises something other than ImportError
689 except Exception:
691 except Exception:
690 pass
692 pass
691
693
692 # call write() so output goes through subclassed implementation
694 # call write() so output goes through subclassed implementation
693 # e.g. color extension on Windows
695 # e.g. color extension on Windows
694 self.write(prompt)
696 self.write(prompt)
695
697
696 # instead of trying to emulate raw_input, swap (self.fin,
698 # instead of trying to emulate raw_input, swap (self.fin,
697 # self.fout) with (sys.stdin, sys.stdout)
699 # self.fout) with (sys.stdin, sys.stdout)
698 oldin = sys.stdin
700 oldin = sys.stdin
699 oldout = sys.stdout
701 oldout = sys.stdout
700 sys.stdin = self.fin
702 sys.stdin = self.fin
701 sys.stdout = self.fout
703 sys.stdout = self.fout
702 # prompt ' ' must exist; otherwise readline may delete entire line
704 # prompt ' ' must exist; otherwise readline may delete entire line
703 # - http://bugs.python.org/issue12833
705 # - http://bugs.python.org/issue12833
704 line = raw_input(' ')
706 line = raw_input(' ')
705 sys.stdin = oldin
707 sys.stdin = oldin
706 sys.stdout = oldout
708 sys.stdout = oldout
707
709
708 # When stdin is in binary mode on Windows, it can cause
710 # When stdin is in binary mode on Windows, it can cause
709 # raw_input() to emit an extra trailing carriage return
711 # raw_input() to emit an extra trailing carriage return
710 if os.linesep == '\r\n' and line and line[-1] == '\r':
712 if os.linesep == '\r\n' and line and line[-1] == '\r':
711 line = line[:-1]
713 line = line[:-1]
712 return line
714 return line
713
715
714 def prompt(self, msg, default="y"):
716 def prompt(self, msg, default="y"):
715 """Prompt user with msg, read response.
717 """Prompt user with msg, read response.
716 If ui is not interactive, the default is returned.
718 If ui is not interactive, the default is returned.
717 """
719 """
718 if not self.interactive():
720 if not self.interactive():
719 self.write(msg, ' ', default, "\n")
721 self.write(msg, ' ', default, "\n")
720 return default
722 return default
721 try:
723 try:
722 r = self._readline(self.label(msg, 'ui.prompt'))
724 r = self._readline(self.label(msg, 'ui.prompt'))
723 if not r:
725 if not r:
724 r = default
726 r = default
725 if self.configbool('ui', 'promptecho'):
727 if self.configbool('ui', 'promptecho'):
726 self.write(r, "\n")
728 self.write(r, "\n")
727 return r
729 return r
728 except EOFError:
730 except EOFError:
729 raise util.Abort(_('response expected'))
731 raise util.Abort(_('response expected'))
730
732
731 @staticmethod
733 @staticmethod
732 def extractchoices(prompt):
734 def extractchoices(prompt):
733 """Extract prompt message and list of choices from specified prompt.
735 """Extract prompt message and list of choices from specified prompt.
734
736
735 This returns tuple "(message, choices)", and "choices" is the
737 This returns tuple "(message, choices)", and "choices" is the
736 list of tuple "(response character, text without &)".
738 list of tuple "(response character, text without &)".
737 """
739 """
738 parts = prompt.split('$$')
740 parts = prompt.split('$$')
739 msg = parts[0].rstrip(' ')
741 msg = parts[0].rstrip(' ')
740 choices = [p.strip(' ') for p in parts[1:]]
742 choices = [p.strip(' ') for p in parts[1:]]
741 return (msg,
743 return (msg,
742 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
744 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
743 for s in choices])
745 for s in choices])
744
746
745 def promptchoice(self, prompt, default=0):
747 def promptchoice(self, prompt, default=0):
746 """Prompt user with a message, read response, and ensure it matches
748 """Prompt user with a message, read response, and ensure it matches
747 one of the provided choices. The prompt is formatted as follows:
749 one of the provided choices. The prompt is formatted as follows:
748
750
749 "would you like fries with that (Yn)? $$ &Yes $$ &No"
751 "would you like fries with that (Yn)? $$ &Yes $$ &No"
750
752
751 The index of the choice is returned. Responses are case
753 The index of the choice is returned. Responses are case
752 insensitive. If ui is not interactive, the default is
754 insensitive. If ui is not interactive, the default is
753 returned.
755 returned.
754 """
756 """
755
757
756 msg, choices = self.extractchoices(prompt)
758 msg, choices = self.extractchoices(prompt)
757 resps = [r for r, t in choices]
759 resps = [r for r, t in choices]
758 while True:
760 while True:
759 r = self.prompt(msg, resps[default])
761 r = self.prompt(msg, resps[default])
760 if r.lower() in resps:
762 if r.lower() in resps:
761 return resps.index(r.lower())
763 return resps.index(r.lower())
762 self.write(_("unrecognized response\n"))
764 self.write(_("unrecognized response\n"))
763
765
764 def getpass(self, prompt=None, default=None):
766 def getpass(self, prompt=None, default=None):
765 if not self.interactive():
767 if not self.interactive():
766 return default
768 return default
767 try:
769 try:
768 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
770 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
769 # disable getpass() only if explicitly specified. it's still valid
771 # disable getpass() only if explicitly specified. it's still valid
770 # to interact with tty even if fin is not a tty.
772 # to interact with tty even if fin is not a tty.
771 if self.configbool('ui', 'nontty'):
773 if self.configbool('ui', 'nontty'):
772 return self.fin.readline().rstrip('\n')
774 return self.fin.readline().rstrip('\n')
773 else:
775 else:
774 return getpass.getpass('')
776 return getpass.getpass('')
775 except EOFError:
777 except EOFError:
776 raise util.Abort(_('response expected'))
778 raise util.Abort(_('response expected'))
777 def status(self, *msg, **opts):
779 def status(self, *msg, **opts):
778 '''write status message to output (if ui.quiet is False)
780 '''write status message to output (if ui.quiet is False)
779
781
780 This adds an output label of "ui.status".
782 This adds an output label of "ui.status".
781 '''
783 '''
782 if not self.quiet:
784 if not self.quiet:
783 opts['label'] = opts.get('label', '') + ' ui.status'
785 opts['label'] = opts.get('label', '') + ' ui.status'
784 self.write(*msg, **opts)
786 self.write(*msg, **opts)
785 def warn(self, *msg, **opts):
787 def warn(self, *msg, **opts):
786 '''write warning message to output (stderr)
788 '''write warning message to output (stderr)
787
789
788 This adds an output label of "ui.warning".
790 This adds an output label of "ui.warning".
789 '''
791 '''
790 opts['label'] = opts.get('label', '') + ' ui.warning'
792 opts['label'] = opts.get('label', '') + ' ui.warning'
791 self.write_err(*msg, **opts)
793 self.write_err(*msg, **opts)
792 def note(self, *msg, **opts):
794 def note(self, *msg, **opts):
793 '''write note to output (if ui.verbose is True)
795 '''write note to output (if ui.verbose is True)
794
796
795 This adds an output label of "ui.note".
797 This adds an output label of "ui.note".
796 '''
798 '''
797 if self.verbose:
799 if self.verbose:
798 opts['label'] = opts.get('label', '') + ' ui.note'
800 opts['label'] = opts.get('label', '') + ' ui.note'
799 self.write(*msg, **opts)
801 self.write(*msg, **opts)
800 def debug(self, *msg, **opts):
802 def debug(self, *msg, **opts):
801 '''write debug message to output (if ui.debugflag is True)
803 '''write debug message to output (if ui.debugflag is True)
802
804
803 This adds an output label of "ui.debug".
805 This adds an output label of "ui.debug".
804 '''
806 '''
805 if self.debugflag:
807 if self.debugflag:
806 opts['label'] = opts.get('label', '') + ' ui.debug'
808 opts['label'] = opts.get('label', '') + ' ui.debug'
807 self.write(*msg, **opts)
809 self.write(*msg, **opts)
808 def edit(self, text, user, extra={}, editform=None):
810 def edit(self, text, user, extra={}, editform=None):
809 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
811 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
810 text=True)
812 text=True)
811 try:
813 try:
812 f = os.fdopen(fd, "w")
814 f = os.fdopen(fd, "w")
813 f.write(text)
815 f.write(text)
814 f.close()
816 f.close()
815
817
816 environ = {'HGUSER': user}
818 environ = {'HGUSER': user}
817 if 'transplant_source' in extra:
819 if 'transplant_source' in extra:
818 environ.update({'HGREVISION': hex(extra['transplant_source'])})
820 environ.update({'HGREVISION': hex(extra['transplant_source'])})
819 for label in ('intermediate-source', 'source', 'rebase_source'):
821 for label in ('intermediate-source', 'source', 'rebase_source'):
820 if label in extra:
822 if label in extra:
821 environ.update({'HGREVISION': extra[label]})
823 environ.update({'HGREVISION': extra[label]})
822 break
824 break
823 if editform:
825 if editform:
824 environ.update({'HGEDITFORM': editform})
826 environ.update({'HGEDITFORM': editform})
825
827
826 editor = self.geteditor()
828 editor = self.geteditor()
827
829
828 self.system("%s \"%s\"" % (editor, name),
830 self.system("%s \"%s\"" % (editor, name),
829 environ=environ,
831 environ=environ,
830 onerr=util.Abort, errprefix=_("edit failed"))
832 onerr=util.Abort, errprefix=_("edit failed"))
831
833
832 f = open(name)
834 f = open(name)
833 t = f.read()
835 t = f.read()
834 f.close()
836 f.close()
835 finally:
837 finally:
836 os.unlink(name)
838 os.unlink(name)
837
839
838 return t
840 return t
839
841
840 def system(self, cmd, environ={}, cwd=None, onerr=None, errprefix=None):
842 def system(self, cmd, environ={}, cwd=None, onerr=None, errprefix=None):
841 '''execute shell command with appropriate output stream. command
843 '''execute shell command with appropriate output stream. command
842 output will be redirected if fout is not stdout.
844 output will be redirected if fout is not stdout.
843 '''
845 '''
844 out = self.fout
846 out = self.fout
845 if any(s[1] for s in self._bufferstates):
847 if any(s[1] for s in self._bufferstates):
846 out = self
848 out = self
847 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
849 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
848 errprefix=errprefix, out=out)
850 errprefix=errprefix, out=out)
849
851
850 def traceback(self, exc=None, force=False):
852 def traceback(self, exc=None, force=False):
851 '''print exception traceback if traceback printing enabled or forced.
853 '''print exception traceback if traceback printing enabled or forced.
852 only to call in exception handler. returns true if traceback
854 only to call in exception handler. returns true if traceback
853 printed.'''
855 printed.'''
854 if self.tracebackflag or force:
856 if self.tracebackflag or force:
855 if exc is None:
857 if exc is None:
856 exc = sys.exc_info()
858 exc = sys.exc_info()
857 cause = getattr(exc[1], 'cause', None)
859 cause = getattr(exc[1], 'cause', None)
858
860
859 if cause is not None:
861 if cause is not None:
860 causetb = traceback.format_tb(cause[2])
862 causetb = traceback.format_tb(cause[2])
861 exctb = traceback.format_tb(exc[2])
863 exctb = traceback.format_tb(exc[2])
862 exconly = traceback.format_exception_only(cause[0], cause[1])
864 exconly = traceback.format_exception_only(cause[0], cause[1])
863
865
864 # exclude frame where 'exc' was chained and rethrown from exctb
866 # exclude frame where 'exc' was chained and rethrown from exctb
865 self.write_err('Traceback (most recent call last):\n',
867 self.write_err('Traceback (most recent call last):\n',
866 ''.join(exctb[:-1]),
868 ''.join(exctb[:-1]),
867 ''.join(causetb),
869 ''.join(causetb),
868 ''.join(exconly))
870 ''.join(exconly))
869 else:
871 else:
870 self.flush() # flush debug or status message
872 self.flush() # flush debug or status message
871 traceback.print_exception(exc[0], exc[1], exc[2],
873 traceback.print_exception(exc[0], exc[1], exc[2],
872 file=self.ferr)
874 file=self.ferr)
873 return self.tracebackflag or force
875 return self.tracebackflag or force
874
876
875 def geteditor(self):
877 def geteditor(self):
876 '''return editor to use'''
878 '''return editor to use'''
877 if sys.platform == 'plan9':
879 if sys.platform == 'plan9':
878 # vi is the MIPS instruction simulator on Plan 9. We
880 # vi is the MIPS instruction simulator on Plan 9. We
879 # instead default to E to plumb commit messages to
881 # instead default to E to plumb commit messages to
880 # avoid confusion.
882 # avoid confusion.
881 editor = 'E'
883 editor = 'E'
882 else:
884 else:
883 editor = 'vi'
885 editor = 'vi'
884 return (os.environ.get("HGEDITOR") or
886 return (os.environ.get("HGEDITOR") or
885 self.config("ui", "editor") or
887 self.config("ui", "editor") or
886 os.environ.get("VISUAL") or
888 os.environ.get("VISUAL") or
887 os.environ.get("EDITOR", editor))
889 os.environ.get("EDITOR", editor))
888
890
891 @util.propertycache
892 def _progbar(self):
893 """setup the progbar singleton to the ui object"""
894 if (self.quiet or self.debugflag
895 or self.configbool('progress', 'disable', True)
896 or not progress.shouldprint(self)):
897 return None
898 return getprogbar(self)
899
900 def _progclear(self):
901 """clear progress bar output if any. use it before any output"""
902 if '_progbar' not in vars(self): # nothing loadef yet
903 return
904 if self._progbar is not None and self._progbar.printed:
905 self._progbar.clear()
906
889 def progress(self, topic, pos, item="", unit="", total=None):
907 def progress(self, topic, pos, item="", unit="", total=None):
890 '''show a progress message
908 '''show a progress message
891
909
892 With stock hg, this is simply a debug message that is hidden
910 With stock hg, this is simply a debug message that is hidden
893 by default, but with extensions or GUI tools it may be
911 by default, but with extensions or GUI tools it may be
894 visible. 'topic' is the current operation, 'item' is a
912 visible. 'topic' is the current operation, 'item' is a
895 non-numeric marker of the current position (i.e. the currently
913 non-numeric marker of the current position (i.e. the currently
896 in-process file), 'pos' is the current numeric position (i.e.
914 in-process file), 'pos' is the current numeric position (i.e.
897 revision, bytes, etc.), unit is a corresponding unit label,
915 revision, bytes, etc.), unit is a corresponding unit label,
898 and total is the highest expected pos.
916 and total is the highest expected pos.
899
917
900 Multiple nested topics may be active at a time.
918 Multiple nested topics may be active at a time.
901
919
902 All topics should be marked closed by setting pos to None at
920 All topics should be marked closed by setting pos to None at
903 termination.
921 termination.
904 '''
922 '''
905
923 if self._progbar is not None:
924 self._progbar.progress(topic, pos, item=item, unit=unit,
925 total=total)
906 if pos is None or not self.configbool('progress', 'debug'):
926 if pos is None or not self.configbool('progress', 'debug'):
907 return
927 return
908
928
909 if unit:
929 if unit:
910 unit = ' ' + unit
930 unit = ' ' + unit
911 if item:
931 if item:
912 item = ' ' + item
932 item = ' ' + item
913
933
914 if total:
934 if total:
915 pct = 100.0 * pos / total
935 pct = 100.0 * pos / total
916 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
936 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
917 % (topic, item, pos, total, unit, pct))
937 % (topic, item, pos, total, unit, pct))
918 else:
938 else:
919 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
939 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
920
940
921 def log(self, service, *msg, **opts):
941 def log(self, service, *msg, **opts):
922 '''hook for logging facility extensions
942 '''hook for logging facility extensions
923
943
924 service should be a readily-identifiable subsystem, which will
944 service should be a readily-identifiable subsystem, which will
925 allow filtering.
945 allow filtering.
926 message should be a newline-terminated string to log.
946 message should be a newline-terminated string to log.
927 '''
947 '''
928 pass
948 pass
929
949
930 def label(self, msg, label):
950 def label(self, msg, label):
931 '''style msg based on supplied label
951 '''style msg based on supplied label
932
952
933 Like ui.write(), this just returns msg unchanged, but extensions
953 Like ui.write(), this just returns msg unchanged, but extensions
934 and GUI tools can override it to allow styling output without
954 and GUI tools can override it to allow styling output without
935 writing it.
955 writing it.
936
956
937 ui.write(s, 'label') is equivalent to
957 ui.write(s, 'label') is equivalent to
938 ui.write(ui.label(s, 'label')).
958 ui.write(ui.label(s, 'label')).
939 '''
959 '''
940 return msg
960 return msg
941
961
942 class paths(dict):
962 class paths(dict):
943 """Represents a collection of paths and their configs.
963 """Represents a collection of paths and their configs.
944
964
945 Data is initially derived from ui instances and the config files they have
965 Data is initially derived from ui instances and the config files they have
946 loaded.
966 loaded.
947 """
967 """
948 def __init__(self, ui):
968 def __init__(self, ui):
949 dict.__init__(self)
969 dict.__init__(self)
950
970
951 for name, loc in ui.configitems('paths'):
971 for name, loc in ui.configitems('paths'):
952 # No location is the same as not existing.
972 # No location is the same as not existing.
953 if not loc:
973 if not loc:
954 continue
974 continue
955 self[name] = path(name, rawloc=loc)
975 self[name] = path(name, rawloc=loc)
956
976
957 def getpath(self, name, default=None):
977 def getpath(self, name, default=None):
958 """Return a ``path`` for the specified name, falling back to a default.
978 """Return a ``path`` for the specified name, falling back to a default.
959
979
960 Returns the first of ``name`` or ``default`` that is present, or None
980 Returns the first of ``name`` or ``default`` that is present, or None
961 if neither is present.
981 if neither is present.
962 """
982 """
963 try:
983 try:
964 return self[name]
984 return self[name]
965 except KeyError:
985 except KeyError:
966 if default is not None:
986 if default is not None:
967 try:
987 try:
968 return self[default]
988 return self[default]
969 except KeyError:
989 except KeyError:
970 pass
990 pass
971
991
972 return None
992 return None
973
993
974 class path(object):
994 class path(object):
975 """Represents an individual path and its configuration."""
995 """Represents an individual path and its configuration."""
976
996
977 def __init__(self, name, rawloc=None):
997 def __init__(self, name, rawloc=None):
978 """Construct a path from its config options.
998 """Construct a path from its config options.
979
999
980 ``name`` is the symbolic name of the path.
1000 ``name`` is the symbolic name of the path.
981 ``rawloc`` is the raw location, as defined in the config.
1001 ``rawloc`` is the raw location, as defined in the config.
982 """
1002 """
983 self.name = name
1003 self.name = name
984 # We'll do more intelligent things with rawloc in the future.
1004 # We'll do more intelligent things with rawloc in the future.
985 self.loc = rawloc
1005 self.loc = rawloc
986
1006
987 # we instantiate one globally shared progress bar to avoid
1007 # we instantiate one globally shared progress bar to avoid
988 # competing progress bars when multiple UI objects get created
1008 # competing progress bars when multiple UI objects get created
989 _progresssingleton = None
1009 _progresssingleton = None
990
1010
991 def getprogbar(ui):
1011 def getprogbar(ui):
992 global _progresssingleton
1012 global _progresssingleton
993 if _progresssingleton is None:
1013 if _progresssingleton is None:
994 # passing 'ui' object to the singleton is fishy,
1014 # passing 'ui' object to the singleton is fishy,
995 # this is how the extension used to work but feel free to rework it.
1015 # this is how the extension used to work but feel free to rework it.
996 _progresssingleton = progress.progbar(ui)
1016 _progresssingleton = progress.progbar(ui)
997 return _progresssingleton
1017 return _progresssingleton
@@ -1,572 +1,573 b''
1 Create test repository:
1 Create test repository:
2
2
3 $ hg init repo
3 $ hg init repo
4 $ cd repo
4 $ cd repo
5 $ echo x1 > x.txt
5 $ echo x1 > x.txt
6
6
7 $ hg init foo
7 $ hg init foo
8 $ cd foo
8 $ cd foo
9 $ echo y1 > y.txt
9 $ echo y1 > y.txt
10
10
11 $ hg init bar
11 $ hg init bar
12 $ cd bar
12 $ cd bar
13 $ echo z1 > z.txt
13 $ echo z1 > z.txt
14
14
15 $ cd ..
15 $ cd ..
16 $ echo 'bar = bar' > .hgsub
16 $ echo 'bar = bar' > .hgsub
17
17
18 $ cd ..
18 $ cd ..
19 $ echo 'foo = foo' > .hgsub
19 $ echo 'foo = foo' > .hgsub
20
20
21 Add files --- .hgsub files must go first to trigger subrepos:
21 Add files --- .hgsub files must go first to trigger subrepos:
22
22
23 $ hg add -S .hgsub
23 $ hg add -S .hgsub
24 $ hg add -S foo/.hgsub
24 $ hg add -S foo/.hgsub
25 $ hg add -S foo/bar
25 $ hg add -S foo/bar
26 adding foo/bar/z.txt (glob)
26 adding foo/bar/z.txt (glob)
27 $ hg add -S
27 $ hg add -S
28 adding x.txt
28 adding x.txt
29 adding foo/y.txt (glob)
29 adding foo/y.txt (glob)
30
30
31 Test recursive status without committing anything:
31 Test recursive status without committing anything:
32
32
33 $ hg status -S
33 $ hg status -S
34 A .hgsub
34 A .hgsub
35 A foo/.hgsub
35 A foo/.hgsub
36 A foo/bar/z.txt
36 A foo/bar/z.txt
37 A foo/y.txt
37 A foo/y.txt
38 A x.txt
38 A x.txt
39
39
40 Test recursive diff without committing anything:
40 Test recursive diff without committing anything:
41
41
42 $ hg diff --nodates -S foo
42 $ hg diff --nodates -S foo
43 diff -r 000000000000 foo/.hgsub
43 diff -r 000000000000 foo/.hgsub
44 --- /dev/null
44 --- /dev/null
45 +++ b/foo/.hgsub
45 +++ b/foo/.hgsub
46 @@ -0,0 +1,1 @@
46 @@ -0,0 +1,1 @@
47 +bar = bar
47 +bar = bar
48 diff -r 000000000000 foo/y.txt
48 diff -r 000000000000 foo/y.txt
49 --- /dev/null
49 --- /dev/null
50 +++ b/foo/y.txt
50 +++ b/foo/y.txt
51 @@ -0,0 +1,1 @@
51 @@ -0,0 +1,1 @@
52 +y1
52 +y1
53 diff -r 000000000000 foo/bar/z.txt
53 diff -r 000000000000 foo/bar/z.txt
54 --- /dev/null
54 --- /dev/null
55 +++ b/foo/bar/z.txt
55 +++ b/foo/bar/z.txt
56 @@ -0,0 +1,1 @@
56 @@ -0,0 +1,1 @@
57 +z1
57 +z1
58
58
59 Commits:
59 Commits:
60
60
61 $ hg commit -m fails
61 $ hg commit -m fails
62 abort: uncommitted changes in subrepository 'foo'
62 abort: uncommitted changes in subrepository 'foo'
63 (use --subrepos for recursive commit)
63 (use --subrepos for recursive commit)
64 [255]
64 [255]
65
65
66 The --subrepos flag overwrite the config setting:
66 The --subrepos flag overwrite the config setting:
67
67
68 $ hg commit -m 0-0-0 --config ui.commitsubrepos=No --subrepos
68 $ hg commit -m 0-0-0 --config ui.commitsubrepos=No --subrepos
69 committing subrepository foo
69 committing subrepository foo
70 committing subrepository foo/bar (glob)
70 committing subrepository foo/bar (glob)
71
71
72 $ cd foo
72 $ cd foo
73 $ echo y2 >> y.txt
73 $ echo y2 >> y.txt
74 $ hg commit -m 0-1-0
74 $ hg commit -m 0-1-0
75
75
76 $ cd bar
76 $ cd bar
77 $ echo z2 >> z.txt
77 $ echo z2 >> z.txt
78 $ hg commit -m 0-1-1
78 $ hg commit -m 0-1-1
79
79
80 $ cd ..
80 $ cd ..
81 $ hg commit -m 0-2-1
81 $ hg commit -m 0-2-1
82
82
83 $ cd ..
83 $ cd ..
84 $ hg commit -m 1-2-1
84 $ hg commit -m 1-2-1
85
85
86 Change working directory:
86 Change working directory:
87
87
88 $ echo y3 >> foo/y.txt
88 $ echo y3 >> foo/y.txt
89 $ echo z3 >> foo/bar/z.txt
89 $ echo z3 >> foo/bar/z.txt
90 $ hg status -S
90 $ hg status -S
91 M foo/bar/z.txt
91 M foo/bar/z.txt
92 M foo/y.txt
92 M foo/y.txt
93 $ hg diff --nodates -S
93 $ hg diff --nodates -S
94 diff -r d254738c5f5e foo/y.txt
94 diff -r d254738c5f5e foo/y.txt
95 --- a/foo/y.txt
95 --- a/foo/y.txt
96 +++ b/foo/y.txt
96 +++ b/foo/y.txt
97 @@ -1,2 +1,3 @@
97 @@ -1,2 +1,3 @@
98 y1
98 y1
99 y2
99 y2
100 +y3
100 +y3
101 diff -r 9647f22de499 foo/bar/z.txt
101 diff -r 9647f22de499 foo/bar/z.txt
102 --- a/foo/bar/z.txt
102 --- a/foo/bar/z.txt
103 +++ b/foo/bar/z.txt
103 +++ b/foo/bar/z.txt
104 @@ -1,2 +1,3 @@
104 @@ -1,2 +1,3 @@
105 z1
105 z1
106 z2
106 z2
107 +z3
107 +z3
108
108
109 Status call crossing repository boundaries:
109 Status call crossing repository boundaries:
110
110
111 $ hg status -S foo/bar/z.txt
111 $ hg status -S foo/bar/z.txt
112 M foo/bar/z.txt
112 M foo/bar/z.txt
113 $ hg status -S -I 'foo/?.txt'
113 $ hg status -S -I 'foo/?.txt'
114 M foo/y.txt
114 M foo/y.txt
115 $ hg status -S -I '**/?.txt'
115 $ hg status -S -I '**/?.txt'
116 M foo/bar/z.txt
116 M foo/bar/z.txt
117 M foo/y.txt
117 M foo/y.txt
118 $ hg diff --nodates -S -I '**/?.txt'
118 $ hg diff --nodates -S -I '**/?.txt'
119 diff -r d254738c5f5e foo/y.txt
119 diff -r d254738c5f5e foo/y.txt
120 --- a/foo/y.txt
120 --- a/foo/y.txt
121 +++ b/foo/y.txt
121 +++ b/foo/y.txt
122 @@ -1,2 +1,3 @@
122 @@ -1,2 +1,3 @@
123 y1
123 y1
124 y2
124 y2
125 +y3
125 +y3
126 diff -r 9647f22de499 foo/bar/z.txt
126 diff -r 9647f22de499 foo/bar/z.txt
127 --- a/foo/bar/z.txt
127 --- a/foo/bar/z.txt
128 +++ b/foo/bar/z.txt
128 +++ b/foo/bar/z.txt
129 @@ -1,2 +1,3 @@
129 @@ -1,2 +1,3 @@
130 z1
130 z1
131 z2
131 z2
132 +z3
132 +z3
133
133
134 Status from within a subdirectory:
134 Status from within a subdirectory:
135
135
136 $ mkdir dir
136 $ mkdir dir
137 $ cd dir
137 $ cd dir
138 $ echo a1 > a.txt
138 $ echo a1 > a.txt
139 $ hg status -S
139 $ hg status -S
140 M foo/bar/z.txt
140 M foo/bar/z.txt
141 M foo/y.txt
141 M foo/y.txt
142 ? dir/a.txt
142 ? dir/a.txt
143 $ hg diff --nodates -S
143 $ hg diff --nodates -S
144 diff -r d254738c5f5e foo/y.txt
144 diff -r d254738c5f5e foo/y.txt
145 --- a/foo/y.txt
145 --- a/foo/y.txt
146 +++ b/foo/y.txt
146 +++ b/foo/y.txt
147 @@ -1,2 +1,3 @@
147 @@ -1,2 +1,3 @@
148 y1
148 y1
149 y2
149 y2
150 +y3
150 +y3
151 diff -r 9647f22de499 foo/bar/z.txt
151 diff -r 9647f22de499 foo/bar/z.txt
152 --- a/foo/bar/z.txt
152 --- a/foo/bar/z.txt
153 +++ b/foo/bar/z.txt
153 +++ b/foo/bar/z.txt
154 @@ -1,2 +1,3 @@
154 @@ -1,2 +1,3 @@
155 z1
155 z1
156 z2
156 z2
157 +z3
157 +z3
158
158
159 Status with relative path:
159 Status with relative path:
160
160
161 $ hg status -S ..
161 $ hg status -S ..
162 M ../foo/bar/z.txt
162 M ../foo/bar/z.txt
163 M ../foo/y.txt
163 M ../foo/y.txt
164 ? a.txt
164 ? a.txt
165
165
166 XXX: filtering lfilesrepo.status() in 3.3-rc causes these files to be listed as
166 XXX: filtering lfilesrepo.status() in 3.3-rc causes these files to be listed as
167 added instead of modified.
167 added instead of modified.
168 $ hg status -S .. --config extensions.largefiles=
168 $ hg status -S .. --config extensions.largefiles=
169 M ../foo/bar/z.txt
169 M ../foo/bar/z.txt
170 M ../foo/y.txt
170 M ../foo/y.txt
171 ? a.txt
171 ? a.txt
172
172
173 $ hg diff --nodates -S ..
173 $ hg diff --nodates -S ..
174 diff -r d254738c5f5e foo/y.txt
174 diff -r d254738c5f5e foo/y.txt
175 --- a/foo/y.txt
175 --- a/foo/y.txt
176 +++ b/foo/y.txt
176 +++ b/foo/y.txt
177 @@ -1,2 +1,3 @@
177 @@ -1,2 +1,3 @@
178 y1
178 y1
179 y2
179 y2
180 +y3
180 +y3
181 diff -r 9647f22de499 foo/bar/z.txt
181 diff -r 9647f22de499 foo/bar/z.txt
182 --- a/foo/bar/z.txt
182 --- a/foo/bar/z.txt
183 +++ b/foo/bar/z.txt
183 +++ b/foo/bar/z.txt
184 @@ -1,2 +1,3 @@
184 @@ -1,2 +1,3 @@
185 z1
185 z1
186 z2
186 z2
187 +z3
187 +z3
188 $ cd ..
188 $ cd ..
189
189
190 Cleanup and final commit:
190 Cleanup and final commit:
191
191
192 $ rm -r dir
192 $ rm -r dir
193 $ hg commit --subrepos -m 2-3-2
193 $ hg commit --subrepos -m 2-3-2
194 committing subrepository foo
194 committing subrepository foo
195 committing subrepository foo/bar (glob)
195 committing subrepository foo/bar (glob)
196
196
197 Test explicit path commands within subrepos: add/forget
197 Test explicit path commands within subrepos: add/forget
198 $ echo z1 > foo/bar/z2.txt
198 $ echo z1 > foo/bar/z2.txt
199 $ hg status -S
199 $ hg status -S
200 ? foo/bar/z2.txt
200 ? foo/bar/z2.txt
201 $ hg add foo/bar/z2.txt
201 $ hg add foo/bar/z2.txt
202 $ hg status -S
202 $ hg status -S
203 A foo/bar/z2.txt
203 A foo/bar/z2.txt
204 $ hg forget foo/bar/z2.txt
204 $ hg forget foo/bar/z2.txt
205 $ hg status -S
205 $ hg status -S
206 ? foo/bar/z2.txt
206 ? foo/bar/z2.txt
207 $ hg forget foo/bar/z2.txt
207 $ hg forget foo/bar/z2.txt
208 not removing foo/bar/z2.txt: file is already untracked (glob)
208 not removing foo/bar/z2.txt: file is already untracked (glob)
209 [1]
209 [1]
210 $ hg status -S
210 $ hg status -S
211 ? foo/bar/z2.txt
211 ? foo/bar/z2.txt
212 $ rm foo/bar/z2.txt
212 $ rm foo/bar/z2.txt
213
213
214 Log with the relationships between repo and its subrepo:
214 Log with the relationships between repo and its subrepo:
215
215
216 $ hg log --template '{rev}:{node|short} {desc}\n'
216 $ hg log --template '{rev}:{node|short} {desc}\n'
217 2:1326fa26d0c0 2-3-2
217 2:1326fa26d0c0 2-3-2
218 1:4b3c9ff4f66b 1-2-1
218 1:4b3c9ff4f66b 1-2-1
219 0:23376cbba0d8 0-0-0
219 0:23376cbba0d8 0-0-0
220
220
221 $ hg -R foo log --template '{rev}:{node|short} {desc}\n'
221 $ hg -R foo log --template '{rev}:{node|short} {desc}\n'
222 3:65903cebad86 2-3-2
222 3:65903cebad86 2-3-2
223 2:d254738c5f5e 0-2-1
223 2:d254738c5f5e 0-2-1
224 1:8629ce7dcc39 0-1-0
224 1:8629ce7dcc39 0-1-0
225 0:af048e97ade2 0-0-0
225 0:af048e97ade2 0-0-0
226
226
227 $ hg -R foo/bar log --template '{rev}:{node|short} {desc}\n'
227 $ hg -R foo/bar log --template '{rev}:{node|short} {desc}\n'
228 2:31ecbdafd357 2-3-2
228 2:31ecbdafd357 2-3-2
229 1:9647f22de499 0-1-1
229 1:9647f22de499 0-1-1
230 0:4904098473f9 0-0-0
230 0:4904098473f9 0-0-0
231
231
232 Status between revisions:
232 Status between revisions:
233
233
234 $ hg status -S
234 $ hg status -S
235 $ hg status -S --rev 0:1
235 $ hg status -S --rev 0:1
236 M .hgsubstate
236 M .hgsubstate
237 M foo/.hgsubstate
237 M foo/.hgsubstate
238 M foo/bar/z.txt
238 M foo/bar/z.txt
239 M foo/y.txt
239 M foo/y.txt
240 $ hg diff --nodates -S -I '**/?.txt' --rev 0:1
240 $ hg diff --nodates -S -I '**/?.txt' --rev 0:1
241 diff -r af048e97ade2 -r d254738c5f5e foo/y.txt
241 diff -r af048e97ade2 -r d254738c5f5e foo/y.txt
242 --- a/foo/y.txt
242 --- a/foo/y.txt
243 +++ b/foo/y.txt
243 +++ b/foo/y.txt
244 @@ -1,1 +1,2 @@
244 @@ -1,1 +1,2 @@
245 y1
245 y1
246 +y2
246 +y2
247 diff -r 4904098473f9 -r 9647f22de499 foo/bar/z.txt
247 diff -r 4904098473f9 -r 9647f22de499 foo/bar/z.txt
248 --- a/foo/bar/z.txt
248 --- a/foo/bar/z.txt
249 +++ b/foo/bar/z.txt
249 +++ b/foo/bar/z.txt
250 @@ -1,1 +1,2 @@
250 @@ -1,1 +1,2 @@
251 z1
251 z1
252 +z2
252 +z2
253
253
254 Enable progress extension for archive tests:
254 Enable progress extension for archive tests:
255
255
256 $ cp $HGRCPATH $HGRCPATH.no-progress
256 $ cp $HGRCPATH $HGRCPATH.no-progress
257 $ cat >> $HGRCPATH <<EOF
257 $ cat >> $HGRCPATH <<EOF
258 > [extensions]
258 > [extensions]
259 > progress =
259 > progress =
260 > [progress]
260 > [progress]
261 > disable=False
261 > assume-tty = 1
262 > assume-tty = 1
262 > delay = 0
263 > delay = 0
263 > # set changedelay really large so we don't see nested topics
264 > # set changedelay really large so we don't see nested topics
264 > changedelay = 30000
265 > changedelay = 30000
265 > format = topic bar number
266 > format = topic bar number
266 > refresh = 0
267 > refresh = 0
267 > width = 60
268 > width = 60
268 > EOF
269 > EOF
269
270
270 Test archiving to a directory tree (the doubled lines in the output
271 Test archiving to a directory tree (the doubled lines in the output
271 only show up in the test output, not in real usage):
272 only show up in the test output, not in real usage):
272
273
273 $ hg archive --subrepos ../archive
274 $ hg archive --subrepos ../archive
274 \r (no-eol) (esc)
275 \r (no-eol) (esc)
275 archiving [ ] 0/3\r (no-eol) (esc)
276 archiving [ ] 0/3\r (no-eol) (esc)
276 archiving [=============> ] 1/3\r (no-eol) (esc)
277 archiving [=============> ] 1/3\r (no-eol) (esc)
277 archiving [===========================> ] 2/3\r (no-eol) (esc)
278 archiving [===========================> ] 2/3\r (no-eol) (esc)
278 archiving [==========================================>] 3/3\r (no-eol) (esc)
279 archiving [==========================================>] 3/3\r (no-eol) (esc)
279 \r (no-eol) (esc)
280 \r (no-eol) (esc)
280 \r (no-eol) (esc)
281 \r (no-eol) (esc)
281 archiving (foo) [ ] 0/3\r (no-eol) (esc)
282 archiving (foo) [ ] 0/3\r (no-eol) (esc)
282 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
283 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
283 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
284 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
284 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
285 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
285 \r (no-eol) (esc)
286 \r (no-eol) (esc)
286 \r (no-eol) (esc)
287 \r (no-eol) (esc)
287 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
288 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
288 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
289 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
289 \r (no-eol) (esc)
290 \r (no-eol) (esc)
290 $ find ../archive | sort
291 $ find ../archive | sort
291 ../archive
292 ../archive
292 ../archive/.hg_archival.txt
293 ../archive/.hg_archival.txt
293 ../archive/.hgsub
294 ../archive/.hgsub
294 ../archive/.hgsubstate
295 ../archive/.hgsubstate
295 ../archive/foo
296 ../archive/foo
296 ../archive/foo/.hgsub
297 ../archive/foo/.hgsub
297 ../archive/foo/.hgsubstate
298 ../archive/foo/.hgsubstate
298 ../archive/foo/bar
299 ../archive/foo/bar
299 ../archive/foo/bar/z.txt
300 ../archive/foo/bar/z.txt
300 ../archive/foo/y.txt
301 ../archive/foo/y.txt
301 ../archive/x.txt
302 ../archive/x.txt
302
303
303 Test archiving to zip file (unzip output is unstable):
304 Test archiving to zip file (unzip output is unstable):
304
305
305 $ hg archive --subrepos --prefix '.' ../archive.zip
306 $ hg archive --subrepos --prefix '.' ../archive.zip
306 \r (no-eol) (esc)
307 \r (no-eol) (esc)
307 archiving [ ] 0/3\r (no-eol) (esc)
308 archiving [ ] 0/3\r (no-eol) (esc)
308 archiving [=============> ] 1/3\r (no-eol) (esc)
309 archiving [=============> ] 1/3\r (no-eol) (esc)
309 archiving [===========================> ] 2/3\r (no-eol) (esc)
310 archiving [===========================> ] 2/3\r (no-eol) (esc)
310 archiving [==========================================>] 3/3\r (no-eol) (esc)
311 archiving [==========================================>] 3/3\r (no-eol) (esc)
311 \r (no-eol) (esc)
312 \r (no-eol) (esc)
312 \r (no-eol) (esc)
313 \r (no-eol) (esc)
313 archiving (foo) [ ] 0/3\r (no-eol) (esc)
314 archiving (foo) [ ] 0/3\r (no-eol) (esc)
314 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
315 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
315 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
316 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
316 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
317 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
317 \r (no-eol) (esc)
318 \r (no-eol) (esc)
318 \r (no-eol) (esc)
319 \r (no-eol) (esc)
319 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
320 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
320 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
321 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
321 \r (no-eol) (esc)
322 \r (no-eol) (esc)
322
323
323 (unzip date formating is unstable, we do not care about it and glob it out)
324 (unzip date formating is unstable, we do not care about it and glob it out)
324
325
325 $ unzip -l ../archive.zip
326 $ unzip -l ../archive.zip
326 Archive: ../archive.zip
327 Archive: ../archive.zip
327 Length Date Time Name
328 Length Date Time Name
328 --------- ---------- ----- ----
329 --------- ---------- ----- ----
329 172 ?????????? 00:00 .hg_archival.txt (glob)
330 172 ?????????? 00:00 .hg_archival.txt (glob)
330 10 ?????????? 00:00 .hgsub (glob)
331 10 ?????????? 00:00 .hgsub (glob)
331 45 ?????????? 00:00 .hgsubstate (glob)
332 45 ?????????? 00:00 .hgsubstate (glob)
332 3 ?????????? 00:00 x.txt (glob)
333 3 ?????????? 00:00 x.txt (glob)
333 10 ?????????? 00:00 foo/.hgsub (glob)
334 10 ?????????? 00:00 foo/.hgsub (glob)
334 45 ?????????? 00:00 foo/.hgsubstate (glob)
335 45 ?????????? 00:00 foo/.hgsubstate (glob)
335 9 ?????????? 00:00 foo/y.txt (glob)
336 9 ?????????? 00:00 foo/y.txt (glob)
336 9 ?????????? 00:00 foo/bar/z.txt (glob)
337 9 ?????????? 00:00 foo/bar/z.txt (glob)
337 --------- -------
338 --------- -------
338 303 8 files
339 303 8 files
339
340
340 Test archiving a revision that references a subrepo that is not yet
341 Test archiving a revision that references a subrepo that is not yet
341 cloned:
342 cloned:
342
343
343 #if hardlink
344 #if hardlink
344 $ hg clone -U . ../empty
345 $ hg clone -U . ../empty
345 \r (no-eol) (esc)
346 \r (no-eol) (esc)
346 linking [ <=> ] 1\r (no-eol) (esc)
347 linking [ <=> ] 1\r (no-eol) (esc)
347 linking [ <=> ] 2\r (no-eol) (esc)
348 linking [ <=> ] 2\r (no-eol) (esc)
348 linking [ <=> ] 3\r (no-eol) (esc)
349 linking [ <=> ] 3\r (no-eol) (esc)
349 linking [ <=> ] 4\r (no-eol) (esc)
350 linking [ <=> ] 4\r (no-eol) (esc)
350 linking [ <=> ] 5\r (no-eol) (esc)
351 linking [ <=> ] 5\r (no-eol) (esc)
351 linking [ <=> ] 6\r (no-eol) (esc)
352 linking [ <=> ] 6\r (no-eol) (esc)
352 linking [ <=> ] 7\r (no-eol) (esc)
353 linking [ <=> ] 7\r (no-eol) (esc)
353 linking [ <=> ] 8\r (no-eol) (esc)
354 linking [ <=> ] 8\r (no-eol) (esc)
354 \r (no-eol) (esc)
355 \r (no-eol) (esc)
355 #else
356 #else
356 $ hg clone -U . ../empty
357 $ hg clone -U . ../empty
357 \r (no-eol) (esc)
358 \r (no-eol) (esc)
358 linking [ <=> ] 1 (no-eol)
359 linking [ <=> ] 1 (no-eol)
359 #endif
360 #endif
360
361
361 $ cd ../empty
362 $ cd ../empty
362 #if hardlink
363 #if hardlink
363 $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz
364 $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz
364 \r (no-eol) (esc)
365 \r (no-eol) (esc)
365 archiving [ ] 0/3\r (no-eol) (esc)
366 archiving [ ] 0/3\r (no-eol) (esc)
366 archiving [=============> ] 1/3\r (no-eol) (esc)
367 archiving [=============> ] 1/3\r (no-eol) (esc)
367 archiving [===========================> ] 2/3\r (no-eol) (esc)
368 archiving [===========================> ] 2/3\r (no-eol) (esc)
368 archiving [==========================================>] 3/3\r (no-eol) (esc)
369 archiving [==========================================>] 3/3\r (no-eol) (esc)
369 \r (no-eol) (esc)
370 \r (no-eol) (esc)
370 \r (no-eol) (esc)
371 \r (no-eol) (esc)
371 linking [ <=> ] 1\r (no-eol) (esc)
372 linking [ <=> ] 1\r (no-eol) (esc)
372 linking [ <=> ] 2\r (no-eol) (esc)
373 linking [ <=> ] 2\r (no-eol) (esc)
373 linking [ <=> ] 3\r (no-eol) (esc)
374 linking [ <=> ] 3\r (no-eol) (esc)
374 linking [ <=> ] 4\r (no-eol) (esc)
375 linking [ <=> ] 4\r (no-eol) (esc)
375 linking [ <=> ] 5\r (no-eol) (esc)
376 linking [ <=> ] 5\r (no-eol) (esc)
376 linking [ <=> ] 6\r (no-eol) (esc)
377 linking [ <=> ] 6\r (no-eol) (esc)
377 linking [ <=> ] 7\r (no-eol) (esc)
378 linking [ <=> ] 7\r (no-eol) (esc)
378 linking [ <=> ] 8\r (no-eol) (esc)
379 linking [ <=> ] 8\r (no-eol) (esc)
379 \r (no-eol) (esc)
380 \r (no-eol) (esc)
380 \r (no-eol) (esc)
381 \r (no-eol) (esc)
381 archiving (foo) [ ] 0/3\r (no-eol) (esc)
382 archiving (foo) [ ] 0/3\r (no-eol) (esc)
382 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
383 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
383 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
384 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
384 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
385 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
385 \r (no-eol) (esc)
386 \r (no-eol) (esc)
386 \r (no-eol) (esc)
387 \r (no-eol) (esc)
387 linking [ <=> ] 1\r (no-eol) (esc)
388 linking [ <=> ] 1\r (no-eol) (esc)
388 linking [ <=> ] 2\r (no-eol) (esc)
389 linking [ <=> ] 2\r (no-eol) (esc)
389 linking [ <=> ] 3\r (no-eol) (esc)
390 linking [ <=> ] 3\r (no-eol) (esc)
390 linking [ <=> ] 4\r (no-eol) (esc)
391 linking [ <=> ] 4\r (no-eol) (esc)
391 linking [ <=> ] 5\r (no-eol) (esc)
392 linking [ <=> ] 5\r (no-eol) (esc)
392 linking [ <=> ] 6\r (no-eol) (esc)
393 linking [ <=> ] 6\r (no-eol) (esc)
393 \r (no-eol) (esc)
394 \r (no-eol) (esc)
394 \r (no-eol) (esc)
395 \r (no-eol) (esc)
395 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
396 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
396 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
397 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
397 \r (no-eol) (esc)
398 \r (no-eol) (esc)
398 cloning subrepo foo from $TESTTMP/repo/foo
399 cloning subrepo foo from $TESTTMP/repo/foo
399 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
400 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
400 #else
401 #else
401 Note there's a slight output glitch on non-hardlink systems: the last
402 Note there's a slight output glitch on non-hardlink systems: the last
402 "linking" progress topic never gets closed, leading to slight output corruption on that platform.
403 "linking" progress topic never gets closed, leading to slight output corruption on that platform.
403 $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz
404 $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz
404 \r (no-eol) (esc)
405 \r (no-eol) (esc)
405 archiving [ ] 0/3\r (no-eol) (esc)
406 archiving [ ] 0/3\r (no-eol) (esc)
406 archiving [=============> ] 1/3\r (no-eol) (esc)
407 archiving [=============> ] 1/3\r (no-eol) (esc)
407 archiving [===========================> ] 2/3\r (no-eol) (esc)
408 archiving [===========================> ] 2/3\r (no-eol) (esc)
408 archiving [==========================================>] 3/3\r (no-eol) (esc)
409 archiving [==========================================>] 3/3\r (no-eol) (esc)
409 \r (no-eol) (esc)
410 \r (no-eol) (esc)
410 \r (no-eol) (esc)
411 \r (no-eol) (esc)
411 linking [ <=> ] 1\r (no-eol) (esc)
412 linking [ <=> ] 1\r (no-eol) (esc)
412 \r (no-eol) (esc)
413 \r (no-eol) (esc)
413 \r (no-eol) (esc)
414 \r (no-eol) (esc)
414 linking [ <=> ] 1cloning subrepo foo from $TESTTMP/repo/foo
415 linking [ <=> ] 1cloning subrepo foo from $TESTTMP/repo/foo
415 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
416 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
416 #endif
417 #endif
417
418
418 Archive + subrepos uses '/' for all component separators
419 Archive + subrepos uses '/' for all component separators
419
420
420 $ tar -tzf ../archive.tar.gz | sort
421 $ tar -tzf ../archive.tar.gz | sort
421 .hg_archival.txt
422 .hg_archival.txt
422 .hgsub
423 .hgsub
423 .hgsubstate
424 .hgsubstate
424 foo/.hgsub
425 foo/.hgsub
425 foo/.hgsubstate
426 foo/.hgsubstate
426 foo/bar/z.txt
427 foo/bar/z.txt
427 foo/y.txt
428 foo/y.txt
428 x.txt
429 x.txt
429
430
430 The newly cloned subrepos contain no working copy:
431 The newly cloned subrepos contain no working copy:
431
432
432 $ hg -R foo summary
433 $ hg -R foo summary
433 parent: -1:000000000000 (no revision checked out)
434 parent: -1:000000000000 (no revision checked out)
434 branch: default
435 branch: default
435 commit: (clean)
436 commit: (clean)
436 update: 4 new changesets (update)
437 update: 4 new changesets (update)
437
438
438 Disable progress extension and cleanup:
439 Disable progress extension and cleanup:
439
440
440 $ mv $HGRCPATH.no-progress $HGRCPATH
441 $ mv $HGRCPATH.no-progress $HGRCPATH
441
442
442 Test archiving when there is a directory in the way for a subrepo
443 Test archiving when there is a directory in the way for a subrepo
443 created by archive:
444 created by archive:
444
445
445 $ hg clone -U . ../almost-empty
446 $ hg clone -U . ../almost-empty
446 $ cd ../almost-empty
447 $ cd ../almost-empty
447 $ mkdir foo
448 $ mkdir foo
448 $ echo f > foo/f
449 $ echo f > foo/f
449 $ hg archive --subrepos -r tip archive
450 $ hg archive --subrepos -r tip archive
450 cloning subrepo foo from $TESTTMP/empty/foo
451 cloning subrepo foo from $TESTTMP/empty/foo
451 abort: destination '$TESTTMP/almost-empty/foo' is not empty (in subrepo foo) (glob)
452 abort: destination '$TESTTMP/almost-empty/foo' is not empty (in subrepo foo) (glob)
452 [255]
453 [255]
453
454
454 Clone and test outgoing:
455 Clone and test outgoing:
455
456
456 $ cd ..
457 $ cd ..
457 $ hg clone repo repo2
458 $ hg clone repo repo2
458 updating to branch default
459 updating to branch default
459 cloning subrepo foo from $TESTTMP/repo/foo
460 cloning subrepo foo from $TESTTMP/repo/foo
460 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
461 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
461 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 $ cd repo2
463 $ cd repo2
463 $ hg outgoing -S
464 $ hg outgoing -S
464 comparing with $TESTTMP/repo (glob)
465 comparing with $TESTTMP/repo (glob)
465 searching for changes
466 searching for changes
466 no changes found
467 no changes found
467 comparing with $TESTTMP/repo/foo
468 comparing with $TESTTMP/repo/foo
468 searching for changes
469 searching for changes
469 no changes found
470 no changes found
470 comparing with $TESTTMP/repo/foo/bar
471 comparing with $TESTTMP/repo/foo/bar
471 searching for changes
472 searching for changes
472 no changes found
473 no changes found
473 [1]
474 [1]
474
475
475 Make nested change:
476 Make nested change:
476
477
477 $ echo y4 >> foo/y.txt
478 $ echo y4 >> foo/y.txt
478 $ hg diff --nodates -S
479 $ hg diff --nodates -S
479 diff -r 65903cebad86 foo/y.txt
480 diff -r 65903cebad86 foo/y.txt
480 --- a/foo/y.txt
481 --- a/foo/y.txt
481 +++ b/foo/y.txt
482 +++ b/foo/y.txt
482 @@ -1,3 +1,4 @@
483 @@ -1,3 +1,4 @@
483 y1
484 y1
484 y2
485 y2
485 y3
486 y3
486 +y4
487 +y4
487 $ hg commit --subrepos -m 3-4-2
488 $ hg commit --subrepos -m 3-4-2
488 committing subrepository foo
489 committing subrepository foo
489 $ hg outgoing -S
490 $ hg outgoing -S
490 comparing with $TESTTMP/repo (glob)
491 comparing with $TESTTMP/repo (glob)
491 searching for changes
492 searching for changes
492 changeset: 3:2655b8ecc4ee
493 changeset: 3:2655b8ecc4ee
493 tag: tip
494 tag: tip
494 user: test
495 user: test
495 date: Thu Jan 01 00:00:00 1970 +0000
496 date: Thu Jan 01 00:00:00 1970 +0000
496 summary: 3-4-2
497 summary: 3-4-2
497
498
498 comparing with $TESTTMP/repo/foo
499 comparing with $TESTTMP/repo/foo
499 searching for changes
500 searching for changes
500 changeset: 4:e96193d6cb36
501 changeset: 4:e96193d6cb36
501 tag: tip
502 tag: tip
502 user: test
503 user: test
503 date: Thu Jan 01 00:00:00 1970 +0000
504 date: Thu Jan 01 00:00:00 1970 +0000
504 summary: 3-4-2
505 summary: 3-4-2
505
506
506 comparing with $TESTTMP/repo/foo/bar
507 comparing with $TESTTMP/repo/foo/bar
507 searching for changes
508 searching for changes
508 no changes found
509 no changes found
509
510
510
511
511 Switch to original repo and setup default path:
512 Switch to original repo and setup default path:
512
513
513 $ cd ../repo
514 $ cd ../repo
514 $ echo '[paths]' >> .hg/hgrc
515 $ echo '[paths]' >> .hg/hgrc
515 $ echo 'default = ../repo2' >> .hg/hgrc
516 $ echo 'default = ../repo2' >> .hg/hgrc
516
517
517 Test incoming:
518 Test incoming:
518
519
519 $ hg incoming -S
520 $ hg incoming -S
520 comparing with $TESTTMP/repo2 (glob)
521 comparing with $TESTTMP/repo2 (glob)
521 searching for changes
522 searching for changes
522 changeset: 3:2655b8ecc4ee
523 changeset: 3:2655b8ecc4ee
523 tag: tip
524 tag: tip
524 user: test
525 user: test
525 date: Thu Jan 01 00:00:00 1970 +0000
526 date: Thu Jan 01 00:00:00 1970 +0000
526 summary: 3-4-2
527 summary: 3-4-2
527
528
528 comparing with $TESTTMP/repo2/foo
529 comparing with $TESTTMP/repo2/foo
529 searching for changes
530 searching for changes
530 changeset: 4:e96193d6cb36
531 changeset: 4:e96193d6cb36
531 tag: tip
532 tag: tip
532 user: test
533 user: test
533 date: Thu Jan 01 00:00:00 1970 +0000
534 date: Thu Jan 01 00:00:00 1970 +0000
534 summary: 3-4-2
535 summary: 3-4-2
535
536
536 comparing with $TESTTMP/repo2/foo/bar
537 comparing with $TESTTMP/repo2/foo/bar
537 searching for changes
538 searching for changes
538 no changes found
539 no changes found
539
540
540 $ hg incoming -S --bundle incoming.hg
541 $ hg incoming -S --bundle incoming.hg
541 abort: cannot combine --bundle and --subrepos
542 abort: cannot combine --bundle and --subrepos
542 [255]
543 [255]
543
544
544 Test missing subrepo:
545 Test missing subrepo:
545
546
546 $ rm -r foo
547 $ rm -r foo
547 $ hg status -S
548 $ hg status -S
548 warning: error "unknown revision '65903cebad86f1a84bd4f1134f62fa7dcb7a1c98'" in subrepository "foo"
549 warning: error "unknown revision '65903cebad86f1a84bd4f1134f62fa7dcb7a1c98'" in subrepository "foo"
549
550
550 Issue2619: IndexError: list index out of range on hg add with subrepos
551 Issue2619: IndexError: list index out of range on hg add with subrepos
551 The subrepo must sorts after the explicit filename.
552 The subrepo must sorts after the explicit filename.
552
553
553 $ cd ..
554 $ cd ..
554 $ hg init test
555 $ hg init test
555 $ cd test
556 $ cd test
556 $ hg init x
557 $ hg init x
557 $ echo abc > abc.txt
558 $ echo abc > abc.txt
558 $ hg ci -Am "abc"
559 $ hg ci -Am "abc"
559 adding abc.txt
560 adding abc.txt
560 $ echo "x = x" >> .hgsub
561 $ echo "x = x" >> .hgsub
561 $ hg add .hgsub
562 $ hg add .hgsub
562 $ touch a x/a
563 $ touch a x/a
563 $ hg add a x/a
564 $ hg add a x/a
564
565
565 $ hg ci -Sm "added x"
566 $ hg ci -Sm "added x"
566 committing subrepository x
567 committing subrepository x
567 $ echo abc > x/a
568 $ echo abc > x/a
568 $ hg revert --rev '.^' "set:subrepo('glob:x*')"
569 $ hg revert --rev '.^' "set:subrepo('glob:x*')"
569 abort: subrepository 'x' does not exist in 25ac2c9b3180!
570 abort: subrepository 'x' does not exist in 25ac2c9b3180!
570 [255]
571 [255]
571
572
572 $ cd ..
573 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now