##// END OF EJS Templates
py3: use stringutil.pprint() instead of '%r'...
Pulkit Goyal -
r40262:0d403e9f default
parent child Browse files
Show More
@@ -1,604 +1,605
1 test-abort-checkin.t
1 test-abort-checkin.t
2 test-absorb-filefixupstate.py
2 test-absorb-filefixupstate.py
3 test-absorb-phase.t
3 test-absorb-phase.t
4 test-absorb-rename.t
4 test-absorb-rename.t
5 test-absorb-strip.t
5 test-absorb-strip.t
6 test-absorb.t
6 test-absorb.t
7 test-add.t
7 test-add.t
8 test-addremove-similar.t
8 test-addremove-similar.t
9 test-addremove.t
9 test-addremove.t
10 test-alias.t
10 test-alias.t
11 test-amend-subrepo.t
11 test-amend-subrepo.t
12 test-amend.t
12 test-amend.t
13 test-ancestor.py
13 test-ancestor.py
14 test-annotate.py
14 test-annotate.py
15 test-annotate.t
15 test-annotate.t
16 test-archive-symlinks.t
16 test-archive-symlinks.t
17 test-atomictempfile.py
17 test-atomictempfile.py
18 test-audit-path.t
18 test-audit-path.t
19 test-audit-subrepo.t
19 test-audit-subrepo.t
20 test-automv.t
20 test-automv.t
21 test-backout.t
21 test-backout.t
22 test-backwards-remove.t
22 test-backwards-remove.t
23 test-bad-pull.t
23 test-bad-pull.t
24 test-basic.t
24 test-basic.t
25 test-bdiff.py
25 test-bdiff.py
26 test-bheads.t
26 test-bheads.t
27 test-bisect.t
27 test-bisect.t
28 test-bisect2.t
28 test-bisect2.t
29 test-bisect3.t
29 test-bisect3.t
30 test-blackbox.t
30 test-blackbox.t
31 test-bookmarks-current.t
31 test-bookmarks-current.t
32 test-bookmarks-merge.t
32 test-bookmarks-merge.t
33 test-bookmarks-pushpull.t
33 test-bookmarks-pushpull.t
34 test-bookmarks-rebase.t
34 test-bookmarks-rebase.t
35 test-bookmarks-strip.t
35 test-bookmarks-strip.t
36 test-bookmarks.t
36 test-bookmarks.t
37 test-branch-change.t
37 test-branch-change.t
38 test-branch-option.t
38 test-branch-option.t
39 test-branch-tag-confict.t
39 test-branch-tag-confict.t
40 test-branches.t
40 test-branches.t
41 test-bundle-phases.t
41 test-bundle-phases.t
42 test-bundle-r.t
42 test-bundle-r.t
43 test-bundle-type.t
43 test-bundle-type.t
44 test-bundle-vs-outgoing.t
44 test-bundle-vs-outgoing.t
45 test-bundle.t
45 test-bundle.t
46 test-bundle2-exchange.t
46 test-bundle2-exchange.t
47 test-bundle2-format.t
47 test-bundle2-format.t
48 test-bundle2-multiple-changegroups.t
48 test-bundle2-multiple-changegroups.t
49 test-bundle2-pushback.t
49 test-bundle2-pushback.t
50 test-bundle2-remote-changegroup.t
50 test-bundle2-remote-changegroup.t
51 test-cappedreader.py
51 test-cappedreader.py
52 test-casecollision.t
52 test-casecollision.t
53 test-cat.t
53 test-cat.t
54 test-cbor.py
54 test-cbor.py
55 test-censor.t
55 test-censor.t
56 test-changelog-exec.t
56 test-changelog-exec.t
57 test-check-code.t
57 test-check-code.t
58 test-check-commit.t
58 test-check-commit.t
59 test-check-execute.t
59 test-check-execute.t
60 test-check-interfaces.py
60 test-check-interfaces.py
61 test-check-module-imports.t
61 test-check-module-imports.t
62 test-check-py3-compat.t
62 test-check-py3-compat.t
63 test-check-pyflakes.t
63 test-check-pyflakes.t
64 test-check-pylint.t
64 test-check-pylint.t
65 test-check-shbang.t
65 test-check-shbang.t
66 test-children.t
66 test-children.t
67 test-clone-cgi.t
67 test-clone-cgi.t
68 test-clone-pull-corruption.t
68 test-clone-pull-corruption.t
69 test-clone-r.t
69 test-clone-r.t
70 test-clone-uncompressed.t
70 test-clone-uncompressed.t
71 test-clone-update-order.t
71 test-clone-update-order.t
72 test-clone.t
72 test-clone.t
73 test-clonebundles.t
73 test-clonebundles.t
74 test-commit-amend.t
74 test-commit-amend.t
75 test-commit-interactive.t
75 test-commit-interactive.t
76 test-commit-multiple.t
76 test-commit-multiple.t
77 test-commit-unresolved.t
77 test-commit-unresolved.t
78 test-commit.t
78 test-commit.t
79 test-committer.t
79 test-committer.t
80 test-completion.t
80 test-completion.t
81 test-config-env.py
81 test-config-env.py
82 test-config.t
82 test-config.t
83 test-conflict.t
83 test-conflict.t
84 test-confused-revert.t
84 test-confused-revert.t
85 test-context.py
85 test-context.py
86 test-contrib-check-code.t
86 test-contrib-check-code.t
87 test-contrib-check-commit.t
87 test-contrib-check-commit.t
88 test-contrib-dumprevlog.t
88 test-contrib-dumprevlog.t
89 test-contrib-perf.t
89 test-contrib-perf.t
90 test-contrib-testparseutil.t
90 test-contrib-testparseutil.t
91 test-convert-authormap.t
91 test-convert-authormap.t
92 test-convert-clonebranches.t
92 test-convert-clonebranches.t
93 test-convert-cvs-branch.t
93 test-convert-cvs-branch.t
94 test-convert-cvs-detectmerge.t
94 test-convert-cvs-detectmerge.t
95 test-convert-cvs-synthetic.t
95 test-convert-cvs-synthetic.t
96 test-convert-cvs.t
96 test-convert-cvs.t
97 test-convert-cvsnt-mergepoints.t
97 test-convert-cvsnt-mergepoints.t
98 test-convert-datesort.t
98 test-convert-datesort.t
99 test-convert-filemap.t
99 test-convert-filemap.t
100 test-convert-hg-sink.t
100 test-convert-hg-sink.t
101 test-convert-hg-source.t
101 test-convert-hg-source.t
102 test-convert-hg-startrev.t
102 test-convert-hg-startrev.t
103 test-convert-splicemap.t
103 test-convert-splicemap.t
104 test-convert-tagsbranch-topology.t
104 test-convert-tagsbranch-topology.t
105 test-copy-move-merge.t
105 test-copy-move-merge.t
106 test-copy.t
106 test-copy.t
107 test-copytrace-heuristics.t
107 test-copytrace-heuristics.t
108 test-debugbuilddag.t
108 test-debugbuilddag.t
109 test-debugbundle.t
109 test-debugbundle.t
110 test-debugcommands.t
110 test-debugcommands.t
111 test-debugextensions.t
111 test-debugextensions.t
112 test-debugindexdot.t
112 test-debugindexdot.t
113 test-debugrename.t
113 test-debugrename.t
114 test-default-push.t
114 test-default-push.t
115 test-diff-antipatience.t
115 test-diff-antipatience.t
116 test-diff-binary-file.t
116 test-diff-binary-file.t
117 test-diff-change.t
117 test-diff-change.t
118 test-diff-copy-depth.t
118 test-diff-copy-depth.t
119 test-diff-hashes.t
119 test-diff-hashes.t
120 test-diff-ignore-whitespace.t
120 test-diff-ignore-whitespace.t
121 test-diff-indent-heuristic.t
121 test-diff-indent-heuristic.t
122 test-diff-issue2761.t
122 test-diff-issue2761.t
123 test-diff-newlines.t
123 test-diff-newlines.t
124 test-diff-reverse.t
124 test-diff-reverse.t
125 test-diff-subdir.t
125 test-diff-subdir.t
126 test-diff-unified.t
126 test-diff-unified.t
127 test-diff-upgrade.t
127 test-diff-upgrade.t
128 test-diffdir.t
128 test-diffdir.t
129 test-diffstat.t
129 test-diffstat.t
130 test-directaccess.t
130 test-directaccess.t
131 test-dirstate-backup.t
131 test-dirstate-backup.t
132 test-dirstate-nonnormalset.t
132 test-dirstate-nonnormalset.t
133 test-dirstate.t
133 test-dirstate.t
134 test-dispatch.py
134 test-dispatch.py
135 test-doctest.py
135 test-doctest.py
136 test-double-merge.t
136 test-double-merge.t
137 test-drawdag.t
137 test-drawdag.t
138 test-duplicateoptions.py
138 test-duplicateoptions.py
139 test-editor-filename.t
139 test-editor-filename.t
140 test-empty-dir.t
140 test-empty-dir.t
141 test-empty-file.t
141 test-empty-file.t
142 test-empty-group.t
142 test-empty-group.t
143 test-empty.t
143 test-empty.t
144 test-encode.t
144 test-encode.t
145 test-encoding-func.py
145 test-encoding-func.py
146 test-encoding.t
146 test-encoding.t
147 test-eol-add.t
147 test-eol-add.t
148 test-eol-clone.t
148 test-eol-clone.t
149 test-eol-hook.t
149 test-eol-hook.t
150 test-eol-patch.t
150 test-eol-patch.t
151 test-eol-tag.t
151 test-eol-tag.t
152 test-eol-update.t
152 test-eol-update.t
153 test-eol.t
153 test-eol.t
154 test-eolfilename.t
154 test-eolfilename.t
155 test-excessive-merge.t
155 test-excessive-merge.t
156 test-exchange-obsmarkers-case-A1.t
156 test-exchange-obsmarkers-case-A1.t
157 test-exchange-obsmarkers-case-A2.t
157 test-exchange-obsmarkers-case-A2.t
158 test-exchange-obsmarkers-case-A3.t
158 test-exchange-obsmarkers-case-A3.t
159 test-exchange-obsmarkers-case-A4.t
159 test-exchange-obsmarkers-case-A4.t
160 test-exchange-obsmarkers-case-A5.t
160 test-exchange-obsmarkers-case-A5.t
161 test-exchange-obsmarkers-case-A6.t
161 test-exchange-obsmarkers-case-A6.t
162 test-exchange-obsmarkers-case-A7.t
162 test-exchange-obsmarkers-case-A7.t
163 test-exchange-obsmarkers-case-B1.t
163 test-exchange-obsmarkers-case-B1.t
164 test-exchange-obsmarkers-case-B2.t
164 test-exchange-obsmarkers-case-B2.t
165 test-exchange-obsmarkers-case-B3.t
165 test-exchange-obsmarkers-case-B3.t
166 test-exchange-obsmarkers-case-B4.t
166 test-exchange-obsmarkers-case-B4.t
167 test-exchange-obsmarkers-case-B5.t
167 test-exchange-obsmarkers-case-B5.t
168 test-exchange-obsmarkers-case-B6.t
168 test-exchange-obsmarkers-case-B6.t
169 test-exchange-obsmarkers-case-B7.t
169 test-exchange-obsmarkers-case-B7.t
170 test-exchange-obsmarkers-case-C1.t
170 test-exchange-obsmarkers-case-C1.t
171 test-exchange-obsmarkers-case-C2.t
171 test-exchange-obsmarkers-case-C2.t
172 test-exchange-obsmarkers-case-C3.t
172 test-exchange-obsmarkers-case-C3.t
173 test-exchange-obsmarkers-case-C4.t
173 test-exchange-obsmarkers-case-C4.t
174 test-exchange-obsmarkers-case-D1.t
174 test-exchange-obsmarkers-case-D1.t
175 test-exchange-obsmarkers-case-D2.t
175 test-exchange-obsmarkers-case-D2.t
176 test-exchange-obsmarkers-case-D3.t
176 test-exchange-obsmarkers-case-D3.t
177 test-exchange-obsmarkers-case-D4.t
177 test-exchange-obsmarkers-case-D4.t
178 test-execute-bit.t
178 test-execute-bit.t
179 test-export.t
179 test-export.t
180 test-extdata.t
180 test-extdata.t
181 test-extdiff.t
181 test-extdiff.t
182 test-extensions-afterloaded.t
182 test-extensions-afterloaded.t
183 test-extensions-wrapfunction.py
183 test-extensions-wrapfunction.py
184 test-extra-filelog-entry.t
184 test-extra-filelog-entry.t
185 test-fetch.t
185 test-fetch.t
186 test-filebranch.t
186 test-filebranch.t
187 test-filecache.py
187 test-filecache.py
188 test-filelog.py
188 test-filelog.py
189 test-fileset-generated.t
189 test-fileset-generated.t
190 test-fileset.t
190 test-fileset.t
191 test-fix-topology.t
191 test-fix-topology.t
192 test-flags.t
192 test-flags.t
193 test-generaldelta.t
193 test-generaldelta.t
194 test-getbundle.t
194 test-getbundle.t
195 test-git-export.t
195 test-git-export.t
196 test-globalopts.t
196 test-globalopts.t
197 test-glog-beautifygraph.t
197 test-glog-beautifygraph.t
198 test-glog-topological.t
198 test-glog-topological.t
199 test-glog.t
199 test-glog.t
200 test-gpg.t
200 test-gpg.t
201 test-graft.t
201 test-graft.t
202 test-grep.t
202 test-grep.t
203 test-hg-parseurl.py
203 test-hg-parseurl.py
204 test-hghave.t
204 test-hghave.t
205 test-hgignore.t
205 test-hgignore.t
206 test-hgk.t
206 test-hgk.t
207 test-hgrc.t
207 test-hgrc.t
208 test-hgweb-annotate-whitespace.t
208 test-hgweb-annotate-whitespace.t
209 test-hgweb-bundle.t
209 test-hgweb-bundle.t
210 test-hgweb-csp.t
210 test-hgweb-csp.t
211 test-hgweb-descend-empties.t
211 test-hgweb-descend-empties.t
212 test-hgweb-diffs.t
212 test-hgweb-diffs.t
213 test-hgweb-empty.t
213 test-hgweb-empty.t
214 test-hgweb-filelog.t
214 test-hgweb-filelog.t
215 test-hgweb-non-interactive.t
215 test-hgweb-non-interactive.t
216 test-hgweb-raw.t
216 test-hgweb-raw.t
217 test-hgweb-removed.t
217 test-hgweb-removed.t
218 test-hgweb.t
218 test-hgweb.t
219 test-hgwebdir-paths.py
219 test-hgwebdir-paths.py
220 test-hgwebdirsym.t
220 test-hgwebdirsym.t
221 test-histedit-arguments.t
221 test-histedit-arguments.t
222 test-histedit-base.t
222 test-histedit-base.t
223 test-histedit-bookmark-motion.t
223 test-histedit-bookmark-motion.t
224 test-histedit-commute.t
224 test-histedit-commute.t
225 test-histedit-drop.t
225 test-histedit-drop.t
226 test-histedit-edit.t
226 test-histedit-edit.t
227 test-histedit-fold-non-commute.t
227 test-histedit-fold-non-commute.t
228 test-histedit-fold.t
228 test-histedit-fold.t
229 test-histedit-no-backup.t
229 test-histedit-no-backup.t
230 test-histedit-no-change.t
230 test-histedit-no-change.t
231 test-histedit-non-commute-abort.t
231 test-histedit-non-commute-abort.t
232 test-histedit-non-commute.t
232 test-histedit-non-commute.t
233 test-histedit-obsolete.t
233 test-histedit-obsolete.t
234 test-histedit-outgoing.t
234 test-histedit-outgoing.t
235 test-histedit-templates.t
235 test-histedit-templates.t
236 test-http-branchmap.t
236 test-http-branchmap.t
237 test-http-bundle1.t
237 test-http-bundle1.t
238 test-http-clone-r.t
238 test-http-clone-r.t
239 test-http-permissions.t
239 test-http-permissions.t
240 test-http.t
240 test-http.t
241 test-hybridencode.py
241 test-hybridencode.py
242 test-i18n.t
242 test-i18n.t
243 test-identify.t
243 test-identify.t
244 test-impexp-branch.t
244 test-impexp-branch.t
245 test-import-bypass.t
245 test-import-bypass.t
246 test-import-eol.t
246 test-import-eol.t
247 test-import-merge.t
247 test-import-merge.t
248 test-import-unknown.t
248 test-import-unknown.t
249 test-import.t
249 test-import.t
250 test-imports-checker.t
250 test-imports-checker.t
251 test-incoming-outgoing.t
251 test-incoming-outgoing.t
252 test-inherit-mode.t
252 test-inherit-mode.t
253 test-init.t
253 test-init.t
254 test-issue1089.t
254 test-issue1089.t
255 test-issue1102.t
255 test-issue1102.t
256 test-issue1175.t
256 test-issue1175.t
257 test-issue1306.t
257 test-issue1306.t
258 test-issue1438.t
258 test-issue1438.t
259 test-issue1502.t
259 test-issue1502.t
260 test-issue1802.t
260 test-issue1802.t
261 test-issue1877.t
261 test-issue1877.t
262 test-issue1993.t
262 test-issue1993.t
263 test-issue2137.t
263 test-issue2137.t
264 test-issue3084.t
264 test-issue3084.t
265 test-issue4074.t
265 test-issue4074.t
266 test-issue522.t
266 test-issue522.t
267 test-issue586.t
267 test-issue586.t
268 test-issue5979.t
268 test-issue5979.t
269 test-issue612.t
269 test-issue612.t
270 test-issue619.t
270 test-issue619.t
271 test-issue660.t
271 test-issue660.t
272 test-issue672.t
272 test-issue672.t
273 test-issue842.t
273 test-issue842.t
274 test-journal-exists.t
274 test-journal-exists.t
275 test-journal-share.t
275 test-journal-share.t
276 test-journal.t
276 test-journal.t
277 test-known.t
277 test-known.t
278 test-largefiles-cache.t
278 test-largefiles-cache.t
279 test-largefiles-misc.t
279 test-largefiles-misc.t
280 test-largefiles-small-disk.t
280 test-largefiles-small-disk.t
281 test-largefiles-update.t
281 test-largefiles-update.t
282 test-largefiles.t
282 test-largefiles.t
283 test-lfs-largefiles.t
283 test-lfs-largefiles.t
284 test-lfs-pointer.py
284 test-lfs-pointer.py
285 test-linelog.py
285 test-linelog.py
286 test-linerange.py
286 test-linerange.py
287 test-locate.t
287 test-locate.t
288 test-lock-badness.t
288 test-lock-badness.t
289 test-log-linerange.t
289 test-log-linerange.t
290 test-log.t
290 test-log.t
291 test-logexchange.t
291 test-logexchange.t
292 test-lrucachedict.py
292 test-lrucachedict.py
293 test-mactext.t
293 test-mactext.t
294 test-mailmap.t
294 test-mailmap.t
295 test-manifest-merging.t
295 test-manifest-merging.t
296 test-manifest.py
296 test-manifest.py
297 test-manifest.t
297 test-manifest.t
298 test-match.py
298 test-match.py
299 test-mdiff.py
299 test-mdiff.py
300 test-merge-changedelete.t
300 test-merge-changedelete.t
301 test-merge-closedheads.t
301 test-merge-closedheads.t
302 test-merge-commit.t
302 test-merge-commit.t
303 test-merge-criss-cross.t
303 test-merge-criss-cross.t
304 test-merge-default.t
304 test-merge-default.t
305 test-merge-force.t
305 test-merge-force.t
306 test-merge-halt.t
306 test-merge-halt.t
307 test-merge-internal-tools-pattern.t
307 test-merge-internal-tools-pattern.t
308 test-merge-local.t
308 test-merge-local.t
309 test-merge-no-file-change.t
309 test-merge-no-file-change.t
310 test-merge-remove.t
310 test-merge-remove.t
311 test-merge-revert.t
311 test-merge-revert.t
312 test-merge-revert2.t
312 test-merge-revert2.t
313 test-merge-subrepos.t
313 test-merge-subrepos.t
314 test-merge-symlinks.t
314 test-merge-symlinks.t
315 test-merge-tools.t
315 test-merge-tools.t
316 test-merge-types.t
316 test-merge-types.t
317 test-merge1.t
317 test-merge1.t
318 test-merge10.t
318 test-merge10.t
319 test-merge2.t
319 test-merge2.t
320 test-merge4.t
320 test-merge4.t
321 test-merge5.t
321 test-merge5.t
322 test-merge6.t
322 test-merge6.t
323 test-merge7.t
323 test-merge7.t
324 test-merge8.t
324 test-merge8.t
325 test-merge9.t
325 test-merge9.t
326 test-minifileset.py
326 test-minifileset.py
327 test-minirst.py
327 test-minirst.py
328 test-mq-git.t
328 test-mq-git.t
329 test-mq-guards.t
329 test-mq-guards.t
330 test-mq-header-date.t
330 test-mq-header-date.t
331 test-mq-header-from.t
331 test-mq-header-from.t
332 test-mq-merge.t
332 test-mq-merge.t
333 test-mq-pull-from-bundle.t
333 test-mq-pull-from-bundle.t
334 test-mq-qclone-http.t
334 test-mq-qclone-http.t
335 test-mq-qdelete.t
335 test-mq-qdelete.t
336 test-mq-qdiff.t
336 test-mq-qdiff.t
337 test-mq-qfold.t
337 test-mq-qfold.t
338 test-mq-qgoto.t
338 test-mq-qgoto.t
339 test-mq-qimport-fail-cleanup.t
339 test-mq-qimport-fail-cleanup.t
340 test-mq-qnew.t
340 test-mq-qnew.t
341 test-mq-qpush-exact.t
341 test-mq-qpush-exact.t
342 test-mq-qpush-fail.t
342 test-mq-qpush-fail.t
343 test-mq-qqueue.t
343 test-mq-qqueue.t
344 test-mq-qrefresh-interactive.t
344 test-mq-qrefresh-interactive.t
345 test-mq-qrefresh-replace-log-message.t
345 test-mq-qrefresh-replace-log-message.t
346 test-mq-qrefresh.t
346 test-mq-qrefresh.t
347 test-mq-qrename.t
347 test-mq-qrename.t
348 test-mq-qsave.t
348 test-mq-qsave.t
349 test-mq-safety.t
349 test-mq-safety.t
350 test-mq-subrepo.t
350 test-mq-subrepo.t
351 test-mq-symlinks.t
351 test-mq-symlinks.t
352 test-mq.t
352 test-mq.t
353 test-mv-cp-st-diff.t
353 test-mv-cp-st-diff.t
354 test-narrow-acl.t
354 test-narrow-acl.t
355 test-narrow-archive.t
355 test-narrow-archive.t
356 test-narrow-clone-no-ellipsis.t
356 test-narrow-clone-no-ellipsis.t
357 test-narrow-clone-non-narrow-server.t
357 test-narrow-clone-non-narrow-server.t
358 test-narrow-clone-nonlinear.t
358 test-narrow-clone-nonlinear.t
359 test-narrow-clone.t
359 test-narrow-clone.t
360 test-narrow-commit.t
360 test-narrow-commit.t
361 test-narrow-copies.t
361 test-narrow-copies.t
362 test-narrow-debugcommands.t
362 test-narrow-debugcommands.t
363 test-narrow-debugrebuilddirstate.t
363 test-narrow-debugrebuilddirstate.t
364 test-narrow-exchange-merges.t
364 test-narrow-exchange-merges.t
365 test-narrow-exchange.t
365 test-narrow-exchange.t
366 test-narrow-expanddirstate.t
366 test-narrow-expanddirstate.t
367 test-narrow-merge.t
367 test-narrow-merge.t
368 test-narrow-patch.t
368 test-narrow-patch.t
369 test-narrow-patterns.t
369 test-narrow-patterns.t
370 test-narrow-pull.t
370 test-narrow-pull.t
371 test-narrow-rebase.t
371 test-narrow-rebase.t
372 test-narrow-shallow-merges.t
372 test-narrow-shallow-merges.t
373 test-narrow-shallow.t
373 test-narrow-shallow.t
374 test-narrow-strip.t
374 test-narrow-strip.t
375 test-narrow-trackedcmd.t
375 test-narrow-trackedcmd.t
376 test-narrow-update.t
376 test-narrow-update.t
377 test-narrow-widen-no-ellipsis.t
377 test-narrow-widen-no-ellipsis.t
378 test-narrow-widen.t
378 test-narrow-widen.t
379 test-narrow.t
379 test-narrow.t
380 test-nested-repo.t
380 test-nested-repo.t
381 test-newbranch.t
381 test-newbranch.t
382 test-newercgi.t
382 test-newercgi.t
383 test-nointerrupt.t
383 test-nointerrupt.t
384 test-obshistory.t
384 test-obshistory.t
385 test-obsmarker-template.t
385 test-obsmarker-template.t
386 test-obsmarkers-effectflag.t
386 test-obsmarkers-effectflag.t
387 test-obsolete-bounds-checking.t
387 test-obsolete-bounds-checking.t
388 test-obsolete-bundle-strip.t
388 test-obsolete-bundle-strip.t
389 test-obsolete-changeset-exchange.t
389 test-obsolete-changeset-exchange.t
390 test-obsolete-checkheads.t
390 test-obsolete-checkheads.t
391 test-obsolete-distributed.t
391 test-obsolete-distributed.t
392 test-obsolete-divergent.t
392 test-obsolete-divergent.t
393 test-obsolete-tag-cache.t
393 test-obsolete-tag-cache.t
394 test-obsolete.t
394 test-obsolete.t
395 test-pager-legacy.t
395 test-pager.t
396 test-pager.t
396 test-parents.t
397 test-parents.t
397 test-parseindex2.py
398 test-parseindex2.py
398 test-patch-offset.t
399 test-patch-offset.t
399 test-patch.t
400 test-patch.t
400 test-patchbomb-bookmark.t
401 test-patchbomb-bookmark.t
401 test-patchbomb-tls.t
402 test-patchbomb-tls.t
402 test-patchbomb.t
403 test-patchbomb.t
403 test-pathconflicts-merge.t
404 test-pathconflicts-merge.t
404 test-pathconflicts-update.t
405 test-pathconflicts-update.t
405 test-pathencode.py
406 test-pathencode.py
406 test-pending.t
407 test-pending.t
407 test-permissions.t
408 test-permissions.t
408 test-phases-exchange.t
409 test-phases-exchange.t
409 test-phases.t
410 test-phases.t
410 test-progress.t
411 test-progress.t
411 test-pull-branch.t
412 test-pull-branch.t
412 test-pull-http.t
413 test-pull-http.t
413 test-pull-permission.t
414 test-pull-permission.t
414 test-pull-pull-corruption.t
415 test-pull-pull-corruption.t
415 test-pull-r.t
416 test-pull-r.t
416 test-pull-update.t
417 test-pull-update.t
417 test-pull.t
418 test-pull.t
418 test-purge.t
419 test-purge.t
419 test-push-cgi.t
420 test-push-cgi.t
420 test-push-checkheads-partial-C1.t
421 test-push-checkheads-partial-C1.t
421 test-push-checkheads-partial-C2.t
422 test-push-checkheads-partial-C2.t
422 test-push-checkheads-partial-C3.t
423 test-push-checkheads-partial-C3.t
423 test-push-checkheads-partial-C4.t
424 test-push-checkheads-partial-C4.t
424 test-push-checkheads-pruned-B1.t
425 test-push-checkheads-pruned-B1.t
425 test-push-checkheads-pruned-B2.t
426 test-push-checkheads-pruned-B2.t
426 test-push-checkheads-pruned-B3.t
427 test-push-checkheads-pruned-B3.t
427 test-push-checkheads-pruned-B4.t
428 test-push-checkheads-pruned-B4.t
428 test-push-checkheads-pruned-B5.t
429 test-push-checkheads-pruned-B5.t
429 test-push-checkheads-pruned-B6.t
430 test-push-checkheads-pruned-B6.t
430 test-push-checkheads-pruned-B7.t
431 test-push-checkheads-pruned-B7.t
431 test-push-checkheads-pruned-B8.t
432 test-push-checkheads-pruned-B8.t
432 test-push-checkheads-superceed-A1.t
433 test-push-checkheads-superceed-A1.t
433 test-push-checkheads-superceed-A2.t
434 test-push-checkheads-superceed-A2.t
434 test-push-checkheads-superceed-A3.t
435 test-push-checkheads-superceed-A3.t
435 test-push-checkheads-superceed-A4.t
436 test-push-checkheads-superceed-A4.t
436 test-push-checkheads-superceed-A5.t
437 test-push-checkheads-superceed-A5.t
437 test-push-checkheads-superceed-A6.t
438 test-push-checkheads-superceed-A6.t
438 test-push-checkheads-superceed-A7.t
439 test-push-checkheads-superceed-A7.t
439 test-push-checkheads-superceed-A8.t
440 test-push-checkheads-superceed-A8.t
440 test-push-checkheads-unpushed-D1.t
441 test-push-checkheads-unpushed-D1.t
441 test-push-checkheads-unpushed-D2.t
442 test-push-checkheads-unpushed-D2.t
442 test-push-checkheads-unpushed-D3.t
443 test-push-checkheads-unpushed-D3.t
443 test-push-checkheads-unpushed-D4.t
444 test-push-checkheads-unpushed-D4.t
444 test-push-checkheads-unpushed-D5.t
445 test-push-checkheads-unpushed-D5.t
445 test-push-checkheads-unpushed-D6.t
446 test-push-checkheads-unpushed-D6.t
446 test-push-checkheads-unpushed-D7.t
447 test-push-checkheads-unpushed-D7.t
447 test-push-http.t
448 test-push-http.t
448 test-push-warn.t
449 test-push-warn.t
449 test-push.t
450 test-push.t
450 test-pushvars.t
451 test-pushvars.t
451 test-qrecord.t
452 test-qrecord.t
452 test-rebase-abort.t
453 test-rebase-abort.t
453 test-rebase-backup.t
454 test-rebase-backup.t
454 test-rebase-base-flag.t
455 test-rebase-base-flag.t
455 test-rebase-bookmarks.t
456 test-rebase-bookmarks.t
456 test-rebase-brute-force.t
457 test-rebase-brute-force.t
457 test-rebase-cache.t
458 test-rebase-cache.t
458 test-rebase-check-restore.t
459 test-rebase-check-restore.t
459 test-rebase-collapse.t
460 test-rebase-collapse.t
460 test-rebase-conflicts.t
461 test-rebase-conflicts.t
461 test-rebase-dest.t
462 test-rebase-dest.t
462 test-rebase-detach.t
463 test-rebase-detach.t
463 test-rebase-emptycommit.t
464 test-rebase-emptycommit.t
464 test-rebase-inmemory.t
465 test-rebase-inmemory.t
465 test-rebase-interruptions.t
466 test-rebase-interruptions.t
466 test-rebase-issue-noparam-single-rev.t
467 test-rebase-issue-noparam-single-rev.t
467 test-rebase-legacy.t
468 test-rebase-legacy.t
468 test-rebase-mq-skip.t
469 test-rebase-mq-skip.t
469 test-rebase-mq.t
470 test-rebase-mq.t
470 test-rebase-named-branches.t
471 test-rebase-named-branches.t
471 test-rebase-newancestor.t
472 test-rebase-newancestor.t
472 test-rebase-obsolete.t
473 test-rebase-obsolete.t
473 test-rebase-parameters.t
474 test-rebase-parameters.t
474 test-rebase-partial.t
475 test-rebase-partial.t
475 test-rebase-pull.t
476 test-rebase-pull.t
476 test-rebase-rename.t
477 test-rebase-rename.t
477 test-rebase-scenario-global.t
478 test-rebase-scenario-global.t
478 test-rebase-templates.t
479 test-rebase-templates.t
479 test-rebase-transaction.t
480 test-rebase-transaction.t
480 test-rebuildstate.t
481 test-rebuildstate.t
481 test-record.t
482 test-record.t
482 test-relink.t
483 test-relink.t
483 test-remove.t
484 test-remove.t
484 test-removeemptydirs.t
485 test-removeemptydirs.t
485 test-rename-after-merge.t
486 test-rename-after-merge.t
486 test-rename-dir-merge.t
487 test-rename-dir-merge.t
487 test-rename-merge1.t
488 test-rename-merge1.t
488 test-rename-merge2.t
489 test-rename-merge2.t
489 test-rename.t
490 test-rename.t
490 test-repair-strip.t
491 test-repair-strip.t
491 test-repo-compengines.t
492 test-repo-compengines.t
492 test-resolve.t
493 test-resolve.t
493 test-revert-flags.t
494 test-revert-flags.t
494 test-revert-interactive.t
495 test-revert-interactive.t
495 test-revert-unknown.t
496 test-revert-unknown.t
496 test-revert.t
497 test-revert.t
497 test-revisions.t
498 test-revisions.t
498 test-revlog-ancestry.py
499 test-revlog-ancestry.py
499 test-revlog-group-emptyiter.t
500 test-revlog-group-emptyiter.t
500 test-revlog-mmapindex.t
501 test-revlog-mmapindex.t
501 test-revlog-packentry.t
502 test-revlog-packentry.t
502 test-revlog-raw.py
503 test-revlog-raw.py
503 test-revlog-v2.t
504 test-revlog-v2.t
504 test-revset-dirstate-parents.t
505 test-revset-dirstate-parents.t
505 test-revset-legacy-lookup.t
506 test-revset-legacy-lookup.t
506 test-revset-outgoing.t
507 test-revset-outgoing.t
507 test-rollback.t
508 test-rollback.t
508 test-run-tests.py
509 test-run-tests.py
509 test-run-tests.t
510 test-run-tests.t
510 test-schemes.t
511 test-schemes.t
511 test-serve.t
512 test-serve.t
512 test-setdiscovery.t
513 test-setdiscovery.t
513 test-share.t
514 test-share.t
514 test-shelve.t
515 test-shelve.t
515 test-show-stack.t
516 test-show-stack.t
516 test-show-work.t
517 test-show-work.t
517 test-show.t
518 test-show.t
518 test-simple-update.t
519 test-simple-update.t
519 test-simplekeyvaluefile.py
520 test-simplekeyvaluefile.py
520 test-simplemerge.py
521 test-simplemerge.py
521 test-single-head.t
522 test-single-head.t
522 test-sparse-clear.t
523 test-sparse-clear.t
523 test-sparse-clone.t
524 test-sparse-clone.t
524 test-sparse-import.t
525 test-sparse-import.t
525 test-sparse-merges.t
526 test-sparse-merges.t
526 test-sparse-profiles.t
527 test-sparse-profiles.t
527 test-sparse-requirement.t
528 test-sparse-requirement.t
528 test-sparse-verbose-json.t
529 test-sparse-verbose-json.t
529 test-sparse.t
530 test-sparse.t
530 test-split.t
531 test-split.t
531 test-ssh-bundle1.t
532 test-ssh-bundle1.t
532 test-ssh-clone-r.t
533 test-ssh-clone-r.t
533 test-ssh-proto-unbundle.t
534 test-ssh-proto-unbundle.t
534 test-ssh-proto.t
535 test-ssh-proto.t
535 test-ssh.t
536 test-ssh.t
536 test-sshserver.py
537 test-sshserver.py
537 test-stack.t
538 test-stack.t
538 test-status-color.t
539 test-status-color.t
539 test-status-inprocess.py
540 test-status-inprocess.py
540 test-status-rev.t
541 test-status-rev.t
541 test-status-terse.t
542 test-status-terse.t
542 test-storage.py
543 test-storage.py
543 test-stream-bundle-v2.t
544 test-stream-bundle-v2.t
544 test-strict.t
545 test-strict.t
545 test-strip-cross.t
546 test-strip-cross.t
546 test-strip.t
547 test-strip.t
547 test-subrepo-deep-nested-change.t
548 test-subrepo-deep-nested-change.t
548 test-subrepo-missing.t
549 test-subrepo-missing.t
549 test-subrepo-paths.t
550 test-subrepo-paths.t
550 test-subrepo-recursion.t
551 test-subrepo-recursion.t
551 test-subrepo-relative-path.t
552 test-subrepo-relative-path.t
552 test-subrepo.t
553 test-subrepo.t
553 test-symlink-os-yes-fs-no.py
554 test-symlink-os-yes-fs-no.py
554 test-symlink-placeholder.t
555 test-symlink-placeholder.t
555 test-symlinks.t
556 test-symlinks.t
556 test-tag.t
557 test-tag.t
557 test-tags.t
558 test-tags.t
558 test-template-basic.t
559 test-template-basic.t
559 test-template-functions.t
560 test-template-functions.t
560 test-template-keywords.t
561 test-template-keywords.t
561 test-template-map.t
562 test-template-map.t
562 test-transplant.t
563 test-transplant.t
563 test-treemanifest.t
564 test-treemanifest.t
564 test-ui-color.py
565 test-ui-color.py
565 test-ui-config.py
566 test-ui-config.py
566 test-ui-verbosity.py
567 test-ui-verbosity.py
567 test-unamend.t
568 test-unamend.t
568 test-unbundlehash.t
569 test-unbundlehash.t
569 test-uncommit.t
570 test-uncommit.t
570 test-unified-test.t
571 test-unified-test.t
571 test-unionrepo.t
572 test-unionrepo.t
572 test-unrelated-pull.t
573 test-unrelated-pull.t
573 test-up-local-change.t
574 test-up-local-change.t
574 test-update-branches.t
575 test-update-branches.t
575 test-update-dest.t
576 test-update-dest.t
576 test-update-issue1456.t
577 test-update-issue1456.t
577 test-update-names.t
578 test-update-names.t
578 test-update-reverse.t
579 test-update-reverse.t
579 test-upgrade-repo.t
580 test-upgrade-repo.t
580 test-url-download.t
581 test-url-download.t
581 test-url-rev.t
582 test-url-rev.t
582 test-url.py
583 test-url.py
583 test-username-newline.t
584 test-username-newline.t
584 test-util.py
585 test-util.py
585 test-verify.t
586 test-verify.t
586 test-walk.t
587 test-walk.t
587 test-walkrepo.py
588 test-walkrepo.py
588 test-websub.t
589 test-websub.t
589 test-win32text.t
590 test-win32text.t
590 test-wireproto-clientreactor.py
591 test-wireproto-clientreactor.py
591 test-wireproto-command-branchmap.t
592 test-wireproto-command-branchmap.t
592 test-wireproto-command-changesetdata.t
593 test-wireproto-command-changesetdata.t
593 test-wireproto-command-filedata.t
594 test-wireproto-command-filedata.t
594 test-wireproto-command-filesdata.t
595 test-wireproto-command-filesdata.t
595 test-wireproto-command-heads.t
596 test-wireproto-command-heads.t
596 test-wireproto-command-listkeys.t
597 test-wireproto-command-listkeys.t
597 test-wireproto-command-lookup.t
598 test-wireproto-command-lookup.t
598 test-wireproto-command-manifestdata.t
599 test-wireproto-command-manifestdata.t
599 test-wireproto-command-pushkey.t
600 test-wireproto-command-pushkey.t
600 test-wireproto-framing.py
601 test-wireproto-framing.py
601 test-wireproto-serverreactor.py
602 test-wireproto-serverreactor.py
602 test-wireproto.py
603 test-wireproto.py
603 test-wsgirequest.py
604 test-wsgirequest.py
604 test-xdg.t
605 test-xdg.t
@@ -1,1924 +1,1925
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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import collections
10 import collections
11 import contextlib
11 import contextlib
12 import errno
12 import errno
13 import getpass
13 import getpass
14 import inspect
14 import inspect
15 import os
15 import os
16 import re
16 import re
17 import signal
17 import signal
18 import socket
18 import socket
19 import subprocess
19 import subprocess
20 import sys
20 import sys
21 import traceback
21 import traceback
22
22
23 from .i18n import _
23 from .i18n import _
24 from .node import hex
24 from .node import hex
25
25
26 from . import (
26 from . import (
27 color,
27 color,
28 config,
28 config,
29 configitems,
29 configitems,
30 encoding,
30 encoding,
31 error,
31 error,
32 formatter,
32 formatter,
33 progress,
33 progress,
34 pycompat,
34 pycompat,
35 rcutil,
35 rcutil,
36 scmutil,
36 scmutil,
37 util,
37 util,
38 )
38 )
39 from .utils import (
39 from .utils import (
40 dateutil,
40 dateutil,
41 procutil,
41 procutil,
42 stringutil,
42 stringutil,
43 )
43 )
44
44
45 urlreq = util.urlreq
45 urlreq = util.urlreq
46
46
47 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
47 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
48 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
48 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
49 if not c.isalnum())
49 if not c.isalnum())
50
50
51 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
51 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
52 tweakrc = b"""
52 tweakrc = b"""
53 [ui]
53 [ui]
54 # The rollback command is dangerous. As a rule, don't use it.
54 # The rollback command is dangerous. As a rule, don't use it.
55 rollback = False
55 rollback = False
56 # Make `hg status` report copy information
56 # Make `hg status` report copy information
57 statuscopies = yes
57 statuscopies = yes
58 # Prefer curses UIs when available. Revert to plain-text with `text`.
58 # Prefer curses UIs when available. Revert to plain-text with `text`.
59 interface = curses
59 interface = curses
60
60
61 [commands]
61 [commands]
62 # Grep working directory by default.
62 # Grep working directory by default.
63 grep.all-files = True
63 grep.all-files = True
64 # Make `hg status` emit cwd-relative paths by default.
64 # Make `hg status` emit cwd-relative paths by default.
65 status.relative = yes
65 status.relative = yes
66 # Refuse to perform an `hg update` that would cause a file content merge
66 # Refuse to perform an `hg update` that would cause a file content merge
67 update.check = noconflict
67 update.check = noconflict
68 # Show conflicts information in `hg status`
68 # Show conflicts information in `hg status`
69 status.verbose = True
69 status.verbose = True
70 # Refuse to perform `hg resolve --mark` on files that still have conflict
70 # Refuse to perform `hg resolve --mark` on files that still have conflict
71 # markers
71 # markers
72 resolve.mark-check = abort
72 resolve.mark-check = abort
73
73
74 [diff]
74 [diff]
75 git = 1
75 git = 1
76 showfunc = 1
76 showfunc = 1
77 word-diff = 1
77 word-diff = 1
78 """
78 """
79
79
80 samplehgrcs = {
80 samplehgrcs = {
81 'user':
81 'user':
82 b"""# example user config (see 'hg help config' for more info)
82 b"""# example user config (see 'hg help config' for more info)
83 [ui]
83 [ui]
84 # name and email, e.g.
84 # name and email, e.g.
85 # username = Jane Doe <jdoe@example.com>
85 # username = Jane Doe <jdoe@example.com>
86 username =
86 username =
87
87
88 # We recommend enabling tweakdefaults to get slight improvements to
88 # We recommend enabling tweakdefaults to get slight improvements to
89 # the UI over time. Make sure to set HGPLAIN in the environment when
89 # the UI over time. Make sure to set HGPLAIN in the environment when
90 # writing scripts!
90 # writing scripts!
91 # tweakdefaults = True
91 # tweakdefaults = True
92
92
93 # uncomment to disable color in command output
93 # uncomment to disable color in command output
94 # (see 'hg help color' for details)
94 # (see 'hg help color' for details)
95 # color = never
95 # color = never
96
96
97 # uncomment to disable command output pagination
97 # uncomment to disable command output pagination
98 # (see 'hg help pager' for details)
98 # (see 'hg help pager' for details)
99 # paginate = never
99 # paginate = never
100
100
101 [extensions]
101 [extensions]
102 # uncomment these lines to enable some popular extensions
102 # uncomment these lines to enable some popular extensions
103 # (see 'hg help extensions' for more info)
103 # (see 'hg help extensions' for more info)
104 #
104 #
105 # churn =
105 # churn =
106 """,
106 """,
107
107
108 'cloned':
108 'cloned':
109 b"""# example repository config (see 'hg help config' for more info)
109 b"""# example repository config (see 'hg help config' for more info)
110 [paths]
110 [paths]
111 default = %s
111 default = %s
112
112
113 # path aliases to other clones of this repo in URLs or filesystem paths
113 # path aliases to other clones of this repo in URLs or filesystem paths
114 # (see 'hg help config.paths' for more info)
114 # (see 'hg help config.paths' for more info)
115 #
115 #
116 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
116 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
117 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
117 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
118 # my-clone = /home/jdoe/jdoes-clone
118 # my-clone = /home/jdoe/jdoes-clone
119
119
120 [ui]
120 [ui]
121 # name and email (local to this repository, optional), e.g.
121 # name and email (local to this repository, optional), e.g.
122 # username = Jane Doe <jdoe@example.com>
122 # username = Jane Doe <jdoe@example.com>
123 """,
123 """,
124
124
125 'local':
125 'local':
126 b"""# example repository config (see 'hg help config' for more info)
126 b"""# example repository config (see 'hg help config' for more info)
127 [paths]
127 [paths]
128 # path aliases to other clones of this repo in URLs or filesystem paths
128 # path aliases to other clones of this repo in URLs or filesystem paths
129 # (see 'hg help config.paths' for more info)
129 # (see 'hg help config.paths' for more info)
130 #
130 #
131 # default = http://example.com/hg/example-repo
131 # default = http://example.com/hg/example-repo
132 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
132 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
133 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
133 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
134 # my-clone = /home/jdoe/jdoes-clone
134 # my-clone = /home/jdoe/jdoes-clone
135
135
136 [ui]
136 [ui]
137 # name and email (local to this repository, optional), e.g.
137 # name and email (local to this repository, optional), e.g.
138 # username = Jane Doe <jdoe@example.com>
138 # username = Jane Doe <jdoe@example.com>
139 """,
139 """,
140
140
141 'global':
141 'global':
142 b"""# example system-wide hg config (see 'hg help config' for more info)
142 b"""# example system-wide hg config (see 'hg help config' for more info)
143
143
144 [ui]
144 [ui]
145 # uncomment to disable color in command output
145 # uncomment to disable color in command output
146 # (see 'hg help color' for details)
146 # (see 'hg help color' for details)
147 # color = never
147 # color = never
148
148
149 # uncomment to disable command output pagination
149 # uncomment to disable command output pagination
150 # (see 'hg help pager' for details)
150 # (see 'hg help pager' for details)
151 # paginate = never
151 # paginate = never
152
152
153 [extensions]
153 [extensions]
154 # uncomment these lines to enable some popular extensions
154 # uncomment these lines to enable some popular extensions
155 # (see 'hg help extensions' for more info)
155 # (see 'hg help extensions' for more info)
156 #
156 #
157 # blackbox =
157 # blackbox =
158 # churn =
158 # churn =
159 """,
159 """,
160 }
160 }
161
161
162 def _maybestrurl(maybebytes):
162 def _maybestrurl(maybebytes):
163 return pycompat.rapply(pycompat.strurl, maybebytes)
163 return pycompat.rapply(pycompat.strurl, maybebytes)
164
164
165 def _maybebytesurl(maybestr):
165 def _maybebytesurl(maybestr):
166 return pycompat.rapply(pycompat.bytesurl, maybestr)
166 return pycompat.rapply(pycompat.bytesurl, maybestr)
167
167
168 class httppasswordmgrdbproxy(object):
168 class httppasswordmgrdbproxy(object):
169 """Delays loading urllib2 until it's needed."""
169 """Delays loading urllib2 until it's needed."""
170 def __init__(self):
170 def __init__(self):
171 self._mgr = None
171 self._mgr = None
172
172
173 def _get_mgr(self):
173 def _get_mgr(self):
174 if self._mgr is None:
174 if self._mgr is None:
175 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
175 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
176 return self._mgr
176 return self._mgr
177
177
178 def add_password(self, realm, uris, user, passwd):
178 def add_password(self, realm, uris, user, passwd):
179 return self._get_mgr().add_password(
179 return self._get_mgr().add_password(
180 _maybestrurl(realm), _maybestrurl(uris),
180 _maybestrurl(realm), _maybestrurl(uris),
181 _maybestrurl(user), _maybestrurl(passwd))
181 _maybestrurl(user), _maybestrurl(passwd))
182
182
183 def find_user_password(self, realm, uri):
183 def find_user_password(self, realm, uri):
184 mgr = self._get_mgr()
184 mgr = self._get_mgr()
185 return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
185 return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
186 _maybestrurl(uri)))
186 _maybestrurl(uri)))
187
187
188 def _catchterm(*args):
188 def _catchterm(*args):
189 raise error.SignalInterrupt
189 raise error.SignalInterrupt
190
190
191 # unique object used to detect no default value has been provided when
191 # unique object used to detect no default value has been provided when
192 # retrieving configuration value.
192 # retrieving configuration value.
193 _unset = object()
193 _unset = object()
194
194
195 # _reqexithandlers: callbacks run at the end of a request
195 # _reqexithandlers: callbacks run at the end of a request
196 _reqexithandlers = []
196 _reqexithandlers = []
197
197
198 class ui(object):
198 class ui(object):
199 def __init__(self, src=None):
199 def __init__(self, src=None):
200 """Create a fresh new ui object if no src given
200 """Create a fresh new ui object if no src given
201
201
202 Use uimod.ui.load() to create a ui which knows global and user configs.
202 Use uimod.ui.load() to create a ui which knows global and user configs.
203 In most cases, you should use ui.copy() to create a copy of an existing
203 In most cases, you should use ui.copy() to create a copy of an existing
204 ui object.
204 ui object.
205 """
205 """
206 # _buffers: used for temporary capture of output
206 # _buffers: used for temporary capture of output
207 self._buffers = []
207 self._buffers = []
208 # 3-tuple describing how each buffer in the stack behaves.
208 # 3-tuple describing how each buffer in the stack behaves.
209 # Values are (capture stderr, capture subprocesses, apply labels).
209 # Values are (capture stderr, capture subprocesses, apply labels).
210 self._bufferstates = []
210 self._bufferstates = []
211 # When a buffer is active, defines whether we are expanding labels.
211 # When a buffer is active, defines whether we are expanding labels.
212 # This exists to prevent an extra list lookup.
212 # This exists to prevent an extra list lookup.
213 self._bufferapplylabels = None
213 self._bufferapplylabels = None
214 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
214 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
215 self._reportuntrusted = True
215 self._reportuntrusted = True
216 self._knownconfig = configitems.coreitems
216 self._knownconfig = configitems.coreitems
217 self._ocfg = config.config() # overlay
217 self._ocfg = config.config() # overlay
218 self._tcfg = config.config() # trusted
218 self._tcfg = config.config() # trusted
219 self._ucfg = config.config() # untrusted
219 self._ucfg = config.config() # untrusted
220 self._trustusers = set()
220 self._trustusers = set()
221 self._trustgroups = set()
221 self._trustgroups = set()
222 self.callhooks = True
222 self.callhooks = True
223 # Insecure server connections requested.
223 # Insecure server connections requested.
224 self.insecureconnections = False
224 self.insecureconnections = False
225 # Blocked time
225 # Blocked time
226 self.logblockedtimes = False
226 self.logblockedtimes = False
227 # color mode: see mercurial/color.py for possible value
227 # color mode: see mercurial/color.py for possible value
228 self._colormode = None
228 self._colormode = None
229 self._terminfoparams = {}
229 self._terminfoparams = {}
230 self._styles = {}
230 self._styles = {}
231 self._uninterruptible = False
231 self._uninterruptible = False
232
232
233 if src:
233 if src:
234 self.fout = src.fout
234 self.fout = src.fout
235 self.ferr = src.ferr
235 self.ferr = src.ferr
236 self.fin = src.fin
236 self.fin = src.fin
237 self._finoutredirected = src._finoutredirected
237 self._finoutredirected = src._finoutredirected
238 self.pageractive = src.pageractive
238 self.pageractive = src.pageractive
239 self._disablepager = src._disablepager
239 self._disablepager = src._disablepager
240 self._tweaked = src._tweaked
240 self._tweaked = src._tweaked
241
241
242 self._tcfg = src._tcfg.copy()
242 self._tcfg = src._tcfg.copy()
243 self._ucfg = src._ucfg.copy()
243 self._ucfg = src._ucfg.copy()
244 self._ocfg = src._ocfg.copy()
244 self._ocfg = src._ocfg.copy()
245 self._trustusers = src._trustusers.copy()
245 self._trustusers = src._trustusers.copy()
246 self._trustgroups = src._trustgroups.copy()
246 self._trustgroups = src._trustgroups.copy()
247 self.environ = src.environ
247 self.environ = src.environ
248 self.callhooks = src.callhooks
248 self.callhooks = src.callhooks
249 self.insecureconnections = src.insecureconnections
249 self.insecureconnections = src.insecureconnections
250 self._colormode = src._colormode
250 self._colormode = src._colormode
251 self._terminfoparams = src._terminfoparams.copy()
251 self._terminfoparams = src._terminfoparams.copy()
252 self._styles = src._styles.copy()
252 self._styles = src._styles.copy()
253
253
254 self.fixconfig()
254 self.fixconfig()
255
255
256 self.httppasswordmgrdb = src.httppasswordmgrdb
256 self.httppasswordmgrdb = src.httppasswordmgrdb
257 self._blockedtimes = src._blockedtimes
257 self._blockedtimes = src._blockedtimes
258 else:
258 else:
259 self.fout = procutil.stdout
259 self.fout = procutil.stdout
260 self.ferr = procutil.stderr
260 self.ferr = procutil.stderr
261 self.fin = procutil.stdin
261 self.fin = procutil.stdin
262 self._finoutredirected = False
262 self._finoutredirected = False
263 self.pageractive = False
263 self.pageractive = False
264 self._disablepager = False
264 self._disablepager = False
265 self._tweaked = False
265 self._tweaked = False
266
266
267 # shared read-only environment
267 # shared read-only environment
268 self.environ = encoding.environ
268 self.environ = encoding.environ
269
269
270 self.httppasswordmgrdb = httppasswordmgrdbproxy()
270 self.httppasswordmgrdb = httppasswordmgrdbproxy()
271 self._blockedtimes = collections.defaultdict(int)
271 self._blockedtimes = collections.defaultdict(int)
272
272
273 allowed = self.configlist('experimental', 'exportableenviron')
273 allowed = self.configlist('experimental', 'exportableenviron')
274 if '*' in allowed:
274 if '*' in allowed:
275 self._exportableenviron = self.environ
275 self._exportableenviron = self.environ
276 else:
276 else:
277 self._exportableenviron = {}
277 self._exportableenviron = {}
278 for k in allowed:
278 for k in allowed:
279 if k in self.environ:
279 if k in self.environ:
280 self._exportableenviron[k] = self.environ[k]
280 self._exportableenviron[k] = self.environ[k]
281
281
282 @classmethod
282 @classmethod
283 def load(cls):
283 def load(cls):
284 """Create a ui and load global and user configs"""
284 """Create a ui and load global and user configs"""
285 u = cls()
285 u = cls()
286 # we always trust global config files and environment variables
286 # we always trust global config files and environment variables
287 for t, f in rcutil.rccomponents():
287 for t, f in rcutil.rccomponents():
288 if t == 'path':
288 if t == 'path':
289 u.readconfig(f, trust=True)
289 u.readconfig(f, trust=True)
290 elif t == 'items':
290 elif t == 'items':
291 sections = set()
291 sections = set()
292 for section, name, value, source in f:
292 for section, name, value, source in f:
293 # do not set u._ocfg
293 # do not set u._ocfg
294 # XXX clean this up once immutable config object is a thing
294 # XXX clean this up once immutable config object is a thing
295 u._tcfg.set(section, name, value, source)
295 u._tcfg.set(section, name, value, source)
296 u._ucfg.set(section, name, value, source)
296 u._ucfg.set(section, name, value, source)
297 sections.add(section)
297 sections.add(section)
298 for section in sections:
298 for section in sections:
299 u.fixconfig(section=section)
299 u.fixconfig(section=section)
300 else:
300 else:
301 raise error.ProgrammingError('unknown rctype: %s' % t)
301 raise error.ProgrammingError('unknown rctype: %s' % t)
302 u._maybetweakdefaults()
302 u._maybetweakdefaults()
303 return u
303 return u
304
304
305 def _maybetweakdefaults(self):
305 def _maybetweakdefaults(self):
306 if not self.configbool('ui', 'tweakdefaults'):
306 if not self.configbool('ui', 'tweakdefaults'):
307 return
307 return
308 if self._tweaked or self.plain('tweakdefaults'):
308 if self._tweaked or self.plain('tweakdefaults'):
309 return
309 return
310
310
311 # Note: it is SUPER IMPORTANT that you set self._tweaked to
311 # Note: it is SUPER IMPORTANT that you set self._tweaked to
312 # True *before* any calls to setconfig(), otherwise you'll get
312 # True *before* any calls to setconfig(), otherwise you'll get
313 # infinite recursion between setconfig and this method.
313 # infinite recursion between setconfig and this method.
314 #
314 #
315 # TODO: We should extract an inner method in setconfig() to
315 # TODO: We should extract an inner method in setconfig() to
316 # avoid this weirdness.
316 # avoid this weirdness.
317 self._tweaked = True
317 self._tweaked = True
318 tmpcfg = config.config()
318 tmpcfg = config.config()
319 tmpcfg.parse('<tweakdefaults>', tweakrc)
319 tmpcfg.parse('<tweakdefaults>', tweakrc)
320 for section in tmpcfg:
320 for section in tmpcfg:
321 for name, value in tmpcfg.items(section):
321 for name, value in tmpcfg.items(section):
322 if not self.hasconfig(section, name):
322 if not self.hasconfig(section, name):
323 self.setconfig(section, name, value, "<tweakdefaults>")
323 self.setconfig(section, name, value, "<tweakdefaults>")
324
324
325 def copy(self):
325 def copy(self):
326 return self.__class__(self)
326 return self.__class__(self)
327
327
328 def resetstate(self):
328 def resetstate(self):
329 """Clear internal state that shouldn't persist across commands"""
329 """Clear internal state that shouldn't persist across commands"""
330 if self._progbar:
330 if self._progbar:
331 self._progbar.resetstate() # reset last-print time of progress bar
331 self._progbar.resetstate() # reset last-print time of progress bar
332 self.httppasswordmgrdb = httppasswordmgrdbproxy()
332 self.httppasswordmgrdb = httppasswordmgrdbproxy()
333
333
334 @contextlib.contextmanager
334 @contextlib.contextmanager
335 def timeblockedsection(self, key):
335 def timeblockedsection(self, key):
336 # this is open-coded below - search for timeblockedsection to find them
336 # this is open-coded below - search for timeblockedsection to find them
337 starttime = util.timer()
337 starttime = util.timer()
338 try:
338 try:
339 yield
339 yield
340 finally:
340 finally:
341 self._blockedtimes[key + '_blocked'] += \
341 self._blockedtimes[key + '_blocked'] += \
342 (util.timer() - starttime) * 1000
342 (util.timer() - starttime) * 1000
343
343
344 @contextlib.contextmanager
344 @contextlib.contextmanager
345 def uninterruptable(self):
345 def uninterruptable(self):
346 """Mark an operation as unsafe.
346 """Mark an operation as unsafe.
347
347
348 Most operations on a repository are safe to interrupt, but a
348 Most operations on a repository are safe to interrupt, but a
349 few are risky (for example repair.strip). This context manager
349 few are risky (for example repair.strip). This context manager
350 lets you advise Mercurial that something risky is happening so
350 lets you advise Mercurial that something risky is happening so
351 that control-C etc can be blocked if desired.
351 that control-C etc can be blocked if desired.
352 """
352 """
353 enabled = self.configbool('experimental', 'nointerrupt')
353 enabled = self.configbool('experimental', 'nointerrupt')
354 if (enabled and
354 if (enabled and
355 self.configbool('experimental', 'nointerrupt-interactiveonly')):
355 self.configbool('experimental', 'nointerrupt-interactiveonly')):
356 enabled = self.interactive()
356 enabled = self.interactive()
357 if self._uninterruptible or not enabled:
357 if self._uninterruptible or not enabled:
358 # if nointerrupt support is turned off, the process isn't
358 # if nointerrupt support is turned off, the process isn't
359 # interactive, or we're already in an uninterruptable
359 # interactive, or we're already in an uninterruptable
360 # block, do nothing.
360 # block, do nothing.
361 yield
361 yield
362 return
362 return
363 def warn():
363 def warn():
364 self.warn(_("shutting down cleanly\n"))
364 self.warn(_("shutting down cleanly\n"))
365 self.warn(
365 self.warn(
366 _("press ^C again to terminate immediately (dangerous)\n"))
366 _("press ^C again to terminate immediately (dangerous)\n"))
367 return True
367 return True
368 with procutil.uninterruptable(warn):
368 with procutil.uninterruptable(warn):
369 try:
369 try:
370 self._uninterruptible = True
370 self._uninterruptible = True
371 yield
371 yield
372 finally:
372 finally:
373 self._uninterruptible = False
373 self._uninterruptible = False
374
374
375 def formatter(self, topic, opts):
375 def formatter(self, topic, opts):
376 return formatter.formatter(self, self, topic, opts)
376 return formatter.formatter(self, self, topic, opts)
377
377
378 def _trusted(self, fp, f):
378 def _trusted(self, fp, f):
379 st = util.fstat(fp)
379 st = util.fstat(fp)
380 if util.isowner(st):
380 if util.isowner(st):
381 return True
381 return True
382
382
383 tusers, tgroups = self._trustusers, self._trustgroups
383 tusers, tgroups = self._trustusers, self._trustgroups
384 if '*' in tusers or '*' in tgroups:
384 if '*' in tusers or '*' in tgroups:
385 return True
385 return True
386
386
387 user = util.username(st.st_uid)
387 user = util.username(st.st_uid)
388 group = util.groupname(st.st_gid)
388 group = util.groupname(st.st_gid)
389 if user in tusers or group in tgroups or user == util.username():
389 if user in tusers or group in tgroups or user == util.username():
390 return True
390 return True
391
391
392 if self._reportuntrusted:
392 if self._reportuntrusted:
393 self.warn(_('not trusting file %s from untrusted '
393 self.warn(_('not trusting file %s from untrusted '
394 'user %s, group %s\n') % (f, user, group))
394 'user %s, group %s\n') % (f, user, group))
395 return False
395 return False
396
396
397 def readconfig(self, filename, root=None, trust=False,
397 def readconfig(self, filename, root=None, trust=False,
398 sections=None, remap=None):
398 sections=None, remap=None):
399 try:
399 try:
400 fp = open(filename, r'rb')
400 fp = open(filename, r'rb')
401 except IOError:
401 except IOError:
402 if not sections: # ignore unless we were looking for something
402 if not sections: # ignore unless we were looking for something
403 return
403 return
404 raise
404 raise
405
405
406 cfg = config.config()
406 cfg = config.config()
407 trusted = sections or trust or self._trusted(fp, filename)
407 trusted = sections or trust or self._trusted(fp, filename)
408
408
409 try:
409 try:
410 cfg.read(filename, fp, sections=sections, remap=remap)
410 cfg.read(filename, fp, sections=sections, remap=remap)
411 fp.close()
411 fp.close()
412 except error.ConfigError as inst:
412 except error.ConfigError as inst:
413 if trusted:
413 if trusted:
414 raise
414 raise
415 self.warn(_("ignored: %s\n") % stringutil.forcebytestr(inst))
415 self.warn(_("ignored: %s\n") % stringutil.forcebytestr(inst))
416
416
417 if self.plain():
417 if self.plain():
418 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
418 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
419 'logtemplate', 'statuscopies', 'style',
419 'logtemplate', 'statuscopies', 'style',
420 'traceback', 'verbose'):
420 'traceback', 'verbose'):
421 if k in cfg['ui']:
421 if k in cfg['ui']:
422 del cfg['ui'][k]
422 del cfg['ui'][k]
423 for k, v in cfg.items('defaults'):
423 for k, v in cfg.items('defaults'):
424 del cfg['defaults'][k]
424 del cfg['defaults'][k]
425 for k, v in cfg.items('commands'):
425 for k, v in cfg.items('commands'):
426 del cfg['commands'][k]
426 del cfg['commands'][k]
427 # Don't remove aliases from the configuration if in the exceptionlist
427 # Don't remove aliases from the configuration if in the exceptionlist
428 if self.plain('alias'):
428 if self.plain('alias'):
429 for k, v in cfg.items('alias'):
429 for k, v in cfg.items('alias'):
430 del cfg['alias'][k]
430 del cfg['alias'][k]
431 if self.plain('revsetalias'):
431 if self.plain('revsetalias'):
432 for k, v in cfg.items('revsetalias'):
432 for k, v in cfg.items('revsetalias'):
433 del cfg['revsetalias'][k]
433 del cfg['revsetalias'][k]
434 if self.plain('templatealias'):
434 if self.plain('templatealias'):
435 for k, v in cfg.items('templatealias'):
435 for k, v in cfg.items('templatealias'):
436 del cfg['templatealias'][k]
436 del cfg['templatealias'][k]
437
437
438 if trusted:
438 if trusted:
439 self._tcfg.update(cfg)
439 self._tcfg.update(cfg)
440 self._tcfg.update(self._ocfg)
440 self._tcfg.update(self._ocfg)
441 self._ucfg.update(cfg)
441 self._ucfg.update(cfg)
442 self._ucfg.update(self._ocfg)
442 self._ucfg.update(self._ocfg)
443
443
444 if root is None:
444 if root is None:
445 root = os.path.expanduser('~')
445 root = os.path.expanduser('~')
446 self.fixconfig(root=root)
446 self.fixconfig(root=root)
447
447
448 def fixconfig(self, root=None, section=None):
448 def fixconfig(self, root=None, section=None):
449 if section in (None, 'paths'):
449 if section in (None, 'paths'):
450 # expand vars and ~
450 # expand vars and ~
451 # translate paths relative to root (or home) into absolute paths
451 # translate paths relative to root (or home) into absolute paths
452 root = root or encoding.getcwd()
452 root = root or encoding.getcwd()
453 for c in self._tcfg, self._ucfg, self._ocfg:
453 for c in self._tcfg, self._ucfg, self._ocfg:
454 for n, p in c.items('paths'):
454 for n, p in c.items('paths'):
455 # Ignore sub-options.
455 # Ignore sub-options.
456 if ':' in n:
456 if ':' in n:
457 continue
457 continue
458 if not p:
458 if not p:
459 continue
459 continue
460 if '%%' in p:
460 if '%%' in p:
461 s = self.configsource('paths', n) or 'none'
461 s = self.configsource('paths', n) or 'none'
462 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
462 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
463 % (n, p, s))
463 % (n, p, s))
464 p = p.replace('%%', '%')
464 p = p.replace('%%', '%')
465 p = util.expandpath(p)
465 p = util.expandpath(p)
466 if not util.hasscheme(p) and not os.path.isabs(p):
466 if not util.hasscheme(p) and not os.path.isabs(p):
467 p = os.path.normpath(os.path.join(root, p))
467 p = os.path.normpath(os.path.join(root, p))
468 c.set("paths", n, p)
468 c.set("paths", n, p)
469
469
470 if section in (None, 'ui'):
470 if section in (None, 'ui'):
471 # update ui options
471 # update ui options
472 self.debugflag = self.configbool('ui', 'debug')
472 self.debugflag = self.configbool('ui', 'debug')
473 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
473 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
474 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
474 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
475 if self.verbose and self.quiet:
475 if self.verbose and self.quiet:
476 self.quiet = self.verbose = False
476 self.quiet = self.verbose = False
477 self._reportuntrusted = self.debugflag or self.configbool("ui",
477 self._reportuntrusted = self.debugflag or self.configbool("ui",
478 "report_untrusted")
478 "report_untrusted")
479 self.tracebackflag = self.configbool('ui', 'traceback')
479 self.tracebackflag = self.configbool('ui', 'traceback')
480 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
480 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
481
481
482 if section in (None, 'trusted'):
482 if section in (None, 'trusted'):
483 # update trust information
483 # update trust information
484 self._trustusers.update(self.configlist('trusted', 'users'))
484 self._trustusers.update(self.configlist('trusted', 'users'))
485 self._trustgroups.update(self.configlist('trusted', 'groups'))
485 self._trustgroups.update(self.configlist('trusted', 'groups'))
486
486
487 def backupconfig(self, section, item):
487 def backupconfig(self, section, item):
488 return (self._ocfg.backup(section, item),
488 return (self._ocfg.backup(section, item),
489 self._tcfg.backup(section, item),
489 self._tcfg.backup(section, item),
490 self._ucfg.backup(section, item),)
490 self._ucfg.backup(section, item),)
491 def restoreconfig(self, data):
491 def restoreconfig(self, data):
492 self._ocfg.restore(data[0])
492 self._ocfg.restore(data[0])
493 self._tcfg.restore(data[1])
493 self._tcfg.restore(data[1])
494 self._ucfg.restore(data[2])
494 self._ucfg.restore(data[2])
495
495
496 def setconfig(self, section, name, value, source=''):
496 def setconfig(self, section, name, value, source=''):
497 for cfg in (self._ocfg, self._tcfg, self._ucfg):
497 for cfg in (self._ocfg, self._tcfg, self._ucfg):
498 cfg.set(section, name, value, source)
498 cfg.set(section, name, value, source)
499 self.fixconfig(section=section)
499 self.fixconfig(section=section)
500 self._maybetweakdefaults()
500 self._maybetweakdefaults()
501
501
502 def _data(self, untrusted):
502 def _data(self, untrusted):
503 return untrusted and self._ucfg or self._tcfg
503 return untrusted and self._ucfg or self._tcfg
504
504
505 def configsource(self, section, name, untrusted=False):
505 def configsource(self, section, name, untrusted=False):
506 return self._data(untrusted).source(section, name)
506 return self._data(untrusted).source(section, name)
507
507
508 def config(self, section, name, default=_unset, untrusted=False):
508 def config(self, section, name, default=_unset, untrusted=False):
509 """return the plain string version of a config"""
509 """return the plain string version of a config"""
510 value = self._config(section, name, default=default,
510 value = self._config(section, name, default=default,
511 untrusted=untrusted)
511 untrusted=untrusted)
512 if value is _unset:
512 if value is _unset:
513 return None
513 return None
514 return value
514 return value
515
515
516 def _config(self, section, name, default=_unset, untrusted=False):
516 def _config(self, section, name, default=_unset, untrusted=False):
517 value = itemdefault = default
517 value = itemdefault = default
518 item = self._knownconfig.get(section, {}).get(name)
518 item = self._knownconfig.get(section, {}).get(name)
519 alternates = [(section, name)]
519 alternates = [(section, name)]
520
520
521 if item is not None:
521 if item is not None:
522 alternates.extend(item.alias)
522 alternates.extend(item.alias)
523 if callable(item.default):
523 if callable(item.default):
524 itemdefault = item.default()
524 itemdefault = item.default()
525 else:
525 else:
526 itemdefault = item.default
526 itemdefault = item.default
527 else:
527 else:
528 msg = ("accessing unregistered config item: '%s.%s'")
528 msg = ("accessing unregistered config item: '%s.%s'")
529 msg %= (section, name)
529 msg %= (section, name)
530 self.develwarn(msg, 2, 'warn-config-unknown')
530 self.develwarn(msg, 2, 'warn-config-unknown')
531
531
532 if default is _unset:
532 if default is _unset:
533 if item is None:
533 if item is None:
534 value = default
534 value = default
535 elif item.default is configitems.dynamicdefault:
535 elif item.default is configitems.dynamicdefault:
536 value = None
536 value = None
537 msg = "config item requires an explicit default value: '%s.%s'"
537 msg = "config item requires an explicit default value: '%s.%s'"
538 msg %= (section, name)
538 msg %= (section, name)
539 self.develwarn(msg, 2, 'warn-config-default')
539 self.develwarn(msg, 2, 'warn-config-default')
540 else:
540 else:
541 value = itemdefault
541 value = itemdefault
542 elif (item is not None
542 elif (item is not None
543 and item.default is not configitems.dynamicdefault
543 and item.default is not configitems.dynamicdefault
544 and default != itemdefault):
544 and default != itemdefault):
545 msg = ("specifying a mismatched default value for a registered "
545 msg = ("specifying a mismatched default value for a registered "
546 "config item: '%s.%s' '%s'")
546 "config item: '%s.%s' '%s'")
547 msg %= (section, name, pycompat.bytestr(default))
547 msg %= (section, name, pycompat.bytestr(default))
548 self.develwarn(msg, 2, 'warn-config-default')
548 self.develwarn(msg, 2, 'warn-config-default')
549
549
550 for s, n in alternates:
550 for s, n in alternates:
551 candidate = self._data(untrusted).get(s, n, None)
551 candidate = self._data(untrusted).get(s, n, None)
552 if candidate is not None:
552 if candidate is not None:
553 value = candidate
553 value = candidate
554 section = s
554 section = s
555 name = n
555 name = n
556 break
556 break
557
557
558 if self.debugflag and not untrusted and self._reportuntrusted:
558 if self.debugflag and not untrusted and self._reportuntrusted:
559 for s, n in alternates:
559 for s, n in alternates:
560 uvalue = self._ucfg.get(s, n)
560 uvalue = self._ucfg.get(s, n)
561 if uvalue is not None and uvalue != value:
561 if uvalue is not None and uvalue != value:
562 self.debug("ignoring untrusted configuration option "
562 self.debug("ignoring untrusted configuration option "
563 "%s.%s = %s\n" % (s, n, uvalue))
563 "%s.%s = %s\n" % (s, n, uvalue))
564 return value
564 return value
565
565
566 def configsuboptions(self, section, name, default=_unset, untrusted=False):
566 def configsuboptions(self, section, name, default=_unset, untrusted=False):
567 """Get a config option and all sub-options.
567 """Get a config option and all sub-options.
568
568
569 Some config options have sub-options that are declared with the
569 Some config options have sub-options that are declared with the
570 format "key:opt = value". This method is used to return the main
570 format "key:opt = value". This method is used to return the main
571 option and all its declared sub-options.
571 option and all its declared sub-options.
572
572
573 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
573 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
574 is a dict of defined sub-options where keys and values are strings.
574 is a dict of defined sub-options where keys and values are strings.
575 """
575 """
576 main = self.config(section, name, default, untrusted=untrusted)
576 main = self.config(section, name, default, untrusted=untrusted)
577 data = self._data(untrusted)
577 data = self._data(untrusted)
578 sub = {}
578 sub = {}
579 prefix = '%s:' % name
579 prefix = '%s:' % name
580 for k, v in data.items(section):
580 for k, v in data.items(section):
581 if k.startswith(prefix):
581 if k.startswith(prefix):
582 sub[k[len(prefix):]] = v
582 sub[k[len(prefix):]] = v
583
583
584 if self.debugflag and not untrusted and self._reportuntrusted:
584 if self.debugflag and not untrusted and self._reportuntrusted:
585 for k, v in sub.items():
585 for k, v in sub.items():
586 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
586 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
587 if uvalue is not None and uvalue != v:
587 if uvalue is not None and uvalue != v:
588 self.debug('ignoring untrusted configuration option '
588 self.debug('ignoring untrusted configuration option '
589 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
589 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
590
590
591 return main, sub
591 return main, sub
592
592
593 def configpath(self, section, name, default=_unset, untrusted=False):
593 def configpath(self, section, name, default=_unset, untrusted=False):
594 'get a path config item, expanded relative to repo root or config file'
594 'get a path config item, expanded relative to repo root or config file'
595 v = self.config(section, name, default, untrusted)
595 v = self.config(section, name, default, untrusted)
596 if v is None:
596 if v is None:
597 return None
597 return None
598 if not os.path.isabs(v) or "://" not in v:
598 if not os.path.isabs(v) or "://" not in v:
599 src = self.configsource(section, name, untrusted)
599 src = self.configsource(section, name, untrusted)
600 if ':' in src:
600 if ':' in src:
601 base = os.path.dirname(src.rsplit(':')[0])
601 base = os.path.dirname(src.rsplit(':')[0])
602 v = os.path.join(base, os.path.expanduser(v))
602 v = os.path.join(base, os.path.expanduser(v))
603 return v
603 return v
604
604
605 def configbool(self, section, name, default=_unset, untrusted=False):
605 def configbool(self, section, name, default=_unset, untrusted=False):
606 """parse a configuration element as a boolean
606 """parse a configuration element as a boolean
607
607
608 >>> u = ui(); s = b'foo'
608 >>> u = ui(); s = b'foo'
609 >>> u.setconfig(s, b'true', b'yes')
609 >>> u.setconfig(s, b'true', b'yes')
610 >>> u.configbool(s, b'true')
610 >>> u.configbool(s, b'true')
611 True
611 True
612 >>> u.setconfig(s, b'false', b'no')
612 >>> u.setconfig(s, b'false', b'no')
613 >>> u.configbool(s, b'false')
613 >>> u.configbool(s, b'false')
614 False
614 False
615 >>> u.configbool(s, b'unknown')
615 >>> u.configbool(s, b'unknown')
616 False
616 False
617 >>> u.configbool(s, b'unknown', True)
617 >>> u.configbool(s, b'unknown', True)
618 True
618 True
619 >>> u.setconfig(s, b'invalid', b'somevalue')
619 >>> u.setconfig(s, b'invalid', b'somevalue')
620 >>> u.configbool(s, b'invalid')
620 >>> u.configbool(s, b'invalid')
621 Traceback (most recent call last):
621 Traceback (most recent call last):
622 ...
622 ...
623 ConfigError: foo.invalid is not a boolean ('somevalue')
623 ConfigError: foo.invalid is not a boolean ('somevalue')
624 """
624 """
625
625
626 v = self._config(section, name, default, untrusted=untrusted)
626 v = self._config(section, name, default, untrusted=untrusted)
627 if v is None:
627 if v is None:
628 return v
628 return v
629 if v is _unset:
629 if v is _unset:
630 if default is _unset:
630 if default is _unset:
631 return False
631 return False
632 return default
632 return default
633 if isinstance(v, bool):
633 if isinstance(v, bool):
634 return v
634 return v
635 b = stringutil.parsebool(v)
635 b = stringutil.parsebool(v)
636 if b is None:
636 if b is None:
637 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
637 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
638 % (section, name, v))
638 % (section, name, v))
639 return b
639 return b
640
640
641 def configwith(self, convert, section, name, default=_unset,
641 def configwith(self, convert, section, name, default=_unset,
642 desc=None, untrusted=False):
642 desc=None, untrusted=False):
643 """parse a configuration element with a conversion function
643 """parse a configuration element with a conversion function
644
644
645 >>> u = ui(); s = b'foo'
645 >>> u = ui(); s = b'foo'
646 >>> u.setconfig(s, b'float1', b'42')
646 >>> u.setconfig(s, b'float1', b'42')
647 >>> u.configwith(float, s, b'float1')
647 >>> u.configwith(float, s, b'float1')
648 42.0
648 42.0
649 >>> u.setconfig(s, b'float2', b'-4.25')
649 >>> u.setconfig(s, b'float2', b'-4.25')
650 >>> u.configwith(float, s, b'float2')
650 >>> u.configwith(float, s, b'float2')
651 -4.25
651 -4.25
652 >>> u.configwith(float, s, b'unknown', 7)
652 >>> u.configwith(float, s, b'unknown', 7)
653 7.0
653 7.0
654 >>> u.setconfig(s, b'invalid', b'somevalue')
654 >>> u.setconfig(s, b'invalid', b'somevalue')
655 >>> u.configwith(float, s, b'invalid')
655 >>> u.configwith(float, s, b'invalid')
656 Traceback (most recent call last):
656 Traceback (most recent call last):
657 ...
657 ...
658 ConfigError: foo.invalid is not a valid float ('somevalue')
658 ConfigError: foo.invalid is not a valid float ('somevalue')
659 >>> u.configwith(float, s, b'invalid', desc=b'womble')
659 >>> u.configwith(float, s, b'invalid', desc=b'womble')
660 Traceback (most recent call last):
660 Traceback (most recent call last):
661 ...
661 ...
662 ConfigError: foo.invalid is not a valid womble ('somevalue')
662 ConfigError: foo.invalid is not a valid womble ('somevalue')
663 """
663 """
664
664
665 v = self.config(section, name, default, untrusted)
665 v = self.config(section, name, default, untrusted)
666 if v is None:
666 if v is None:
667 return v # do not attempt to convert None
667 return v # do not attempt to convert None
668 try:
668 try:
669 return convert(v)
669 return convert(v)
670 except (ValueError, error.ParseError):
670 except (ValueError, error.ParseError):
671 if desc is None:
671 if desc is None:
672 desc = pycompat.sysbytes(convert.__name__)
672 desc = pycompat.sysbytes(convert.__name__)
673 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
673 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
674 % (section, name, desc, v))
674 % (section, name, desc, v))
675
675
676 def configint(self, section, name, default=_unset, untrusted=False):
676 def configint(self, section, name, default=_unset, untrusted=False):
677 """parse a configuration element as an integer
677 """parse a configuration element as an integer
678
678
679 >>> u = ui(); s = b'foo'
679 >>> u = ui(); s = b'foo'
680 >>> u.setconfig(s, b'int1', b'42')
680 >>> u.setconfig(s, b'int1', b'42')
681 >>> u.configint(s, b'int1')
681 >>> u.configint(s, b'int1')
682 42
682 42
683 >>> u.setconfig(s, b'int2', b'-42')
683 >>> u.setconfig(s, b'int2', b'-42')
684 >>> u.configint(s, b'int2')
684 >>> u.configint(s, b'int2')
685 -42
685 -42
686 >>> u.configint(s, b'unknown', 7)
686 >>> u.configint(s, b'unknown', 7)
687 7
687 7
688 >>> u.setconfig(s, b'invalid', b'somevalue')
688 >>> u.setconfig(s, b'invalid', b'somevalue')
689 >>> u.configint(s, b'invalid')
689 >>> u.configint(s, b'invalid')
690 Traceback (most recent call last):
690 Traceback (most recent call last):
691 ...
691 ...
692 ConfigError: foo.invalid is not a valid integer ('somevalue')
692 ConfigError: foo.invalid is not a valid integer ('somevalue')
693 """
693 """
694
694
695 return self.configwith(int, section, name, default, 'integer',
695 return self.configwith(int, section, name, default, 'integer',
696 untrusted)
696 untrusted)
697
697
698 def configbytes(self, section, name, default=_unset, untrusted=False):
698 def configbytes(self, section, name, default=_unset, untrusted=False):
699 """parse a configuration element as a quantity in bytes
699 """parse a configuration element as a quantity in bytes
700
700
701 Units can be specified as b (bytes), k or kb (kilobytes), m or
701 Units can be specified as b (bytes), k or kb (kilobytes), m or
702 mb (megabytes), g or gb (gigabytes).
702 mb (megabytes), g or gb (gigabytes).
703
703
704 >>> u = ui(); s = b'foo'
704 >>> u = ui(); s = b'foo'
705 >>> u.setconfig(s, b'val1', b'42')
705 >>> u.setconfig(s, b'val1', b'42')
706 >>> u.configbytes(s, b'val1')
706 >>> u.configbytes(s, b'val1')
707 42
707 42
708 >>> u.setconfig(s, b'val2', b'42.5 kb')
708 >>> u.setconfig(s, b'val2', b'42.5 kb')
709 >>> u.configbytes(s, b'val2')
709 >>> u.configbytes(s, b'val2')
710 43520
710 43520
711 >>> u.configbytes(s, b'unknown', b'7 MB')
711 >>> u.configbytes(s, b'unknown', b'7 MB')
712 7340032
712 7340032
713 >>> u.setconfig(s, b'invalid', b'somevalue')
713 >>> u.setconfig(s, b'invalid', b'somevalue')
714 >>> u.configbytes(s, b'invalid')
714 >>> u.configbytes(s, b'invalid')
715 Traceback (most recent call last):
715 Traceback (most recent call last):
716 ...
716 ...
717 ConfigError: foo.invalid is not a byte quantity ('somevalue')
717 ConfigError: foo.invalid is not a byte quantity ('somevalue')
718 """
718 """
719
719
720 value = self._config(section, name, default, untrusted)
720 value = self._config(section, name, default, untrusted)
721 if value is _unset:
721 if value is _unset:
722 if default is _unset:
722 if default is _unset:
723 default = 0
723 default = 0
724 value = default
724 value = default
725 if not isinstance(value, bytes):
725 if not isinstance(value, bytes):
726 return value
726 return value
727 try:
727 try:
728 return util.sizetoint(value)
728 return util.sizetoint(value)
729 except error.ParseError:
729 except error.ParseError:
730 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
730 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
731 % (section, name, value))
731 % (section, name, value))
732
732
733 def configlist(self, section, name, default=_unset, untrusted=False):
733 def configlist(self, section, name, default=_unset, untrusted=False):
734 """parse a configuration element as a list of comma/space separated
734 """parse a configuration element as a list of comma/space separated
735 strings
735 strings
736
736
737 >>> u = ui(); s = b'foo'
737 >>> u = ui(); s = b'foo'
738 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
738 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
739 >>> u.configlist(s, b'list1')
739 >>> u.configlist(s, b'list1')
740 ['this', 'is', 'a small', 'test']
740 ['this', 'is', 'a small', 'test']
741 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
741 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
742 >>> u.configlist(s, b'list2')
742 >>> u.configlist(s, b'list2')
743 ['this', 'is', 'a small', 'test']
743 ['this', 'is', 'a small', 'test']
744 """
744 """
745 # default is not always a list
745 # default is not always a list
746 v = self.configwith(config.parselist, section, name, default,
746 v = self.configwith(config.parselist, section, name, default,
747 'list', untrusted)
747 'list', untrusted)
748 if isinstance(v, bytes):
748 if isinstance(v, bytes):
749 return config.parselist(v)
749 return config.parselist(v)
750 elif v is None:
750 elif v is None:
751 return []
751 return []
752 return v
752 return v
753
753
754 def configdate(self, section, name, default=_unset, untrusted=False):
754 def configdate(self, section, name, default=_unset, untrusted=False):
755 """parse a configuration element as a tuple of ints
755 """parse a configuration element as a tuple of ints
756
756
757 >>> u = ui(); s = b'foo'
757 >>> u = ui(); s = b'foo'
758 >>> u.setconfig(s, b'date', b'0 0')
758 >>> u.setconfig(s, b'date', b'0 0')
759 >>> u.configdate(s, b'date')
759 >>> u.configdate(s, b'date')
760 (0, 0)
760 (0, 0)
761 """
761 """
762 if self.config(section, name, default, untrusted):
762 if self.config(section, name, default, untrusted):
763 return self.configwith(dateutil.parsedate, section, name, default,
763 return self.configwith(dateutil.parsedate, section, name, default,
764 'date', untrusted)
764 'date', untrusted)
765 if default is _unset:
765 if default is _unset:
766 return None
766 return None
767 return default
767 return default
768
768
769 def hasconfig(self, section, name, untrusted=False):
769 def hasconfig(self, section, name, untrusted=False):
770 return self._data(untrusted).hasitem(section, name)
770 return self._data(untrusted).hasitem(section, name)
771
771
772 def has_section(self, section, untrusted=False):
772 def has_section(self, section, untrusted=False):
773 '''tell whether section exists in config.'''
773 '''tell whether section exists in config.'''
774 return section in self._data(untrusted)
774 return section in self._data(untrusted)
775
775
776 def configitems(self, section, untrusted=False, ignoresub=False):
776 def configitems(self, section, untrusted=False, ignoresub=False):
777 items = self._data(untrusted).items(section)
777 items = self._data(untrusted).items(section)
778 if ignoresub:
778 if ignoresub:
779 items = [i for i in items if ':' not in i[0]]
779 items = [i for i in items if ':' not in i[0]]
780 if self.debugflag and not untrusted and self._reportuntrusted:
780 if self.debugflag and not untrusted and self._reportuntrusted:
781 for k, v in self._ucfg.items(section):
781 for k, v in self._ucfg.items(section):
782 if self._tcfg.get(section, k) != v:
782 if self._tcfg.get(section, k) != v:
783 self.debug("ignoring untrusted configuration option "
783 self.debug("ignoring untrusted configuration option "
784 "%s.%s = %s\n" % (section, k, v))
784 "%s.%s = %s\n" % (section, k, v))
785 return items
785 return items
786
786
787 def walkconfig(self, untrusted=False):
787 def walkconfig(self, untrusted=False):
788 cfg = self._data(untrusted)
788 cfg = self._data(untrusted)
789 for section in cfg.sections():
789 for section in cfg.sections():
790 for name, value in self.configitems(section, untrusted):
790 for name, value in self.configitems(section, untrusted):
791 yield section, name, value
791 yield section, name, value
792
792
793 def plain(self, feature=None):
793 def plain(self, feature=None):
794 '''is plain mode active?
794 '''is plain mode active?
795
795
796 Plain mode means that all configuration variables which affect
796 Plain mode means that all configuration variables which affect
797 the behavior and output of Mercurial should be
797 the behavior and output of Mercurial should be
798 ignored. Additionally, the output should be stable,
798 ignored. Additionally, the output should be stable,
799 reproducible and suitable for use in scripts or applications.
799 reproducible and suitable for use in scripts or applications.
800
800
801 The only way to trigger plain mode is by setting either the
801 The only way to trigger plain mode is by setting either the
802 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
802 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
803
803
804 The return value can either be
804 The return value can either be
805 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
805 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
806 - False if feature is disabled by default and not included in HGPLAIN
806 - False if feature is disabled by default and not included in HGPLAIN
807 - True otherwise
807 - True otherwise
808 '''
808 '''
809 if ('HGPLAIN' not in encoding.environ and
809 if ('HGPLAIN' not in encoding.environ and
810 'HGPLAINEXCEPT' not in encoding.environ):
810 'HGPLAINEXCEPT' not in encoding.environ):
811 return False
811 return False
812 exceptions = encoding.environ.get('HGPLAINEXCEPT',
812 exceptions = encoding.environ.get('HGPLAINEXCEPT',
813 '').strip().split(',')
813 '').strip().split(',')
814 # TODO: add support for HGPLAIN=+feature,-feature syntax
814 # TODO: add support for HGPLAIN=+feature,-feature syntax
815 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
815 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
816 exceptions.append('strictflags')
816 exceptions.append('strictflags')
817 if feature and exceptions:
817 if feature and exceptions:
818 return feature not in exceptions
818 return feature not in exceptions
819 return True
819 return True
820
820
821 def username(self, acceptempty=False):
821 def username(self, acceptempty=False):
822 """Return default username to be used in commits.
822 """Return default username to be used in commits.
823
823
824 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
824 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
825 and stop searching if one of these is set.
825 and stop searching if one of these is set.
826 If not found and acceptempty is True, returns None.
826 If not found and acceptempty is True, returns None.
827 If not found and ui.askusername is True, ask the user, else use
827 If not found and ui.askusername is True, ask the user, else use
828 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
828 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
829 If no username could be found, raise an Abort error.
829 If no username could be found, raise an Abort error.
830 """
830 """
831 user = encoding.environ.get("HGUSER")
831 user = encoding.environ.get("HGUSER")
832 if user is None:
832 if user is None:
833 user = self.config("ui", "username")
833 user = self.config("ui", "username")
834 if user is not None:
834 if user is not None:
835 user = os.path.expandvars(user)
835 user = os.path.expandvars(user)
836 if user is None:
836 if user is None:
837 user = encoding.environ.get("EMAIL")
837 user = encoding.environ.get("EMAIL")
838 if user is None and acceptempty:
838 if user is None and acceptempty:
839 return user
839 return user
840 if user is None and self.configbool("ui", "askusername"):
840 if user is None and self.configbool("ui", "askusername"):
841 user = self.prompt(_("enter a commit username:"), default=None)
841 user = self.prompt(_("enter a commit username:"), default=None)
842 if user is None and not self.interactive():
842 if user is None and not self.interactive():
843 try:
843 try:
844 user = '%s@%s' % (procutil.getuser(),
844 user = '%s@%s' % (procutil.getuser(),
845 encoding.strtolocal(socket.getfqdn()))
845 encoding.strtolocal(socket.getfqdn()))
846 self.warn(_("no username found, using '%s' instead\n") % user)
846 self.warn(_("no username found, using '%s' instead\n") % user)
847 except KeyError:
847 except KeyError:
848 pass
848 pass
849 if not user:
849 if not user:
850 raise error.Abort(_('no username supplied'),
850 raise error.Abort(_('no username supplied'),
851 hint=_("use 'hg config --edit' "
851 hint=_("use 'hg config --edit' "
852 'to set your username'))
852 'to set your username'))
853 if "\n" in user:
853 if "\n" in user:
854 raise error.Abort(_("username %r contains a newline\n")
854 raise error.Abort(_("username %r contains a newline\n")
855 % pycompat.bytestr(user))
855 % pycompat.bytestr(user))
856 return user
856 return user
857
857
858 def shortuser(self, user):
858 def shortuser(self, user):
859 """Return a short representation of a user name or email address."""
859 """Return a short representation of a user name or email address."""
860 if not self.verbose:
860 if not self.verbose:
861 user = stringutil.shortuser(user)
861 user = stringutil.shortuser(user)
862 return user
862 return user
863
863
864 def expandpath(self, loc, default=None):
864 def expandpath(self, loc, default=None):
865 """Return repository location relative to cwd or from [paths]"""
865 """Return repository location relative to cwd or from [paths]"""
866 try:
866 try:
867 p = self.paths.getpath(loc)
867 p = self.paths.getpath(loc)
868 if p:
868 if p:
869 return p.rawloc
869 return p.rawloc
870 except error.RepoError:
870 except error.RepoError:
871 pass
871 pass
872
872
873 if default:
873 if default:
874 try:
874 try:
875 p = self.paths.getpath(default)
875 p = self.paths.getpath(default)
876 if p:
876 if p:
877 return p.rawloc
877 return p.rawloc
878 except error.RepoError:
878 except error.RepoError:
879 pass
879 pass
880
880
881 return loc
881 return loc
882
882
883 @util.propertycache
883 @util.propertycache
884 def paths(self):
884 def paths(self):
885 return paths(self)
885 return paths(self)
886
886
887 def pushbuffer(self, error=False, subproc=False, labeled=False):
887 def pushbuffer(self, error=False, subproc=False, labeled=False):
888 """install a buffer to capture standard output of the ui object
888 """install a buffer to capture standard output of the ui object
889
889
890 If error is True, the error output will be captured too.
890 If error is True, the error output will be captured too.
891
891
892 If subproc is True, output from subprocesses (typically hooks) will be
892 If subproc is True, output from subprocesses (typically hooks) will be
893 captured too.
893 captured too.
894
894
895 If labeled is True, any labels associated with buffered
895 If labeled is True, any labels associated with buffered
896 output will be handled. By default, this has no effect
896 output will be handled. By default, this has no effect
897 on the output returned, but extensions and GUI tools may
897 on the output returned, but extensions and GUI tools may
898 handle this argument and returned styled output. If output
898 handle this argument and returned styled output. If output
899 is being buffered so it can be captured and parsed or
899 is being buffered so it can be captured and parsed or
900 processed, labeled should not be set to True.
900 processed, labeled should not be set to True.
901 """
901 """
902 self._buffers.append([])
902 self._buffers.append([])
903 self._bufferstates.append((error, subproc, labeled))
903 self._bufferstates.append((error, subproc, labeled))
904 self._bufferapplylabels = labeled
904 self._bufferapplylabels = labeled
905
905
906 def popbuffer(self):
906 def popbuffer(self):
907 '''pop the last buffer and return the buffered output'''
907 '''pop the last buffer and return the buffered output'''
908 self._bufferstates.pop()
908 self._bufferstates.pop()
909 if self._bufferstates:
909 if self._bufferstates:
910 self._bufferapplylabels = self._bufferstates[-1][2]
910 self._bufferapplylabels = self._bufferstates[-1][2]
911 else:
911 else:
912 self._bufferapplylabels = None
912 self._bufferapplylabels = None
913
913
914 return "".join(self._buffers.pop())
914 return "".join(self._buffers.pop())
915
915
916 def canwritewithoutlabels(self):
916 def canwritewithoutlabels(self):
917 '''check if write skips the label'''
917 '''check if write skips the label'''
918 if self._buffers and not self._bufferapplylabels:
918 if self._buffers and not self._bufferapplylabels:
919 return True
919 return True
920 return self._colormode is None
920 return self._colormode is None
921
921
922 def canbatchlabeledwrites(self):
922 def canbatchlabeledwrites(self):
923 '''check if write calls with labels are batchable'''
923 '''check if write calls with labels are batchable'''
924 # Windows color printing is special, see ``write``.
924 # Windows color printing is special, see ``write``.
925 return self._colormode != 'win32'
925 return self._colormode != 'win32'
926
926
927 def write(self, *args, **opts):
927 def write(self, *args, **opts):
928 '''write args to output
928 '''write args to output
929
929
930 By default, this method simply writes to the buffer or stdout.
930 By default, this method simply writes to the buffer or stdout.
931 Color mode can be set on the UI class to have the output decorated
931 Color mode can be set on the UI class to have the output decorated
932 with color modifier before being written to stdout.
932 with color modifier before being written to stdout.
933
933
934 The color used is controlled by an optional keyword argument, "label".
934 The color used is controlled by an optional keyword argument, "label".
935 This should be a string containing label names separated by space.
935 This should be a string containing label names separated by space.
936 Label names take the form of "topic.type". For example, ui.debug()
936 Label names take the form of "topic.type". For example, ui.debug()
937 issues a label of "ui.debug".
937 issues a label of "ui.debug".
938
938
939 When labeling output for a specific command, a label of
939 When labeling output for a specific command, a label of
940 "cmdname.type" is recommended. For example, status issues
940 "cmdname.type" is recommended. For example, status issues
941 a label of "status.modified" for modified files.
941 a label of "status.modified" for modified files.
942 '''
942 '''
943 if self._buffers:
943 if self._buffers:
944 if self._bufferapplylabels:
944 if self._bufferapplylabels:
945 label = opts.get(r'label', '')
945 label = opts.get(r'label', '')
946 self._buffers[-1].extend(self.label(a, label) for a in args)
946 self._buffers[-1].extend(self.label(a, label) for a in args)
947 else:
947 else:
948 self._buffers[-1].extend(args)
948 self._buffers[-1].extend(args)
949 else:
949 else:
950 self._writenobuf(*args, **opts)
950 self._writenobuf(*args, **opts)
951
951
952 def _writenobuf(self, *args, **opts):
952 def _writenobuf(self, *args, **opts):
953 if self._colormode == 'win32':
953 if self._colormode == 'win32':
954 # windows color printing is its own can of crab, defer to
954 # windows color printing is its own can of crab, defer to
955 # the color module and that is it.
955 # the color module and that is it.
956 color.win32print(self, self._write, *args, **opts)
956 color.win32print(self, self._write, *args, **opts)
957 else:
957 else:
958 msgs = args
958 msgs = args
959 if self._colormode is not None:
959 if self._colormode is not None:
960 label = opts.get(r'label', '')
960 label = opts.get(r'label', '')
961 msgs = [self.label(a, label) for a in args]
961 msgs = [self.label(a, label) for a in args]
962 self._write(*msgs, **opts)
962 self._write(*msgs, **opts)
963
963
964 def _write(self, *msgs, **opts):
964 def _write(self, *msgs, **opts):
965 self._progclear()
965 self._progclear()
966 # opencode timeblockedsection because this is a critical path
966 # opencode timeblockedsection because this is a critical path
967 starttime = util.timer()
967 starttime = util.timer()
968 try:
968 try:
969 self.fout.write(''.join(msgs))
969 self.fout.write(''.join(msgs))
970 except IOError as err:
970 except IOError as err:
971 raise error.StdioError(err)
971 raise error.StdioError(err)
972 finally:
972 finally:
973 self._blockedtimes['stdio_blocked'] += \
973 self._blockedtimes['stdio_blocked'] += \
974 (util.timer() - starttime) * 1000
974 (util.timer() - starttime) * 1000
975
975
976 def write_err(self, *args, **opts):
976 def write_err(self, *args, **opts):
977 self._progclear()
977 self._progclear()
978 if self._bufferstates and self._bufferstates[-1][0]:
978 if self._bufferstates and self._bufferstates[-1][0]:
979 self.write(*args, **opts)
979 self.write(*args, **opts)
980 elif self._colormode == 'win32':
980 elif self._colormode == 'win32':
981 # windows color printing is its own can of crab, defer to
981 # windows color printing is its own can of crab, defer to
982 # the color module and that is it.
982 # the color module and that is it.
983 color.win32print(self, self._write_err, *args, **opts)
983 color.win32print(self, self._write_err, *args, **opts)
984 else:
984 else:
985 msgs = args
985 msgs = args
986 if self._colormode is not None:
986 if self._colormode is not None:
987 label = opts.get(r'label', '')
987 label = opts.get(r'label', '')
988 msgs = [self.label(a, label) for a in args]
988 msgs = [self.label(a, label) for a in args]
989 self._write_err(*msgs, **opts)
989 self._write_err(*msgs, **opts)
990
990
991 def _write_err(self, *msgs, **opts):
991 def _write_err(self, *msgs, **opts):
992 try:
992 try:
993 with self.timeblockedsection('stdio'):
993 with self.timeblockedsection('stdio'):
994 if not getattr(self.fout, 'closed', False):
994 if not getattr(self.fout, 'closed', False):
995 self.fout.flush()
995 self.fout.flush()
996 for a in msgs:
996 for a in msgs:
997 self.ferr.write(a)
997 self.ferr.write(a)
998 # stderr may be buffered under win32 when redirected to files,
998 # stderr may be buffered under win32 when redirected to files,
999 # including stdout.
999 # including stdout.
1000 if not getattr(self.ferr, 'closed', False):
1000 if not getattr(self.ferr, 'closed', False):
1001 self.ferr.flush()
1001 self.ferr.flush()
1002 except IOError as inst:
1002 except IOError as inst:
1003 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1003 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1004 raise error.StdioError(inst)
1004 raise error.StdioError(inst)
1005
1005
1006 def flush(self):
1006 def flush(self):
1007 # opencode timeblockedsection because this is a critical path
1007 # opencode timeblockedsection because this is a critical path
1008 starttime = util.timer()
1008 starttime = util.timer()
1009 try:
1009 try:
1010 try:
1010 try:
1011 self.fout.flush()
1011 self.fout.flush()
1012 except IOError as err:
1012 except IOError as err:
1013 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1013 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1014 raise error.StdioError(err)
1014 raise error.StdioError(err)
1015 finally:
1015 finally:
1016 try:
1016 try:
1017 self.ferr.flush()
1017 self.ferr.flush()
1018 except IOError as err:
1018 except IOError as err:
1019 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1019 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1020 raise error.StdioError(err)
1020 raise error.StdioError(err)
1021 finally:
1021 finally:
1022 self._blockedtimes['stdio_blocked'] += \
1022 self._blockedtimes['stdio_blocked'] += \
1023 (util.timer() - starttime) * 1000
1023 (util.timer() - starttime) * 1000
1024
1024
1025 def _isatty(self, fh):
1025 def _isatty(self, fh):
1026 if self.configbool('ui', 'nontty'):
1026 if self.configbool('ui', 'nontty'):
1027 return False
1027 return False
1028 return procutil.isatty(fh)
1028 return procutil.isatty(fh)
1029
1029
1030 def disablepager(self):
1030 def disablepager(self):
1031 self._disablepager = True
1031 self._disablepager = True
1032
1032
1033 def pager(self, command):
1033 def pager(self, command):
1034 """Start a pager for subsequent command output.
1034 """Start a pager for subsequent command output.
1035
1035
1036 Commands which produce a long stream of output should call
1036 Commands which produce a long stream of output should call
1037 this function to activate the user's preferred pagination
1037 this function to activate the user's preferred pagination
1038 mechanism (which may be no pager). Calling this function
1038 mechanism (which may be no pager). Calling this function
1039 precludes any future use of interactive functionality, such as
1039 precludes any future use of interactive functionality, such as
1040 prompting the user or activating curses.
1040 prompting the user or activating curses.
1041
1041
1042 Args:
1042 Args:
1043 command: The full, non-aliased name of the command. That is, "log"
1043 command: The full, non-aliased name of the command. That is, "log"
1044 not "history, "summary" not "summ", etc.
1044 not "history, "summary" not "summ", etc.
1045 """
1045 """
1046 if (self._disablepager
1046 if (self._disablepager
1047 or self.pageractive):
1047 or self.pageractive):
1048 # how pager should do is already determined
1048 # how pager should do is already determined
1049 return
1049 return
1050
1050
1051 if not command.startswith('internal-always-') and (
1051 if not command.startswith('internal-always-') and (
1052 # explicit --pager=on (= 'internal-always-' prefix) should
1052 # explicit --pager=on (= 'internal-always-' prefix) should
1053 # take precedence over disabling factors below
1053 # take precedence over disabling factors below
1054 command in self.configlist('pager', 'ignore')
1054 command in self.configlist('pager', 'ignore')
1055 or not self.configbool('ui', 'paginate')
1055 or not self.configbool('ui', 'paginate')
1056 or not self.configbool('pager', 'attend-' + command, True)
1056 or not self.configbool('pager', 'attend-' + command, True)
1057 or encoding.environ.get('TERM') == 'dumb'
1057 or encoding.environ.get('TERM') == 'dumb'
1058 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1058 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1059 # formatted() will need some adjustment.
1059 # formatted() will need some adjustment.
1060 or not self.formatted()
1060 or not self.formatted()
1061 or self.plain()
1061 or self.plain()
1062 or self._buffers
1062 or self._buffers
1063 # TODO: expose debugger-enabled on the UI object
1063 # TODO: expose debugger-enabled on the UI object
1064 or '--debugger' in pycompat.sysargv):
1064 or '--debugger' in pycompat.sysargv):
1065 # We only want to paginate if the ui appears to be
1065 # We only want to paginate if the ui appears to be
1066 # interactive, the user didn't say HGPLAIN or
1066 # interactive, the user didn't say HGPLAIN or
1067 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1067 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1068 return
1068 return
1069
1069
1070 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1070 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1071 if not pagercmd:
1071 if not pagercmd:
1072 return
1072 return
1073
1073
1074 pagerenv = {}
1074 pagerenv = {}
1075 for name, value in rcutil.defaultpagerenv().items():
1075 for name, value in rcutil.defaultpagerenv().items():
1076 if name not in encoding.environ:
1076 if name not in encoding.environ:
1077 pagerenv[name] = value
1077 pagerenv[name] = value
1078
1078
1079 self.debug('starting pager for command %r\n' % command)
1079 self.debug('starting pager for command %s\n' %
1080 stringutil.pprint(command))
1080 self.flush()
1081 self.flush()
1081
1082
1082 wasformatted = self.formatted()
1083 wasformatted = self.formatted()
1083 if util.safehasattr(signal, "SIGPIPE"):
1084 if util.safehasattr(signal, "SIGPIPE"):
1084 signal.signal(signal.SIGPIPE, _catchterm)
1085 signal.signal(signal.SIGPIPE, _catchterm)
1085 if self._runpager(pagercmd, pagerenv):
1086 if self._runpager(pagercmd, pagerenv):
1086 self.pageractive = True
1087 self.pageractive = True
1087 # Preserve the formatted-ness of the UI. This is important
1088 # Preserve the formatted-ness of the UI. This is important
1088 # because we mess with stdout, which might confuse
1089 # because we mess with stdout, which might confuse
1089 # auto-detection of things being formatted.
1090 # auto-detection of things being formatted.
1090 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1091 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1091 self.setconfig('ui', 'interactive', False, 'pager')
1092 self.setconfig('ui', 'interactive', False, 'pager')
1092
1093
1093 # If pagermode differs from color.mode, reconfigure color now that
1094 # If pagermode differs from color.mode, reconfigure color now that
1094 # pageractive is set.
1095 # pageractive is set.
1095 cm = self._colormode
1096 cm = self._colormode
1096 if cm != self.config('color', 'pagermode', cm):
1097 if cm != self.config('color', 'pagermode', cm):
1097 color.setup(self)
1098 color.setup(self)
1098 else:
1099 else:
1099 # If the pager can't be spawned in dispatch when --pager=on is
1100 # If the pager can't be spawned in dispatch when --pager=on is
1100 # given, don't try again when the command runs, to avoid a duplicate
1101 # given, don't try again when the command runs, to avoid a duplicate
1101 # warning about a missing pager command.
1102 # warning about a missing pager command.
1102 self.disablepager()
1103 self.disablepager()
1103
1104
1104 def _runpager(self, command, env=None):
1105 def _runpager(self, command, env=None):
1105 """Actually start the pager and set up file descriptors.
1106 """Actually start the pager and set up file descriptors.
1106
1107
1107 This is separate in part so that extensions (like chg) can
1108 This is separate in part so that extensions (like chg) can
1108 override how a pager is invoked.
1109 override how a pager is invoked.
1109 """
1110 """
1110 if command == 'cat':
1111 if command == 'cat':
1111 # Save ourselves some work.
1112 # Save ourselves some work.
1112 return False
1113 return False
1113 # If the command doesn't contain any of these characters, we
1114 # If the command doesn't contain any of these characters, we
1114 # assume it's a binary and exec it directly. This means for
1115 # assume it's a binary and exec it directly. This means for
1115 # simple pager command configurations, we can degrade
1116 # simple pager command configurations, we can degrade
1116 # gracefully and tell the user about their broken pager.
1117 # gracefully and tell the user about their broken pager.
1117 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1118 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1118
1119
1119 if pycompat.iswindows and not shell:
1120 if pycompat.iswindows and not shell:
1120 # Window's built-in `more` cannot be invoked with shell=False, but
1121 # Window's built-in `more` cannot be invoked with shell=False, but
1121 # its `more.com` can. Hide this implementation detail from the
1122 # its `more.com` can. Hide this implementation detail from the
1122 # user so we can also get sane bad PAGER behavior. MSYS has
1123 # user so we can also get sane bad PAGER behavior. MSYS has
1123 # `more.exe`, so do a cmd.exe style resolution of the executable to
1124 # `more.exe`, so do a cmd.exe style resolution of the executable to
1124 # determine which one to use.
1125 # determine which one to use.
1125 fullcmd = procutil.findexe(command)
1126 fullcmd = procutil.findexe(command)
1126 if not fullcmd:
1127 if not fullcmd:
1127 self.warn(_("missing pager command '%s', skipping pager\n")
1128 self.warn(_("missing pager command '%s', skipping pager\n")
1128 % command)
1129 % command)
1129 return False
1130 return False
1130
1131
1131 command = fullcmd
1132 command = fullcmd
1132
1133
1133 try:
1134 try:
1134 pager = subprocess.Popen(
1135 pager = subprocess.Popen(
1135 procutil.tonativestr(command), shell=shell, bufsize=-1,
1136 procutil.tonativestr(command), shell=shell, bufsize=-1,
1136 close_fds=procutil.closefds, stdin=subprocess.PIPE,
1137 close_fds=procutil.closefds, stdin=subprocess.PIPE,
1137 stdout=procutil.stdout, stderr=procutil.stderr,
1138 stdout=procutil.stdout, stderr=procutil.stderr,
1138 env=procutil.tonativeenv(procutil.shellenviron(env)))
1139 env=procutil.tonativeenv(procutil.shellenviron(env)))
1139 except OSError as e:
1140 except OSError as e:
1140 if e.errno == errno.ENOENT and not shell:
1141 if e.errno == errno.ENOENT and not shell:
1141 self.warn(_("missing pager command '%s', skipping pager\n")
1142 self.warn(_("missing pager command '%s', skipping pager\n")
1142 % command)
1143 % command)
1143 return False
1144 return False
1144 raise
1145 raise
1145
1146
1146 # back up original file descriptors
1147 # back up original file descriptors
1147 stdoutfd = os.dup(procutil.stdout.fileno())
1148 stdoutfd = os.dup(procutil.stdout.fileno())
1148 stderrfd = os.dup(procutil.stderr.fileno())
1149 stderrfd = os.dup(procutil.stderr.fileno())
1149
1150
1150 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1151 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1151 if self._isatty(procutil.stderr):
1152 if self._isatty(procutil.stderr):
1152 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1153 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1153
1154
1154 @self.atexit
1155 @self.atexit
1155 def killpager():
1156 def killpager():
1156 if util.safehasattr(signal, "SIGINT"):
1157 if util.safehasattr(signal, "SIGINT"):
1157 signal.signal(signal.SIGINT, signal.SIG_IGN)
1158 signal.signal(signal.SIGINT, signal.SIG_IGN)
1158 # restore original fds, closing pager.stdin copies in the process
1159 # restore original fds, closing pager.stdin copies in the process
1159 os.dup2(stdoutfd, procutil.stdout.fileno())
1160 os.dup2(stdoutfd, procutil.stdout.fileno())
1160 os.dup2(stderrfd, procutil.stderr.fileno())
1161 os.dup2(stderrfd, procutil.stderr.fileno())
1161 pager.stdin.close()
1162 pager.stdin.close()
1162 pager.wait()
1163 pager.wait()
1163
1164
1164 return True
1165 return True
1165
1166
1166 @property
1167 @property
1167 def _exithandlers(self):
1168 def _exithandlers(self):
1168 return _reqexithandlers
1169 return _reqexithandlers
1169
1170
1170 def atexit(self, func, *args, **kwargs):
1171 def atexit(self, func, *args, **kwargs):
1171 '''register a function to run after dispatching a request
1172 '''register a function to run after dispatching a request
1172
1173
1173 Handlers do not stay registered across request boundaries.'''
1174 Handlers do not stay registered across request boundaries.'''
1174 self._exithandlers.append((func, args, kwargs))
1175 self._exithandlers.append((func, args, kwargs))
1175 return func
1176 return func
1176
1177
1177 def interface(self, feature):
1178 def interface(self, feature):
1178 """what interface to use for interactive console features?
1179 """what interface to use for interactive console features?
1179
1180
1180 The interface is controlled by the value of `ui.interface` but also by
1181 The interface is controlled by the value of `ui.interface` but also by
1181 the value of feature-specific configuration. For example:
1182 the value of feature-specific configuration. For example:
1182
1183
1183 ui.interface.histedit = text
1184 ui.interface.histedit = text
1184 ui.interface.chunkselector = curses
1185 ui.interface.chunkselector = curses
1185
1186
1186 Here the features are "histedit" and "chunkselector".
1187 Here the features are "histedit" and "chunkselector".
1187
1188
1188 The configuration above means that the default interfaces for commands
1189 The configuration above means that the default interfaces for commands
1189 is curses, the interface for histedit is text and the interface for
1190 is curses, the interface for histedit is text and the interface for
1190 selecting chunk is crecord (the best curses interface available).
1191 selecting chunk is crecord (the best curses interface available).
1191
1192
1192 Consider the following example:
1193 Consider the following example:
1193 ui.interface = curses
1194 ui.interface = curses
1194 ui.interface.histedit = text
1195 ui.interface.histedit = text
1195
1196
1196 Then histedit will use the text interface and chunkselector will use
1197 Then histedit will use the text interface and chunkselector will use
1197 the default curses interface (crecord at the moment).
1198 the default curses interface (crecord at the moment).
1198 """
1199 """
1199 alldefaults = frozenset(["text", "curses"])
1200 alldefaults = frozenset(["text", "curses"])
1200
1201
1201 featureinterfaces = {
1202 featureinterfaces = {
1202 "chunkselector": [
1203 "chunkselector": [
1203 "text",
1204 "text",
1204 "curses",
1205 "curses",
1205 ]
1206 ]
1206 }
1207 }
1207
1208
1208 # Feature-specific interface
1209 # Feature-specific interface
1209 if feature not in featureinterfaces.keys():
1210 if feature not in featureinterfaces.keys():
1210 # Programming error, not user error
1211 # Programming error, not user error
1211 raise ValueError("Unknown feature requested %s" % feature)
1212 raise ValueError("Unknown feature requested %s" % feature)
1212
1213
1213 availableinterfaces = frozenset(featureinterfaces[feature])
1214 availableinterfaces = frozenset(featureinterfaces[feature])
1214 if alldefaults > availableinterfaces:
1215 if alldefaults > availableinterfaces:
1215 # Programming error, not user error. We need a use case to
1216 # Programming error, not user error. We need a use case to
1216 # define the right thing to do here.
1217 # define the right thing to do here.
1217 raise ValueError(
1218 raise ValueError(
1218 "Feature %s does not handle all default interfaces" %
1219 "Feature %s does not handle all default interfaces" %
1219 feature)
1220 feature)
1220
1221
1221 if self.plain() or encoding.environ.get('TERM') == 'dumb':
1222 if self.plain() or encoding.environ.get('TERM') == 'dumb':
1222 return "text"
1223 return "text"
1223
1224
1224 # Default interface for all the features
1225 # Default interface for all the features
1225 defaultinterface = "text"
1226 defaultinterface = "text"
1226 i = self.config("ui", "interface")
1227 i = self.config("ui", "interface")
1227 if i in alldefaults:
1228 if i in alldefaults:
1228 defaultinterface = i
1229 defaultinterface = i
1229
1230
1230 choseninterface = defaultinterface
1231 choseninterface = defaultinterface
1231 f = self.config("ui", "interface.%s" % feature)
1232 f = self.config("ui", "interface.%s" % feature)
1232 if f in availableinterfaces:
1233 if f in availableinterfaces:
1233 choseninterface = f
1234 choseninterface = f
1234
1235
1235 if i is not None and defaultinterface != i:
1236 if i is not None and defaultinterface != i:
1236 if f is not None:
1237 if f is not None:
1237 self.warn(_("invalid value for ui.interface: %s\n") %
1238 self.warn(_("invalid value for ui.interface: %s\n") %
1238 (i,))
1239 (i,))
1239 else:
1240 else:
1240 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1241 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1241 (i, choseninterface))
1242 (i, choseninterface))
1242 if f is not None and choseninterface != f:
1243 if f is not None and choseninterface != f:
1243 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1244 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1244 (feature, f, choseninterface))
1245 (feature, f, choseninterface))
1245
1246
1246 return choseninterface
1247 return choseninterface
1247
1248
1248 def interactive(self):
1249 def interactive(self):
1249 '''is interactive input allowed?
1250 '''is interactive input allowed?
1250
1251
1251 An interactive session is a session where input can be reasonably read
1252 An interactive session is a session where input can be reasonably read
1252 from `sys.stdin'. If this function returns false, any attempt to read
1253 from `sys.stdin'. If this function returns false, any attempt to read
1253 from stdin should fail with an error, unless a sensible default has been
1254 from stdin should fail with an error, unless a sensible default has been
1254 specified.
1255 specified.
1255
1256
1256 Interactiveness is triggered by the value of the `ui.interactive'
1257 Interactiveness is triggered by the value of the `ui.interactive'
1257 configuration variable or - if it is unset - when `sys.stdin' points
1258 configuration variable or - if it is unset - when `sys.stdin' points
1258 to a terminal device.
1259 to a terminal device.
1259
1260
1260 This function refers to input only; for output, see `ui.formatted()'.
1261 This function refers to input only; for output, see `ui.formatted()'.
1261 '''
1262 '''
1262 i = self.configbool("ui", "interactive")
1263 i = self.configbool("ui", "interactive")
1263 if i is None:
1264 if i is None:
1264 # some environments replace stdin without implementing isatty
1265 # some environments replace stdin without implementing isatty
1265 # usually those are non-interactive
1266 # usually those are non-interactive
1266 return self._isatty(self.fin)
1267 return self._isatty(self.fin)
1267
1268
1268 return i
1269 return i
1269
1270
1270 def termwidth(self):
1271 def termwidth(self):
1271 '''how wide is the terminal in columns?
1272 '''how wide is the terminal in columns?
1272 '''
1273 '''
1273 if 'COLUMNS' in encoding.environ:
1274 if 'COLUMNS' in encoding.environ:
1274 try:
1275 try:
1275 return int(encoding.environ['COLUMNS'])
1276 return int(encoding.environ['COLUMNS'])
1276 except ValueError:
1277 except ValueError:
1277 pass
1278 pass
1278 return scmutil.termsize(self)[0]
1279 return scmutil.termsize(self)[0]
1279
1280
1280 def formatted(self):
1281 def formatted(self):
1281 '''should formatted output be used?
1282 '''should formatted output be used?
1282
1283
1283 It is often desirable to format the output to suite the output medium.
1284 It is often desirable to format the output to suite the output medium.
1284 Examples of this are truncating long lines or colorizing messages.
1285 Examples of this are truncating long lines or colorizing messages.
1285 However, this is not often not desirable when piping output into other
1286 However, this is not often not desirable when piping output into other
1286 utilities, e.g. `grep'.
1287 utilities, e.g. `grep'.
1287
1288
1288 Formatted output is triggered by the value of the `ui.formatted'
1289 Formatted output is triggered by the value of the `ui.formatted'
1289 configuration variable or - if it is unset - when `sys.stdout' points
1290 configuration variable or - if it is unset - when `sys.stdout' points
1290 to a terminal device. Please note that `ui.formatted' should be
1291 to a terminal device. Please note that `ui.formatted' should be
1291 considered an implementation detail; it is not intended for use outside
1292 considered an implementation detail; it is not intended for use outside
1292 Mercurial or its extensions.
1293 Mercurial or its extensions.
1293
1294
1294 This function refers to output only; for input, see `ui.interactive()'.
1295 This function refers to output only; for input, see `ui.interactive()'.
1295 This function always returns false when in plain mode, see `ui.plain()'.
1296 This function always returns false when in plain mode, see `ui.plain()'.
1296 '''
1297 '''
1297 if self.plain():
1298 if self.plain():
1298 return False
1299 return False
1299
1300
1300 i = self.configbool("ui", "formatted")
1301 i = self.configbool("ui", "formatted")
1301 if i is None:
1302 if i is None:
1302 # some environments replace stdout without implementing isatty
1303 # some environments replace stdout without implementing isatty
1303 # usually those are non-interactive
1304 # usually those are non-interactive
1304 return self._isatty(self.fout)
1305 return self._isatty(self.fout)
1305
1306
1306 return i
1307 return i
1307
1308
1308 def _readline(self):
1309 def _readline(self):
1309 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1310 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1310 # because they have to be text streams with *no buffering*. Instead,
1311 # because they have to be text streams with *no buffering*. Instead,
1311 # we use rawinput() only if call_readline() will be invoked by
1312 # we use rawinput() only if call_readline() will be invoked by
1312 # PyOS_Readline(), so no I/O will be made at Python layer.
1313 # PyOS_Readline(), so no I/O will be made at Python layer.
1313 usereadline = (self._isatty(self.fin) and self._isatty(self.fout)
1314 usereadline = (self._isatty(self.fin) and self._isatty(self.fout)
1314 and procutil.isstdin(self.fin)
1315 and procutil.isstdin(self.fin)
1315 and procutil.isstdout(self.fout))
1316 and procutil.isstdout(self.fout))
1316 if usereadline:
1317 if usereadline:
1317 try:
1318 try:
1318 # magically add command line editing support, where
1319 # magically add command line editing support, where
1319 # available
1320 # available
1320 import readline
1321 import readline
1321 # force demandimport to really load the module
1322 # force demandimport to really load the module
1322 readline.read_history_file
1323 readline.read_history_file
1323 # windows sometimes raises something other than ImportError
1324 # windows sometimes raises something other than ImportError
1324 except Exception:
1325 except Exception:
1325 usereadline = False
1326 usereadline = False
1326
1327
1327 # prompt ' ' must exist; otherwise readline may delete entire line
1328 # prompt ' ' must exist; otherwise readline may delete entire line
1328 # - http://bugs.python.org/issue12833
1329 # - http://bugs.python.org/issue12833
1329 with self.timeblockedsection('stdio'):
1330 with self.timeblockedsection('stdio'):
1330 if usereadline:
1331 if usereadline:
1331 line = encoding.strtolocal(pycompat.rawinput(r' '))
1332 line = encoding.strtolocal(pycompat.rawinput(r' '))
1332 # When stdin is in binary mode on Windows, it can cause
1333 # When stdin is in binary mode on Windows, it can cause
1333 # raw_input() to emit an extra trailing carriage return
1334 # raw_input() to emit an extra trailing carriage return
1334 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1335 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1335 line = line[:-1]
1336 line = line[:-1]
1336 else:
1337 else:
1337 self.fout.write(b' ')
1338 self.fout.write(b' ')
1338 self.fout.flush()
1339 self.fout.flush()
1339 line = self.fin.readline()
1340 line = self.fin.readline()
1340 if not line:
1341 if not line:
1341 raise EOFError
1342 raise EOFError
1342 line = line.rstrip(pycompat.oslinesep)
1343 line = line.rstrip(pycompat.oslinesep)
1343
1344
1344 return line
1345 return line
1345
1346
1346 def prompt(self, msg, default="y"):
1347 def prompt(self, msg, default="y"):
1347 """Prompt user with msg, read response.
1348 """Prompt user with msg, read response.
1348 If ui is not interactive, the default is returned.
1349 If ui is not interactive, the default is returned.
1349 """
1350 """
1350 if not self.interactive():
1351 if not self.interactive():
1351 self.write(msg, ' ', default or '', "\n")
1352 self.write(msg, ' ', default or '', "\n")
1352 return default
1353 return default
1353 self._writenobuf(msg, label='ui.prompt')
1354 self._writenobuf(msg, label='ui.prompt')
1354 self.flush()
1355 self.flush()
1355 try:
1356 try:
1356 r = self._readline()
1357 r = self._readline()
1357 if not r:
1358 if not r:
1358 r = default
1359 r = default
1359 if self.configbool('ui', 'promptecho'):
1360 if self.configbool('ui', 'promptecho'):
1360 self.write(r, "\n")
1361 self.write(r, "\n")
1361 return r
1362 return r
1362 except EOFError:
1363 except EOFError:
1363 raise error.ResponseExpected()
1364 raise error.ResponseExpected()
1364
1365
1365 @staticmethod
1366 @staticmethod
1366 def extractchoices(prompt):
1367 def extractchoices(prompt):
1367 """Extract prompt message and list of choices from specified prompt.
1368 """Extract prompt message and list of choices from specified prompt.
1368
1369
1369 This returns tuple "(message, choices)", and "choices" is the
1370 This returns tuple "(message, choices)", and "choices" is the
1370 list of tuple "(response character, text without &)".
1371 list of tuple "(response character, text without &)".
1371
1372
1372 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1373 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1373 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1374 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1374 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1375 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1375 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1376 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1376 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1377 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1377 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1378 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1378 """
1379 """
1379
1380
1380 # Sadly, the prompt string may have been built with a filename
1381 # Sadly, the prompt string may have been built with a filename
1381 # containing "$$" so let's try to find the first valid-looking
1382 # containing "$$" so let's try to find the first valid-looking
1382 # prompt to start parsing. Sadly, we also can't rely on
1383 # prompt to start parsing. Sadly, we also can't rely on
1383 # choices containing spaces, ASCII, or basically anything
1384 # choices containing spaces, ASCII, or basically anything
1384 # except an ampersand followed by a character.
1385 # except an ampersand followed by a character.
1385 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1386 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1386 msg = m.group(1)
1387 msg = m.group(1)
1387 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1388 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1388 def choicetuple(s):
1389 def choicetuple(s):
1389 ampidx = s.index('&')
1390 ampidx = s.index('&')
1390 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1391 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1391 return (msg, [choicetuple(s) for s in choices])
1392 return (msg, [choicetuple(s) for s in choices])
1392
1393
1393 def promptchoice(self, prompt, default=0):
1394 def promptchoice(self, prompt, default=0):
1394 """Prompt user with a message, read response, and ensure it matches
1395 """Prompt user with a message, read response, and ensure it matches
1395 one of the provided choices. The prompt is formatted as follows:
1396 one of the provided choices. The prompt is formatted as follows:
1396
1397
1397 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1398 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1398
1399
1399 The index of the choice is returned. Responses are case
1400 The index of the choice is returned. Responses are case
1400 insensitive. If ui is not interactive, the default is
1401 insensitive. If ui is not interactive, the default is
1401 returned.
1402 returned.
1402 """
1403 """
1403
1404
1404 msg, choices = self.extractchoices(prompt)
1405 msg, choices = self.extractchoices(prompt)
1405 resps = [r for r, t in choices]
1406 resps = [r for r, t in choices]
1406 while True:
1407 while True:
1407 r = self.prompt(msg, resps[default])
1408 r = self.prompt(msg, resps[default])
1408 if r.lower() in resps:
1409 if r.lower() in resps:
1409 return resps.index(r.lower())
1410 return resps.index(r.lower())
1410 self.write(_("unrecognized response\n"))
1411 self.write(_("unrecognized response\n"))
1411
1412
1412 def getpass(self, prompt=None, default=None):
1413 def getpass(self, prompt=None, default=None):
1413 if not self.interactive():
1414 if not self.interactive():
1414 return default
1415 return default
1415 try:
1416 try:
1416 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1417 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1417 # disable getpass() only if explicitly specified. it's still valid
1418 # disable getpass() only if explicitly specified. it's still valid
1418 # to interact with tty even if fin is not a tty.
1419 # to interact with tty even if fin is not a tty.
1419 with self.timeblockedsection('stdio'):
1420 with self.timeblockedsection('stdio'):
1420 if self.configbool('ui', 'nontty'):
1421 if self.configbool('ui', 'nontty'):
1421 l = self.fin.readline()
1422 l = self.fin.readline()
1422 if not l:
1423 if not l:
1423 raise EOFError
1424 raise EOFError
1424 return l.rstrip('\n')
1425 return l.rstrip('\n')
1425 else:
1426 else:
1426 return getpass.getpass('')
1427 return getpass.getpass('')
1427 except EOFError:
1428 except EOFError:
1428 raise error.ResponseExpected()
1429 raise error.ResponseExpected()
1429
1430
1430 def status(self, *msg, **opts):
1431 def status(self, *msg, **opts):
1431 '''write status message to output (if ui.quiet is False)
1432 '''write status message to output (if ui.quiet is False)
1432
1433
1433 This adds an output label of "ui.status".
1434 This adds an output label of "ui.status".
1434 '''
1435 '''
1435 if not self.quiet:
1436 if not self.quiet:
1436 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1437 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1437 self.write(*msg, **opts)
1438 self.write(*msg, **opts)
1438
1439
1439 def warn(self, *msg, **opts):
1440 def warn(self, *msg, **opts):
1440 '''write warning message to output (stderr)
1441 '''write warning message to output (stderr)
1441
1442
1442 This adds an output label of "ui.warning".
1443 This adds an output label of "ui.warning".
1443 '''
1444 '''
1444 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1445 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1445 self.write_err(*msg, **opts)
1446 self.write_err(*msg, **opts)
1446
1447
1447 def error(self, *msg, **opts):
1448 def error(self, *msg, **opts):
1448 '''write error message to output (stderr)
1449 '''write error message to output (stderr)
1449
1450
1450 This adds an output label of "ui.error".
1451 This adds an output label of "ui.error".
1451 '''
1452 '''
1452 opts[r'label'] = opts.get(r'label', '') + ' ui.error'
1453 opts[r'label'] = opts.get(r'label', '') + ' ui.error'
1453 self.write_err(*msg, **opts)
1454 self.write_err(*msg, **opts)
1454
1455
1455 def note(self, *msg, **opts):
1456 def note(self, *msg, **opts):
1456 '''write note to output (if ui.verbose is True)
1457 '''write note to output (if ui.verbose is True)
1457
1458
1458 This adds an output label of "ui.note".
1459 This adds an output label of "ui.note".
1459 '''
1460 '''
1460 if self.verbose:
1461 if self.verbose:
1461 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1462 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1462 self.write(*msg, **opts)
1463 self.write(*msg, **opts)
1463
1464
1464 def debug(self, *msg, **opts):
1465 def debug(self, *msg, **opts):
1465 '''write debug message to output (if ui.debugflag is True)
1466 '''write debug message to output (if ui.debugflag is True)
1466
1467
1467 This adds an output label of "ui.debug".
1468 This adds an output label of "ui.debug".
1468 '''
1469 '''
1469 if self.debugflag:
1470 if self.debugflag:
1470 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1471 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1471 self.write(*msg, **opts)
1472 self.write(*msg, **opts)
1472
1473
1473 def edit(self, text, user, extra=None, editform=None, pending=None,
1474 def edit(self, text, user, extra=None, editform=None, pending=None,
1474 repopath=None, action=None):
1475 repopath=None, action=None):
1475 if action is None:
1476 if action is None:
1476 self.develwarn('action is None but will soon be a required '
1477 self.develwarn('action is None but will soon be a required '
1477 'parameter to ui.edit()')
1478 'parameter to ui.edit()')
1478 extra_defaults = {
1479 extra_defaults = {
1479 'prefix': 'editor',
1480 'prefix': 'editor',
1480 'suffix': '.txt',
1481 'suffix': '.txt',
1481 }
1482 }
1482 if extra is not None:
1483 if extra is not None:
1483 if extra.get('suffix') is not None:
1484 if extra.get('suffix') is not None:
1484 self.develwarn('extra.suffix is not None but will soon be '
1485 self.develwarn('extra.suffix is not None but will soon be '
1485 'ignored by ui.edit()')
1486 'ignored by ui.edit()')
1486 extra_defaults.update(extra)
1487 extra_defaults.update(extra)
1487 extra = extra_defaults
1488 extra = extra_defaults
1488
1489
1489 if action == 'diff':
1490 if action == 'diff':
1490 suffix = '.diff'
1491 suffix = '.diff'
1491 elif action:
1492 elif action:
1492 suffix = '.%s.hg.txt' % action
1493 suffix = '.%s.hg.txt' % action
1493 else:
1494 else:
1494 suffix = extra['suffix']
1495 suffix = extra['suffix']
1495
1496
1496 rdir = None
1497 rdir = None
1497 if self.configbool('experimental', 'editortmpinhg'):
1498 if self.configbool('experimental', 'editortmpinhg'):
1498 rdir = repopath
1499 rdir = repopath
1499 (fd, name) = pycompat.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1500 (fd, name) = pycompat.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1500 suffix=suffix,
1501 suffix=suffix,
1501 dir=rdir)
1502 dir=rdir)
1502 try:
1503 try:
1503 f = os.fdopen(fd, r'wb')
1504 f = os.fdopen(fd, r'wb')
1504 f.write(util.tonativeeol(text))
1505 f.write(util.tonativeeol(text))
1505 f.close()
1506 f.close()
1506
1507
1507 environ = {'HGUSER': user}
1508 environ = {'HGUSER': user}
1508 if 'transplant_source' in extra:
1509 if 'transplant_source' in extra:
1509 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1510 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1510 for label in ('intermediate-source', 'source', 'rebase_source'):
1511 for label in ('intermediate-source', 'source', 'rebase_source'):
1511 if label in extra:
1512 if label in extra:
1512 environ.update({'HGREVISION': extra[label]})
1513 environ.update({'HGREVISION': extra[label]})
1513 break
1514 break
1514 if editform:
1515 if editform:
1515 environ.update({'HGEDITFORM': editform})
1516 environ.update({'HGEDITFORM': editform})
1516 if pending:
1517 if pending:
1517 environ.update({'HG_PENDING': pending})
1518 environ.update({'HG_PENDING': pending})
1518
1519
1519 editor = self.geteditor()
1520 editor = self.geteditor()
1520
1521
1521 self.system("%s \"%s\"" % (editor, name),
1522 self.system("%s \"%s\"" % (editor, name),
1522 environ=environ,
1523 environ=environ,
1523 onerr=error.Abort, errprefix=_("edit failed"),
1524 onerr=error.Abort, errprefix=_("edit failed"),
1524 blockedtag='editor')
1525 blockedtag='editor')
1525
1526
1526 f = open(name, r'rb')
1527 f = open(name, r'rb')
1527 t = util.fromnativeeol(f.read())
1528 t = util.fromnativeeol(f.read())
1528 f.close()
1529 f.close()
1529 finally:
1530 finally:
1530 os.unlink(name)
1531 os.unlink(name)
1531
1532
1532 return t
1533 return t
1533
1534
1534 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1535 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1535 blockedtag=None):
1536 blockedtag=None):
1536 '''execute shell command with appropriate output stream. command
1537 '''execute shell command with appropriate output stream. command
1537 output will be redirected if fout is not stdout.
1538 output will be redirected if fout is not stdout.
1538
1539
1539 if command fails and onerr is None, return status, else raise onerr
1540 if command fails and onerr is None, return status, else raise onerr
1540 object as exception.
1541 object as exception.
1541 '''
1542 '''
1542 if blockedtag is None:
1543 if blockedtag is None:
1543 # Long cmds tend to be because of an absolute path on cmd. Keep
1544 # Long cmds tend to be because of an absolute path on cmd. Keep
1544 # the tail end instead
1545 # the tail end instead
1545 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1546 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1546 blockedtag = 'unknown_system_' + cmdsuffix
1547 blockedtag = 'unknown_system_' + cmdsuffix
1547 out = self.fout
1548 out = self.fout
1548 if any(s[1] for s in self._bufferstates):
1549 if any(s[1] for s in self._bufferstates):
1549 out = self
1550 out = self
1550 with self.timeblockedsection(blockedtag):
1551 with self.timeblockedsection(blockedtag):
1551 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1552 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1552 if rc and onerr:
1553 if rc and onerr:
1553 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1554 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1554 procutil.explainexit(rc))
1555 procutil.explainexit(rc))
1555 if errprefix:
1556 if errprefix:
1556 errmsg = '%s: %s' % (errprefix, errmsg)
1557 errmsg = '%s: %s' % (errprefix, errmsg)
1557 raise onerr(errmsg)
1558 raise onerr(errmsg)
1558 return rc
1559 return rc
1559
1560
1560 def _runsystem(self, cmd, environ, cwd, out):
1561 def _runsystem(self, cmd, environ, cwd, out):
1561 """actually execute the given shell command (can be overridden by
1562 """actually execute the given shell command (can be overridden by
1562 extensions like chg)"""
1563 extensions like chg)"""
1563 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1564 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1564
1565
1565 def traceback(self, exc=None, force=False):
1566 def traceback(self, exc=None, force=False):
1566 '''print exception traceback if traceback printing enabled or forced.
1567 '''print exception traceback if traceback printing enabled or forced.
1567 only to call in exception handler. returns true if traceback
1568 only to call in exception handler. returns true if traceback
1568 printed.'''
1569 printed.'''
1569 if self.tracebackflag or force:
1570 if self.tracebackflag or force:
1570 if exc is None:
1571 if exc is None:
1571 exc = sys.exc_info()
1572 exc = sys.exc_info()
1572 cause = getattr(exc[1], 'cause', None)
1573 cause = getattr(exc[1], 'cause', None)
1573
1574
1574 if cause is not None:
1575 if cause is not None:
1575 causetb = traceback.format_tb(cause[2])
1576 causetb = traceback.format_tb(cause[2])
1576 exctb = traceback.format_tb(exc[2])
1577 exctb = traceback.format_tb(exc[2])
1577 exconly = traceback.format_exception_only(cause[0], cause[1])
1578 exconly = traceback.format_exception_only(cause[0], cause[1])
1578
1579
1579 # exclude frame where 'exc' was chained and rethrown from exctb
1580 # exclude frame where 'exc' was chained and rethrown from exctb
1580 self.write_err('Traceback (most recent call last):\n',
1581 self.write_err('Traceback (most recent call last):\n',
1581 ''.join(exctb[:-1]),
1582 ''.join(exctb[:-1]),
1582 ''.join(causetb),
1583 ''.join(causetb),
1583 ''.join(exconly))
1584 ''.join(exconly))
1584 else:
1585 else:
1585 output = traceback.format_exception(exc[0], exc[1], exc[2])
1586 output = traceback.format_exception(exc[0], exc[1], exc[2])
1586 self.write_err(encoding.strtolocal(r''.join(output)))
1587 self.write_err(encoding.strtolocal(r''.join(output)))
1587 return self.tracebackflag or force
1588 return self.tracebackflag or force
1588
1589
1589 def geteditor(self):
1590 def geteditor(self):
1590 '''return editor to use'''
1591 '''return editor to use'''
1591 if pycompat.sysplatform == 'plan9':
1592 if pycompat.sysplatform == 'plan9':
1592 # vi is the MIPS instruction simulator on Plan 9. We
1593 # vi is the MIPS instruction simulator on Plan 9. We
1593 # instead default to E to plumb commit messages to
1594 # instead default to E to plumb commit messages to
1594 # avoid confusion.
1595 # avoid confusion.
1595 editor = 'E'
1596 editor = 'E'
1596 else:
1597 else:
1597 editor = 'vi'
1598 editor = 'vi'
1598 return (encoding.environ.get("HGEDITOR") or
1599 return (encoding.environ.get("HGEDITOR") or
1599 self.config("ui", "editor", editor))
1600 self.config("ui", "editor", editor))
1600
1601
1601 @util.propertycache
1602 @util.propertycache
1602 def _progbar(self):
1603 def _progbar(self):
1603 """setup the progbar singleton to the ui object"""
1604 """setup the progbar singleton to the ui object"""
1604 if (self.quiet or self.debugflag
1605 if (self.quiet or self.debugflag
1605 or self.configbool('progress', 'disable')
1606 or self.configbool('progress', 'disable')
1606 or not progress.shouldprint(self)):
1607 or not progress.shouldprint(self)):
1607 return None
1608 return None
1608 return getprogbar(self)
1609 return getprogbar(self)
1609
1610
1610 def _progclear(self):
1611 def _progclear(self):
1611 """clear progress bar output if any. use it before any output"""
1612 """clear progress bar output if any. use it before any output"""
1612 if not haveprogbar(): # nothing loaded yet
1613 if not haveprogbar(): # nothing loaded yet
1613 return
1614 return
1614 if self._progbar is not None and self._progbar.printed:
1615 if self._progbar is not None and self._progbar.printed:
1615 self._progbar.clear()
1616 self._progbar.clear()
1616
1617
1617 def progress(self, topic, pos, item="", unit="", total=None):
1618 def progress(self, topic, pos, item="", unit="", total=None):
1618 '''show a progress message
1619 '''show a progress message
1619
1620
1620 By default a textual progress bar will be displayed if an operation
1621 By default a textual progress bar will be displayed if an operation
1621 takes too long. 'topic' is the current operation, 'item' is a
1622 takes too long. 'topic' is the current operation, 'item' is a
1622 non-numeric marker of the current position (i.e. the currently
1623 non-numeric marker of the current position (i.e. the currently
1623 in-process file), 'pos' is the current numeric position (i.e.
1624 in-process file), 'pos' is the current numeric position (i.e.
1624 revision, bytes, etc.), unit is a corresponding unit label,
1625 revision, bytes, etc.), unit is a corresponding unit label,
1625 and total is the highest expected pos.
1626 and total is the highest expected pos.
1626
1627
1627 Multiple nested topics may be active at a time.
1628 Multiple nested topics may be active at a time.
1628
1629
1629 All topics should be marked closed by setting pos to None at
1630 All topics should be marked closed by setting pos to None at
1630 termination.
1631 termination.
1631 '''
1632 '''
1632 if self._progbar is not None:
1633 if self._progbar is not None:
1633 self._progbar.progress(topic, pos, item=item, unit=unit,
1634 self._progbar.progress(topic, pos, item=item, unit=unit,
1634 total=total)
1635 total=total)
1635 if pos is None or not self.configbool('progress', 'debug'):
1636 if pos is None or not self.configbool('progress', 'debug'):
1636 return
1637 return
1637
1638
1638 if unit:
1639 if unit:
1639 unit = ' ' + unit
1640 unit = ' ' + unit
1640 if item:
1641 if item:
1641 item = ' ' + item
1642 item = ' ' + item
1642
1643
1643 if total:
1644 if total:
1644 pct = 100.0 * pos / total
1645 pct = 100.0 * pos / total
1645 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1646 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1646 % (topic, item, pos, total, unit, pct))
1647 % (topic, item, pos, total, unit, pct))
1647 else:
1648 else:
1648 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1649 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1649
1650
1650 def makeprogress(self, topic, unit="", total=None):
1651 def makeprogress(self, topic, unit="", total=None):
1651 '''exists only so low-level modules won't need to import scmutil'''
1652 '''exists only so low-level modules won't need to import scmutil'''
1652 return scmutil.progress(self, topic, unit, total)
1653 return scmutil.progress(self, topic, unit, total)
1653
1654
1654 def log(self, service, *msg, **opts):
1655 def log(self, service, *msg, **opts):
1655 '''hook for logging facility extensions
1656 '''hook for logging facility extensions
1656
1657
1657 service should be a readily-identifiable subsystem, which will
1658 service should be a readily-identifiable subsystem, which will
1658 allow filtering.
1659 allow filtering.
1659
1660
1660 *msg should be a newline-terminated format string to log, and
1661 *msg should be a newline-terminated format string to log, and
1661 then any values to %-format into that format string.
1662 then any values to %-format into that format string.
1662
1663
1663 **opts currently has no defined meanings.
1664 **opts currently has no defined meanings.
1664 '''
1665 '''
1665
1666
1666 def label(self, msg, label):
1667 def label(self, msg, label):
1667 '''style msg based on supplied label
1668 '''style msg based on supplied label
1668
1669
1669 If some color mode is enabled, this will add the necessary control
1670 If some color mode is enabled, this will add the necessary control
1670 characters to apply such color. In addition, 'debug' color mode adds
1671 characters to apply such color. In addition, 'debug' color mode adds
1671 markup showing which label affects a piece of text.
1672 markup showing which label affects a piece of text.
1672
1673
1673 ui.write(s, 'label') is equivalent to
1674 ui.write(s, 'label') is equivalent to
1674 ui.write(ui.label(s, 'label')).
1675 ui.write(ui.label(s, 'label')).
1675 '''
1676 '''
1676 if self._colormode is not None:
1677 if self._colormode is not None:
1677 return color.colorlabel(self, msg, label)
1678 return color.colorlabel(self, msg, label)
1678 return msg
1679 return msg
1679
1680
1680 def develwarn(self, msg, stacklevel=1, config=None):
1681 def develwarn(self, msg, stacklevel=1, config=None):
1681 """issue a developer warning message
1682 """issue a developer warning message
1682
1683
1683 Use 'stacklevel' to report the offender some layers further up in the
1684 Use 'stacklevel' to report the offender some layers further up in the
1684 stack.
1685 stack.
1685 """
1686 """
1686 if not self.configbool('devel', 'all-warnings'):
1687 if not self.configbool('devel', 'all-warnings'):
1687 if config is None or not self.configbool('devel', config):
1688 if config is None or not self.configbool('devel', config):
1688 return
1689 return
1689 msg = 'devel-warn: ' + msg
1690 msg = 'devel-warn: ' + msg
1690 stacklevel += 1 # get in develwarn
1691 stacklevel += 1 # get in develwarn
1691 if self.tracebackflag:
1692 if self.tracebackflag:
1692 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1693 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1693 self.log('develwarn', '%s at:\n%s' %
1694 self.log('develwarn', '%s at:\n%s' %
1694 (msg, ''.join(util.getstackframes(stacklevel))))
1695 (msg, ''.join(util.getstackframes(stacklevel))))
1695 else:
1696 else:
1696 curframe = inspect.currentframe()
1697 curframe = inspect.currentframe()
1697 calframe = inspect.getouterframes(curframe, 2)
1698 calframe = inspect.getouterframes(curframe, 2)
1698 fname, lineno, fmsg = calframe[stacklevel][1:4]
1699 fname, lineno, fmsg = calframe[stacklevel][1:4]
1699 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
1700 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
1700 self.write_err('%s at: %s:%d (%s)\n'
1701 self.write_err('%s at: %s:%d (%s)\n'
1701 % (msg, fname, lineno, fmsg))
1702 % (msg, fname, lineno, fmsg))
1702 self.log('develwarn', '%s at: %s:%d (%s)\n',
1703 self.log('develwarn', '%s at: %s:%d (%s)\n',
1703 msg, fname, lineno, fmsg)
1704 msg, fname, lineno, fmsg)
1704 curframe = calframe = None # avoid cycles
1705 curframe = calframe = None # avoid cycles
1705
1706
1706 def deprecwarn(self, msg, version, stacklevel=2):
1707 def deprecwarn(self, msg, version, stacklevel=2):
1707 """issue a deprecation warning
1708 """issue a deprecation warning
1708
1709
1709 - msg: message explaining what is deprecated and how to upgrade,
1710 - msg: message explaining what is deprecated and how to upgrade,
1710 - version: last version where the API will be supported,
1711 - version: last version where the API will be supported,
1711 """
1712 """
1712 if not (self.configbool('devel', 'all-warnings')
1713 if not (self.configbool('devel', 'all-warnings')
1713 or self.configbool('devel', 'deprec-warn')):
1714 or self.configbool('devel', 'deprec-warn')):
1714 return
1715 return
1715 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1716 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1716 " update your code.)") % version
1717 " update your code.)") % version
1717 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1718 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1718
1719
1719 def exportableenviron(self):
1720 def exportableenviron(self):
1720 """The environment variables that are safe to export, e.g. through
1721 """The environment variables that are safe to export, e.g. through
1721 hgweb.
1722 hgweb.
1722 """
1723 """
1723 return self._exportableenviron
1724 return self._exportableenviron
1724
1725
1725 @contextlib.contextmanager
1726 @contextlib.contextmanager
1726 def configoverride(self, overrides, source=""):
1727 def configoverride(self, overrides, source=""):
1727 """Context manager for temporary config overrides
1728 """Context manager for temporary config overrides
1728 `overrides` must be a dict of the following structure:
1729 `overrides` must be a dict of the following structure:
1729 {(section, name) : value}"""
1730 {(section, name) : value}"""
1730 backups = {}
1731 backups = {}
1731 try:
1732 try:
1732 for (section, name), value in overrides.items():
1733 for (section, name), value in overrides.items():
1733 backups[(section, name)] = self.backupconfig(section, name)
1734 backups[(section, name)] = self.backupconfig(section, name)
1734 self.setconfig(section, name, value, source)
1735 self.setconfig(section, name, value, source)
1735 yield
1736 yield
1736 finally:
1737 finally:
1737 for __, backup in backups.items():
1738 for __, backup in backups.items():
1738 self.restoreconfig(backup)
1739 self.restoreconfig(backup)
1739 # just restoring ui.quiet config to the previous value is not enough
1740 # just restoring ui.quiet config to the previous value is not enough
1740 # as it does not update ui.quiet class member
1741 # as it does not update ui.quiet class member
1741 if ('ui', 'quiet') in overrides:
1742 if ('ui', 'quiet') in overrides:
1742 self.fixconfig(section='ui')
1743 self.fixconfig(section='ui')
1743
1744
1744 class paths(dict):
1745 class paths(dict):
1745 """Represents a collection of paths and their configs.
1746 """Represents a collection of paths and their configs.
1746
1747
1747 Data is initially derived from ui instances and the config files they have
1748 Data is initially derived from ui instances and the config files they have
1748 loaded.
1749 loaded.
1749 """
1750 """
1750 def __init__(self, ui):
1751 def __init__(self, ui):
1751 dict.__init__(self)
1752 dict.__init__(self)
1752
1753
1753 for name, loc in ui.configitems('paths', ignoresub=True):
1754 for name, loc in ui.configitems('paths', ignoresub=True):
1754 # No location is the same as not existing.
1755 # No location is the same as not existing.
1755 if not loc:
1756 if not loc:
1756 continue
1757 continue
1757 loc, sub = ui.configsuboptions('paths', name)
1758 loc, sub = ui.configsuboptions('paths', name)
1758 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1759 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1759
1760
1760 def getpath(self, name, default=None):
1761 def getpath(self, name, default=None):
1761 """Return a ``path`` from a string, falling back to default.
1762 """Return a ``path`` from a string, falling back to default.
1762
1763
1763 ``name`` can be a named path or locations. Locations are filesystem
1764 ``name`` can be a named path or locations. Locations are filesystem
1764 paths or URIs.
1765 paths or URIs.
1765
1766
1766 Returns None if ``name`` is not a registered path, a URI, or a local
1767 Returns None if ``name`` is not a registered path, a URI, or a local
1767 path to a repo.
1768 path to a repo.
1768 """
1769 """
1769 # Only fall back to default if no path was requested.
1770 # Only fall back to default if no path was requested.
1770 if name is None:
1771 if name is None:
1771 if not default:
1772 if not default:
1772 default = ()
1773 default = ()
1773 elif not isinstance(default, (tuple, list)):
1774 elif not isinstance(default, (tuple, list)):
1774 default = (default,)
1775 default = (default,)
1775 for k in default:
1776 for k in default:
1776 try:
1777 try:
1777 return self[k]
1778 return self[k]
1778 except KeyError:
1779 except KeyError:
1779 continue
1780 continue
1780 return None
1781 return None
1781
1782
1782 # Most likely empty string.
1783 # Most likely empty string.
1783 # This may need to raise in the future.
1784 # This may need to raise in the future.
1784 if not name:
1785 if not name:
1785 return None
1786 return None
1786
1787
1787 try:
1788 try:
1788 return self[name]
1789 return self[name]
1789 except KeyError:
1790 except KeyError:
1790 # Try to resolve as a local path or URI.
1791 # Try to resolve as a local path or URI.
1791 try:
1792 try:
1792 # We don't pass sub-options in, so no need to pass ui instance.
1793 # We don't pass sub-options in, so no need to pass ui instance.
1793 return path(None, None, rawloc=name)
1794 return path(None, None, rawloc=name)
1794 except ValueError:
1795 except ValueError:
1795 raise error.RepoError(_('repository %s does not exist') %
1796 raise error.RepoError(_('repository %s does not exist') %
1796 name)
1797 name)
1797
1798
1798 _pathsuboptions = {}
1799 _pathsuboptions = {}
1799
1800
1800 def pathsuboption(option, attr):
1801 def pathsuboption(option, attr):
1801 """Decorator used to declare a path sub-option.
1802 """Decorator used to declare a path sub-option.
1802
1803
1803 Arguments are the sub-option name and the attribute it should set on
1804 Arguments are the sub-option name and the attribute it should set on
1804 ``path`` instances.
1805 ``path`` instances.
1805
1806
1806 The decorated function will receive as arguments a ``ui`` instance,
1807 The decorated function will receive as arguments a ``ui`` instance,
1807 ``path`` instance, and the string value of this option from the config.
1808 ``path`` instance, and the string value of this option from the config.
1808 The function should return the value that will be set on the ``path``
1809 The function should return the value that will be set on the ``path``
1809 instance.
1810 instance.
1810
1811
1811 This decorator can be used to perform additional verification of
1812 This decorator can be used to perform additional verification of
1812 sub-options and to change the type of sub-options.
1813 sub-options and to change the type of sub-options.
1813 """
1814 """
1814 def register(func):
1815 def register(func):
1815 _pathsuboptions[option] = (attr, func)
1816 _pathsuboptions[option] = (attr, func)
1816 return func
1817 return func
1817 return register
1818 return register
1818
1819
1819 @pathsuboption('pushurl', 'pushloc')
1820 @pathsuboption('pushurl', 'pushloc')
1820 def pushurlpathoption(ui, path, value):
1821 def pushurlpathoption(ui, path, value):
1821 u = util.url(value)
1822 u = util.url(value)
1822 # Actually require a URL.
1823 # Actually require a URL.
1823 if not u.scheme:
1824 if not u.scheme:
1824 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1825 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1825 return None
1826 return None
1826
1827
1827 # Don't support the #foo syntax in the push URL to declare branch to
1828 # Don't support the #foo syntax in the push URL to declare branch to
1828 # push.
1829 # push.
1829 if u.fragment:
1830 if u.fragment:
1830 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1831 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1831 'ignoring)\n') % path.name)
1832 'ignoring)\n') % path.name)
1832 u.fragment = None
1833 u.fragment = None
1833
1834
1834 return bytes(u)
1835 return bytes(u)
1835
1836
1836 @pathsuboption('pushrev', 'pushrev')
1837 @pathsuboption('pushrev', 'pushrev')
1837 def pushrevpathoption(ui, path, value):
1838 def pushrevpathoption(ui, path, value):
1838 return value
1839 return value
1839
1840
1840 class path(object):
1841 class path(object):
1841 """Represents an individual path and its configuration."""
1842 """Represents an individual path and its configuration."""
1842
1843
1843 def __init__(self, ui, name, rawloc=None, suboptions=None):
1844 def __init__(self, ui, name, rawloc=None, suboptions=None):
1844 """Construct a path from its config options.
1845 """Construct a path from its config options.
1845
1846
1846 ``ui`` is the ``ui`` instance the path is coming from.
1847 ``ui`` is the ``ui`` instance the path is coming from.
1847 ``name`` is the symbolic name of the path.
1848 ``name`` is the symbolic name of the path.
1848 ``rawloc`` is the raw location, as defined in the config.
1849 ``rawloc`` is the raw location, as defined in the config.
1849 ``pushloc`` is the raw locations pushes should be made to.
1850 ``pushloc`` is the raw locations pushes should be made to.
1850
1851
1851 If ``name`` is not defined, we require that the location be a) a local
1852 If ``name`` is not defined, we require that the location be a) a local
1852 filesystem path with a .hg directory or b) a URL. If not,
1853 filesystem path with a .hg directory or b) a URL. If not,
1853 ``ValueError`` is raised.
1854 ``ValueError`` is raised.
1854 """
1855 """
1855 if not rawloc:
1856 if not rawloc:
1856 raise ValueError('rawloc must be defined')
1857 raise ValueError('rawloc must be defined')
1857
1858
1858 # Locations may define branches via syntax <base>#<branch>.
1859 # Locations may define branches via syntax <base>#<branch>.
1859 u = util.url(rawloc)
1860 u = util.url(rawloc)
1860 branch = None
1861 branch = None
1861 if u.fragment:
1862 if u.fragment:
1862 branch = u.fragment
1863 branch = u.fragment
1863 u.fragment = None
1864 u.fragment = None
1864
1865
1865 self.url = u
1866 self.url = u
1866 self.branch = branch
1867 self.branch = branch
1867
1868
1868 self.name = name
1869 self.name = name
1869 self.rawloc = rawloc
1870 self.rawloc = rawloc
1870 self.loc = '%s' % u
1871 self.loc = '%s' % u
1871
1872
1872 # When given a raw location but not a symbolic name, validate the
1873 # When given a raw location but not a symbolic name, validate the
1873 # location is valid.
1874 # location is valid.
1874 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1875 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1875 raise ValueError('location is not a URL or path to a local '
1876 raise ValueError('location is not a URL or path to a local '
1876 'repo: %s' % rawloc)
1877 'repo: %s' % rawloc)
1877
1878
1878 suboptions = suboptions or {}
1879 suboptions = suboptions or {}
1879
1880
1880 # Now process the sub-options. If a sub-option is registered, its
1881 # Now process the sub-options. If a sub-option is registered, its
1881 # attribute will always be present. The value will be None if there
1882 # attribute will always be present. The value will be None if there
1882 # was no valid sub-option.
1883 # was no valid sub-option.
1883 for suboption, (attr, func) in _pathsuboptions.iteritems():
1884 for suboption, (attr, func) in _pathsuboptions.iteritems():
1884 if suboption not in suboptions:
1885 if suboption not in suboptions:
1885 setattr(self, attr, None)
1886 setattr(self, attr, None)
1886 continue
1887 continue
1887
1888
1888 value = func(ui, self, suboptions[suboption])
1889 value = func(ui, self, suboptions[suboption])
1889 setattr(self, attr, value)
1890 setattr(self, attr, value)
1890
1891
1891 def _isvalidlocalpath(self, path):
1892 def _isvalidlocalpath(self, path):
1892 """Returns True if the given path is a potentially valid repository.
1893 """Returns True if the given path is a potentially valid repository.
1893 This is its own function so that extensions can change the definition of
1894 This is its own function so that extensions can change the definition of
1894 'valid' in this case (like when pulling from a git repo into a hg
1895 'valid' in this case (like when pulling from a git repo into a hg
1895 one)."""
1896 one)."""
1896 return os.path.isdir(os.path.join(path, '.hg'))
1897 return os.path.isdir(os.path.join(path, '.hg'))
1897
1898
1898 @property
1899 @property
1899 def suboptions(self):
1900 def suboptions(self):
1900 """Return sub-options and their values for this path.
1901 """Return sub-options and their values for this path.
1901
1902
1902 This is intended to be used for presentation purposes.
1903 This is intended to be used for presentation purposes.
1903 """
1904 """
1904 d = {}
1905 d = {}
1905 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1906 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1906 value = getattr(self, attr)
1907 value = getattr(self, attr)
1907 if value is not None:
1908 if value is not None:
1908 d[subopt] = value
1909 d[subopt] = value
1909 return d
1910 return d
1910
1911
1911 # we instantiate one globally shared progress bar to avoid
1912 # we instantiate one globally shared progress bar to avoid
1912 # competing progress bars when multiple UI objects get created
1913 # competing progress bars when multiple UI objects get created
1913 _progresssingleton = None
1914 _progresssingleton = None
1914
1915
1915 def getprogbar(ui):
1916 def getprogbar(ui):
1916 global _progresssingleton
1917 global _progresssingleton
1917 if _progresssingleton is None:
1918 if _progresssingleton is None:
1918 # passing 'ui' object to the singleton is fishy,
1919 # passing 'ui' object to the singleton is fishy,
1919 # this is how the extension used to work but feel free to rework it.
1920 # this is how the extension used to work but feel free to rework it.
1920 _progresssingleton = progress.progbar(ui)
1921 _progresssingleton = progress.progbar(ui)
1921 return _progresssingleton
1922 return _progresssingleton
1922
1923
1923 def haveprogbar():
1924 def haveprogbar():
1924 return _progresssingleton is not None
1925 return _progresssingleton is not None
General Comments 0
You need to be logged in to leave comments. Login now