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