##// END OF EJS Templates
py3: don't use str() to stringify pushloc
Yuya Nishihara -
r36749:8988c34b default
parent child Browse files
Show More
@@ -1,389 +1,390 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-bookmarks-current.t
19 test-bookmarks-current.t
20 test-bookmarks-merge.t
20 test-bookmarks-merge.t
21 test-bookmarks-rebase.t
21 test-bookmarks-rebase.t
22 test-bookmarks-strip.t
22 test-bookmarks-strip.t
23 test-bookmarks.t
23 test-bookmarks.t
24 test-branch-option.t
24 test-branch-option.t
25 test-branch-tag-confict.t
25 test-branch-tag-confict.t
26 test-branches.t
26 test-branches.t
27 test-bundle-phases.t
27 test-bundle-phases.t
28 test-bundle-type.t
28 test-bundle-type.t
29 test-bundle-vs-outgoing.t
29 test-bundle-vs-outgoing.t
30 test-bundle2-multiple-changegroups.t
30 test-bundle2-multiple-changegroups.t
31 test-cappedreader.py
31 test-cappedreader.py
32 test-casecollision.t
32 test-casecollision.t
33 test-cat.t
33 test-cat.t
34 test-censor.t
34 test-censor.t
35 test-changelog-exec.t
35 test-changelog-exec.t
36 test-check-commit.t
36 test-check-commit.t
37 test-check-execute.t
37 test-check-execute.t
38 test-check-module-imports.t
38 test-check-module-imports.t
39 test-check-pyflakes.t
39 test-check-pyflakes.t
40 test-check-pylint.t
40 test-check-pylint.t
41 test-check-shbang.t
41 test-check-shbang.t
42 test-children.t
42 test-children.t
43 test-clone-pull-corruption.t
43 test-clone-pull-corruption.t
44 test-clone-r.t
44 test-clone-r.t
45 test-clone-update-order.t
45 test-clone-update-order.t
46 test-command-template.t
46 test-command-template.t
47 test-commit-amend.t
47 test-commit-amend.t
48 test-commit-unresolved.t
48 test-commit-unresolved.t
49 test-commit.t
49 test-commit.t
50 test-completion.t
50 test-completion.t
51 test-config-env.py
51 test-config-env.py
52 test-config.t
52 test-config.t
53 test-conflict.t
53 test-conflict.t
54 test-confused-revert.t
54 test-confused-revert.t
55 test-contrib-check-code.t
55 test-contrib-check-code.t
56 test-contrib-check-commit.t
56 test-contrib-check-commit.t
57 test-convert-authormap.t
57 test-convert-authormap.t
58 test-convert-clonebranches.t
58 test-convert-clonebranches.t
59 test-convert-datesort.t
59 test-convert-datesort.t
60 test-convert-filemap.t
60 test-convert-filemap.t
61 test-convert-hg-sink.t
61 test-convert-hg-sink.t
62 test-convert-hg-source.t
62 test-convert-hg-source.t
63 test-convert-hg-startrev.t
63 test-convert-hg-startrev.t
64 test-copy-move-merge.t
64 test-copy-move-merge.t
65 test-copy.t
65 test-copy.t
66 test-copytrace-heuristics.t
66 test-copytrace-heuristics.t
67 test-debugbuilddag.t
67 test-debugbuilddag.t
68 test-debugbundle.t
68 test-debugbundle.t
69 test-debugextensions.t
69 test-debugextensions.t
70 test-debugindexdot.t
70 test-debugindexdot.t
71 test-debugrename.t
71 test-debugrename.t
72 test-default-push.t
72 test-diff-binary-file.t
73 test-diff-binary-file.t
73 test-diff-change.t
74 test-diff-change.t
74 test-diff-copy-depth.t
75 test-diff-copy-depth.t
75 test-diff-hashes.t
76 test-diff-hashes.t
76 test-diff-issue2761.t
77 test-diff-issue2761.t
77 test-diff-newlines.t
78 test-diff-newlines.t
78 test-diff-reverse.t
79 test-diff-reverse.t
79 test-diff-subdir.t
80 test-diff-subdir.t
80 test-diffdir.t
81 test-diffdir.t
81 test-directaccess.t
82 test-directaccess.t
82 test-dirstate-backup.t
83 test-dirstate-backup.t
83 test-dirstate-nonnormalset.t
84 test-dirstate-nonnormalset.t
84 test-doctest.py
85 test-doctest.py
85 test-double-merge.t
86 test-double-merge.t
86 test-drawdag.t
87 test-drawdag.t
87 test-duplicateoptions.py
88 test-duplicateoptions.py
88 test-empty-dir.t
89 test-empty-dir.t
89 test-empty-file.t
90 test-empty-file.t
90 test-empty-group.t
91 test-empty-group.t
91 test-empty.t
92 test-empty.t
92 test-encode.t
93 test-encode.t
93 test-encoding-func.py
94 test-encoding-func.py
94 test-encoding.t
95 test-encoding.t
95 test-eol-add.t
96 test-eol-add.t
96 test-eol-clone.t
97 test-eol-clone.t
97 test-eol-hook.t
98 test-eol-hook.t
98 test-eol-tag.t
99 test-eol-tag.t
99 test-eol-update.t
100 test-eol-update.t
100 test-excessive-merge.t
101 test-excessive-merge.t
101 test-exchange-obsmarkers-case-A1.t
102 test-exchange-obsmarkers-case-A1.t
102 test-exchange-obsmarkers-case-A2.t
103 test-exchange-obsmarkers-case-A2.t
103 test-exchange-obsmarkers-case-A3.t
104 test-exchange-obsmarkers-case-A3.t
104 test-exchange-obsmarkers-case-A4.t
105 test-exchange-obsmarkers-case-A4.t
105 test-exchange-obsmarkers-case-A5.t
106 test-exchange-obsmarkers-case-A5.t
106 test-exchange-obsmarkers-case-A6.t
107 test-exchange-obsmarkers-case-A6.t
107 test-exchange-obsmarkers-case-A7.t
108 test-exchange-obsmarkers-case-A7.t
108 test-exchange-obsmarkers-case-B1.t
109 test-exchange-obsmarkers-case-B1.t
109 test-exchange-obsmarkers-case-B2.t
110 test-exchange-obsmarkers-case-B2.t
110 test-exchange-obsmarkers-case-B3.t
111 test-exchange-obsmarkers-case-B3.t
111 test-exchange-obsmarkers-case-B4.t
112 test-exchange-obsmarkers-case-B4.t
112 test-exchange-obsmarkers-case-B5.t
113 test-exchange-obsmarkers-case-B5.t
113 test-exchange-obsmarkers-case-B6.t
114 test-exchange-obsmarkers-case-B6.t
114 test-exchange-obsmarkers-case-B7.t
115 test-exchange-obsmarkers-case-B7.t
115 test-exchange-obsmarkers-case-C1.t
116 test-exchange-obsmarkers-case-C1.t
116 test-exchange-obsmarkers-case-C2.t
117 test-exchange-obsmarkers-case-C2.t
117 test-exchange-obsmarkers-case-C3.t
118 test-exchange-obsmarkers-case-C3.t
118 test-exchange-obsmarkers-case-C4.t
119 test-exchange-obsmarkers-case-C4.t
119 test-exchange-obsmarkers-case-D1.t
120 test-exchange-obsmarkers-case-D1.t
120 test-exchange-obsmarkers-case-D2.t
121 test-exchange-obsmarkers-case-D2.t
121 test-exchange-obsmarkers-case-D3.t
122 test-exchange-obsmarkers-case-D3.t
122 test-exchange-obsmarkers-case-D4.t
123 test-exchange-obsmarkers-case-D4.t
123 test-execute-bit.t
124 test-execute-bit.t
124 test-extdiff.t
125 test-extdiff.t
125 test-extra-filelog-entry.t
126 test-extra-filelog-entry.t
126 test-filebranch.t
127 test-filebranch.t
127 test-fileset-generated.t
128 test-fileset-generated.t
128 test-flags.t
129 test-flags.t
129 test-generaldelta.t
130 test-generaldelta.t
130 test-getbundle.t
131 test-getbundle.t
131 test-git-export.t
132 test-git-export.t
132 test-glog-topological.t
133 test-glog-topological.t
133 test-gpg.t
134 test-gpg.t
134 test-graft.t
135 test-graft.t
135 test-hghave.t
136 test-hghave.t
136 test-hgignore.t
137 test-hgignore.t
137 test-hgk.t
138 test-hgk.t
138 test-hgweb-bundle.t
139 test-hgweb-bundle.t
139 test-hgweb-descend-empties.t
140 test-hgweb-descend-empties.t
140 test-hgweb-removed.t
141 test-hgweb-removed.t
141 test-histedit-arguments.t
142 test-histedit-arguments.t
142 test-histedit-base.t
143 test-histedit-base.t
143 test-histedit-bookmark-motion.t
144 test-histedit-bookmark-motion.t
144 test-histedit-commute.t
145 test-histedit-commute.t
145 test-histedit-drop.t
146 test-histedit-drop.t
146 test-histedit-edit.t
147 test-histedit-edit.t
147 test-histedit-fold-non-commute.t
148 test-histedit-fold-non-commute.t
148 test-histedit-fold.t
149 test-histedit-fold.t
149 test-histedit-no-change.t
150 test-histedit-no-change.t
150 test-histedit-non-commute-abort.t
151 test-histedit-non-commute-abort.t
151 test-histedit-non-commute.t
152 test-histedit-non-commute.t
152 test-histedit-obsolete.t
153 test-histedit-obsolete.t
153 test-histedit-outgoing.t
154 test-histedit-outgoing.t
154 test-histedit-templates.t
155 test-histedit-templates.t
155 test-http-branchmap.t
156 test-http-branchmap.t
156 test-http-bundle1.t
157 test-http-bundle1.t
157 test-http-clone-r.t
158 test-http-clone-r.t
158 test-identify.t
159 test-identify.t
159 test-import-unknown.t
160 test-import-unknown.t
160 test-imports-checker.t
161 test-imports-checker.t
161 test-inherit-mode.t
162 test-inherit-mode.t
162 test-issue1089.t
163 test-issue1089.t
163 test-issue1102.t
164 test-issue1102.t
164 test-issue1175.t
165 test-issue1175.t
165 test-issue1306.t
166 test-issue1306.t
166 test-issue1438.t
167 test-issue1438.t
167 test-issue1502.t
168 test-issue1502.t
168 test-issue1802.t
169 test-issue1802.t
169 test-issue1877.t
170 test-issue1877.t
170 test-issue1993.t
171 test-issue1993.t
171 test-issue2137.t
172 test-issue2137.t
172 test-issue3084.t
173 test-issue3084.t
173 test-issue4074.t
174 test-issue4074.t
174 test-issue522.t
175 test-issue522.t
175 test-issue586.t
176 test-issue586.t
176 test-issue612.t
177 test-issue612.t
177 test-issue619.t
178 test-issue619.t
178 test-issue672.t
179 test-issue672.t
179 test-issue842.t
180 test-issue842.t
180 test-journal-exists.t
181 test-journal-exists.t
181 test-largefiles-cache.t
182 test-largefiles-cache.t
182 test-largefiles-misc.t
183 test-largefiles-misc.t
183 test-largefiles-small-disk.t
184 test-largefiles-small-disk.t
184 test-locate.t
185 test-locate.t
185 test-lock-badness.t
186 test-lock-badness.t
186 test-log.t
187 test-log.t
187 test-logexchange.t
188 test-logexchange.t
188 test-lrucachedict.py
189 test-lrucachedict.py
189 test-mactext.t
190 test-mactext.t
190 test-manifest-merging.t
191 test-manifest-merging.t
191 test-manifest.py
192 test-manifest.py
192 test-manifest.t
193 test-manifest.t
193 test-match.py
194 test-match.py
194 test-mdiff.py
195 test-mdiff.py
195 test-merge-closedheads.t
196 test-merge-closedheads.t
196 test-merge-commit.t
197 test-merge-commit.t
197 test-merge-criss-cross.t
198 test-merge-criss-cross.t
198 test-merge-default.t
199 test-merge-default.t
199 test-merge-internal-tools-pattern.t
200 test-merge-internal-tools-pattern.t
200 test-merge-local.t
201 test-merge-local.t
201 test-merge-remove.t
202 test-merge-remove.t
202 test-merge-revert.t
203 test-merge-revert.t
203 test-merge-revert2.t
204 test-merge-revert2.t
204 test-merge-subrepos.t
205 test-merge-subrepos.t
205 test-merge-symlinks.t
206 test-merge-symlinks.t
206 test-merge-types.t
207 test-merge-types.t
207 test-merge1.t
208 test-merge1.t
208 test-merge10.t
209 test-merge10.t
209 test-merge2.t
210 test-merge2.t
210 test-merge4.t
211 test-merge4.t
211 test-merge5.t
212 test-merge5.t
212 test-merge6.t
213 test-merge6.t
213 test-merge7.t
214 test-merge7.t
214 test-merge8.t
215 test-merge8.t
215 test-merge9.t
216 test-merge9.t
216 test-mq-git.t
217 test-mq-git.t
217 test-mq-header-date.t
218 test-mq-header-date.t
218 test-mq-header-from.t
219 test-mq-header-from.t
219 test-mq-pull-from-bundle.t
220 test-mq-pull-from-bundle.t
220 test-mq-qdiff.t
221 test-mq-qdiff.t
221 test-mq-qfold.t
222 test-mq-qfold.t
222 test-mq-qgoto.t
223 test-mq-qgoto.t
223 test-mq-qimport-fail-cleanup.t
224 test-mq-qimport-fail-cleanup.t
224 test-mq-qpush-exact.t
225 test-mq-qpush-exact.t
225 test-mq-qqueue.t
226 test-mq-qqueue.t
226 test-mq-qrefresh-replace-log-message.t
227 test-mq-qrefresh-replace-log-message.t
227 test-mq-qrefresh.t
228 test-mq-qrefresh.t
228 test-mq-qrename.t
229 test-mq-qrename.t
229 test-mq-qsave.t
230 test-mq-qsave.t
230 test-mq-safety.t
231 test-mq-safety.t
231 test-mq-symlinks.t
232 test-mq-symlinks.t
232 test-mv-cp-st-diff.t
233 test-mv-cp-st-diff.t
233 test-narrow-archive.t
234 test-narrow-archive.t
234 test-narrow-clone-no-ellipsis.t
235 test-narrow-clone-no-ellipsis.t
235 test-narrow-clone-nonlinear.t
236 test-narrow-clone-nonlinear.t
236 test-narrow-clone.t
237 test-narrow-clone.t
237 test-narrow-commit.t
238 test-narrow-commit.t
238 test-narrow-copies.t
239 test-narrow-copies.t
239 test-narrow-debugcommands.t
240 test-narrow-debugcommands.t
240 test-narrow-debugrebuilddirstate.t
241 test-narrow-debugrebuilddirstate.t
241 test-narrow-exchange-merges.t
242 test-narrow-exchange-merges.t
242 test-narrow-exchange.t
243 test-narrow-exchange.t
243 test-narrow-expanddirstate.t
244 test-narrow-expanddirstate.t
244 test-narrow-merge.t
245 test-narrow-merge.t
245 test-narrow-patch.t
246 test-narrow-patch.t
246 test-narrow-patterns.t
247 test-narrow-patterns.t
247 test-narrow-pull.t
248 test-narrow-pull.t
248 test-narrow-rebase.t
249 test-narrow-rebase.t
249 test-narrow-shallow-merges.t
250 test-narrow-shallow-merges.t
250 test-narrow-shallow.t
251 test-narrow-shallow.t
251 test-narrow-strip.t
252 test-narrow-strip.t
252 test-narrow-update.t
253 test-narrow-update.t
253 test-nested-repo.t
254 test-nested-repo.t
254 test-newbranch.t
255 test-newbranch.t
255 test-obshistory.t
256 test-obshistory.t
256 test-obsmarker-template.t
257 test-obsmarker-template.t
257 test-obsmarkers-effectflag.t
258 test-obsmarkers-effectflag.t
258 test-obsolete-bundle-strip.t
259 test-obsolete-bundle-strip.t
259 test-obsolete-changeset-exchange.t
260 test-obsolete-changeset-exchange.t
260 test-obsolete-checkheads.t
261 test-obsolete-checkheads.t
261 test-obsolete-distributed.t
262 test-obsolete-distributed.t
262 test-obsolete-tag-cache.t
263 test-obsolete-tag-cache.t
263 test-parents.t
264 test-parents.t
264 test-pathconflicts-merge.t
265 test-pathconflicts-merge.t
265 test-pathconflicts-update.t
266 test-pathconflicts-update.t
266 test-pending.t
267 test-pending.t
267 test-permissions.t
268 test-permissions.t
268 test-phases.t
269 test-phases.t
269 test-pull-branch.t
270 test-pull-branch.t
270 test-pull-http.t
271 test-pull-http.t
271 test-pull-permission.t
272 test-pull-permission.t
272 test-pull-pull-corruption.t
273 test-pull-pull-corruption.t
273 test-pull-r.t
274 test-pull-r.t
274 test-pull-update.t
275 test-pull-update.t
275 test-purge.t
276 test-purge.t
276 test-push-checkheads-partial-C1.t
277 test-push-checkheads-partial-C1.t
277 test-push-checkheads-partial-C2.t
278 test-push-checkheads-partial-C2.t
278 test-push-checkheads-partial-C3.t
279 test-push-checkheads-partial-C3.t
279 test-push-checkheads-partial-C4.t
280 test-push-checkheads-partial-C4.t
280 test-push-checkheads-pruned-B1.t
281 test-push-checkheads-pruned-B1.t
281 test-push-checkheads-pruned-B2.t
282 test-push-checkheads-pruned-B2.t
282 test-push-checkheads-pruned-B3.t
283 test-push-checkheads-pruned-B3.t
283 test-push-checkheads-pruned-B4.t
284 test-push-checkheads-pruned-B4.t
284 test-push-checkheads-pruned-B5.t
285 test-push-checkheads-pruned-B5.t
285 test-push-checkheads-pruned-B6.t
286 test-push-checkheads-pruned-B6.t
286 test-push-checkheads-pruned-B7.t
287 test-push-checkheads-pruned-B7.t
287 test-push-checkheads-pruned-B8.t
288 test-push-checkheads-pruned-B8.t
288 test-push-checkheads-superceed-A1.t
289 test-push-checkheads-superceed-A1.t
289 test-push-checkheads-superceed-A2.t
290 test-push-checkheads-superceed-A2.t
290 test-push-checkheads-superceed-A3.t
291 test-push-checkheads-superceed-A3.t
291 test-push-checkheads-superceed-A4.t
292 test-push-checkheads-superceed-A4.t
292 test-push-checkheads-superceed-A5.t
293 test-push-checkheads-superceed-A5.t
293 test-push-checkheads-superceed-A6.t
294 test-push-checkheads-superceed-A6.t
294 test-push-checkheads-superceed-A7.t
295 test-push-checkheads-superceed-A7.t
295 test-push-checkheads-superceed-A8.t
296 test-push-checkheads-superceed-A8.t
296 test-push-checkheads-unpushed-D1.t
297 test-push-checkheads-unpushed-D1.t
297 test-push-checkheads-unpushed-D2.t
298 test-push-checkheads-unpushed-D2.t
298 test-push-checkheads-unpushed-D3.t
299 test-push-checkheads-unpushed-D3.t
299 test-push-checkheads-unpushed-D4.t
300 test-push-checkheads-unpushed-D4.t
300 test-push-checkheads-unpushed-D5.t
301 test-push-checkheads-unpushed-D5.t
301 test-push-checkheads-unpushed-D6.t
302 test-push-checkheads-unpushed-D6.t
302 test-push-checkheads-unpushed-D7.t
303 test-push-checkheads-unpushed-D7.t
303 test-push-http.t
304 test-push-http.t
304 test-push-warn.t
305 test-push-warn.t
305 test-pushvars.t
306 test-pushvars.t
306 test-rebase-abort.t
307 test-rebase-abort.t
307 test-rebase-base-flag.t
308 test-rebase-base-flag.t
308 test-rebase-bookmarks.t
309 test-rebase-bookmarks.t
309 test-rebase-brute-force.t
310 test-rebase-brute-force.t
310 test-rebase-cache.t
311 test-rebase-cache.t
311 test-rebase-check-restore.t
312 test-rebase-check-restore.t
312 test-rebase-collapse.t
313 test-rebase-collapse.t
313 test-rebase-dest.t
314 test-rebase-dest.t
314 test-rebase-detach.t
315 test-rebase-detach.t
315 test-rebase-emptycommit.t
316 test-rebase-emptycommit.t
316 test-rebase-inmemory.t
317 test-rebase-inmemory.t
317 test-rebase-interruptions.t
318 test-rebase-interruptions.t
318 test-rebase-issue-noparam-single-rev.t
319 test-rebase-issue-noparam-single-rev.t
319 test-rebase-legacy.t
320 test-rebase-legacy.t
320 test-rebase-mq-skip.t
321 test-rebase-mq-skip.t
321 test-rebase-named-branches.t
322 test-rebase-named-branches.t
322 test-rebase-newancestor.t
323 test-rebase-newancestor.t
323 test-rebase-obsolete.t
324 test-rebase-obsolete.t
324 test-rebase-parameters.t
325 test-rebase-parameters.t
325 test-rebase-partial.t
326 test-rebase-partial.t
326 test-rebase-pull.t
327 test-rebase-pull.t
327 test-rebase-rename.t
328 test-rebase-rename.t
328 test-rebase-scenario-global.t
329 test-rebase-scenario-global.t
329 test-rebase-templates.t
330 test-rebase-templates.t
330 test-rebase-transaction.t
331 test-rebase-transaction.t
331 test-record.t
332 test-record.t
332 test-relink.t
333 test-relink.t
333 test-remove.t
334 test-remove.t
334 test-rename-after-merge.t
335 test-rename-after-merge.t
335 test-rename-dir-merge.t
336 test-rename-dir-merge.t
336 test-rename-merge1.t
337 test-rename-merge1.t
337 test-rename.t
338 test-rename.t
338 test-repair-strip.t
339 test-repair-strip.t
339 test-repo-compengines.t
340 test-repo-compengines.t
340 test-resolve.t
341 test-resolve.t
341 test-revert-flags.t
342 test-revert-flags.t
342 test-revert-unknown.t
343 test-revert-unknown.t
343 test-revlog-ancestry.py
344 test-revlog-ancestry.py
344 test-revlog-group-emptyiter.t
345 test-revlog-group-emptyiter.t
345 test-revlog-mmapindex.t
346 test-revlog-mmapindex.t
346 test-revlog-packentry.t
347 test-revlog-packentry.t
347 test-revset-dirstate-parents.t
348 test-revset-dirstate-parents.t
348 test-revset-outgoing.t
349 test-revset-outgoing.t
349 test-run-tests.py
350 test-run-tests.py
350 test-schemes.t
351 test-schemes.t
351 test-serve.t
352 test-serve.t
352 test-share.t
353 test-share.t
353 test-show-stack.t
354 test-show-stack.t
354 test-show-work.t
355 test-show-work.t
355 test-show.t
356 test-show.t
356 test-simple-update.t
357 test-simple-update.t
357 test-single-head.t
358 test-single-head.t
358 test-sparse-clear.t
359 test-sparse-clear.t
359 test-sparse-merges.t
360 test-sparse-merges.t
360 test-sparse-requirement.t
361 test-sparse-requirement.t
361 test-sparse-verbose-json.t
362 test-sparse-verbose-json.t
362 test-ssh-clone-r.t
363 test-ssh-clone-r.t
363 test-ssh-proto.t
364 test-ssh-proto.t
364 test-sshserver.py
365 test-sshserver.py
365 test-status-rev.t
366 test-status-rev.t
366 test-status-terse.t
367 test-status-terse.t
367 test-strip-cross.t
368 test-strip-cross.t
368 test-strip.t
369 test-strip.t
369 test-subrepo-deep-nested-change.t
370 test-subrepo-deep-nested-change.t
370 test-subrepo.t
371 test-subrepo.t
371 test-symlinks.t
372 test-symlinks.t
372 test-treemanifest.t
373 test-treemanifest.t
373 test-unamend.t
374 test-unamend.t
374 test-uncommit.t
375 test-uncommit.t
375 test-unified-test.t
376 test-unified-test.t
376 test-unrelated-pull.t
377 test-unrelated-pull.t
377 test-up-local-change.t
378 test-up-local-change.t
378 test-update-branches.t
379 test-update-branches.t
379 test-update-dest.t
380 test-update-dest.t
380 test-update-issue1456.t
381 test-update-issue1456.t
381 test-update-names.t
382 test-update-names.t
382 test-update-reverse.t
383 test-update-reverse.t
383 test-upgrade-repo.t
384 test-upgrade-repo.t
384 test-url-rev.t
385 test-url-rev.t
385 test-username-newline.t
386 test-username-newline.t
386 test-verify.t
387 test-verify.t
387 test-websub.t
388 test-websub.t
388 test-win32text.t
389 test-win32text.t
389 test-xdg.t
390 test-xdg.t
@@ -1,1850 +1,1850 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 = newitems.items()
738 items = newitems.items()
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(), socket.getfqdn())
804 self.warn(_("no username found, using '%s' instead\n") % user)
804 self.warn(_("no username found, using '%s' instead\n") % user)
805 except KeyError:
805 except KeyError:
806 pass
806 pass
807 if not user:
807 if not user:
808 raise error.Abort(_('no username supplied'),
808 raise error.Abort(_('no username supplied'),
809 hint=_("use 'hg config --edit' "
809 hint=_("use 'hg config --edit' "
810 'to set your username'))
810 'to set your username'))
811 if "\n" in user:
811 if "\n" in user:
812 raise error.Abort(_("username %r contains a newline\n")
812 raise error.Abort(_("username %r contains a newline\n")
813 % pycompat.bytestr(user))
813 % pycompat.bytestr(user))
814 return user
814 return user
815
815
816 def shortuser(self, user):
816 def shortuser(self, user):
817 """Return a short representation of a user name or email address."""
817 """Return a short representation of a user name or email address."""
818 if not self.verbose:
818 if not self.verbose:
819 user = util.shortuser(user)
819 user = util.shortuser(user)
820 return user
820 return user
821
821
822 def expandpath(self, loc, default=None):
822 def expandpath(self, loc, default=None):
823 """Return repository location relative to cwd or from [paths]"""
823 """Return repository location relative to cwd or from [paths]"""
824 try:
824 try:
825 p = self.paths.getpath(loc)
825 p = self.paths.getpath(loc)
826 if p:
826 if p:
827 return p.rawloc
827 return p.rawloc
828 except error.RepoError:
828 except error.RepoError:
829 pass
829 pass
830
830
831 if default:
831 if default:
832 try:
832 try:
833 p = self.paths.getpath(default)
833 p = self.paths.getpath(default)
834 if p:
834 if p:
835 return p.rawloc
835 return p.rawloc
836 except error.RepoError:
836 except error.RepoError:
837 pass
837 pass
838
838
839 return loc
839 return loc
840
840
841 @util.propertycache
841 @util.propertycache
842 def paths(self):
842 def paths(self):
843 return paths(self)
843 return paths(self)
844
844
845 def pushbuffer(self, error=False, subproc=False, labeled=False):
845 def pushbuffer(self, error=False, subproc=False, labeled=False):
846 """install a buffer to capture standard output of the ui object
846 """install a buffer to capture standard output of the ui object
847
847
848 If error is True, the error output will be captured too.
848 If error is True, the error output will be captured too.
849
849
850 If subproc is True, output from subprocesses (typically hooks) will be
850 If subproc is True, output from subprocesses (typically hooks) will be
851 captured too.
851 captured too.
852
852
853 If labeled is True, any labels associated with buffered
853 If labeled is True, any labels associated with buffered
854 output will be handled. By default, this has no effect
854 output will be handled. By default, this has no effect
855 on the output returned, but extensions and GUI tools may
855 on the output returned, but extensions and GUI tools may
856 handle this argument and returned styled output. If output
856 handle this argument and returned styled output. If output
857 is being buffered so it can be captured and parsed or
857 is being buffered so it can be captured and parsed or
858 processed, labeled should not be set to True.
858 processed, labeled should not be set to True.
859 """
859 """
860 self._buffers.append([])
860 self._buffers.append([])
861 self._bufferstates.append((error, subproc, labeled))
861 self._bufferstates.append((error, subproc, labeled))
862 self._bufferapplylabels = labeled
862 self._bufferapplylabels = labeled
863
863
864 def popbuffer(self):
864 def popbuffer(self):
865 '''pop the last buffer and return the buffered output'''
865 '''pop the last buffer and return the buffered output'''
866 self._bufferstates.pop()
866 self._bufferstates.pop()
867 if self._bufferstates:
867 if self._bufferstates:
868 self._bufferapplylabels = self._bufferstates[-1][2]
868 self._bufferapplylabels = self._bufferstates[-1][2]
869 else:
869 else:
870 self._bufferapplylabels = None
870 self._bufferapplylabels = None
871
871
872 return "".join(self._buffers.pop())
872 return "".join(self._buffers.pop())
873
873
874 def canwritewithoutlabels(self):
874 def canwritewithoutlabels(self):
875 '''check if write skips the label'''
875 '''check if write skips the label'''
876 if self._buffers and not self._bufferapplylabels:
876 if self._buffers and not self._bufferapplylabels:
877 return True
877 return True
878 return self._colormode is None
878 return self._colormode is None
879
879
880 def canbatchlabeledwrites(self):
880 def canbatchlabeledwrites(self):
881 '''check if write calls with labels are batchable'''
881 '''check if write calls with labels are batchable'''
882 # Windows color printing is special, see ``write``.
882 # Windows color printing is special, see ``write``.
883 return self._colormode != 'win32'
883 return self._colormode != 'win32'
884
884
885 def write(self, *args, **opts):
885 def write(self, *args, **opts):
886 '''write args to output
886 '''write args to output
887
887
888 By default, this method simply writes to the buffer or stdout.
888 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
889 Color mode can be set on the UI class to have the output decorated
890 with color modifier before being written to stdout.
890 with color modifier before being written to stdout.
891
891
892 The color used is controlled by an optional keyword argument, "label".
892 The color used is controlled by an optional keyword argument, "label".
893 This should be a string containing label names separated by space.
893 This should be a string containing label names separated by space.
894 Label names take the form of "topic.type". For example, ui.debug()
894 Label names take the form of "topic.type". For example, ui.debug()
895 issues a label of "ui.debug".
895 issues a label of "ui.debug".
896
896
897 When labeling output for a specific command, a label of
897 When labeling output for a specific command, a label of
898 "cmdname.type" is recommended. For example, status issues
898 "cmdname.type" is recommended. For example, status issues
899 a label of "status.modified" for modified files.
899 a label of "status.modified" for modified files.
900 '''
900 '''
901 if self._buffers:
901 if self._buffers:
902 if self._bufferapplylabels:
902 if self._bufferapplylabels:
903 label = opts.get(r'label', '')
903 label = opts.get(r'label', '')
904 self._buffers[-1].extend(self.label(a, label) for a in args)
904 self._buffers[-1].extend(self.label(a, label) for a in args)
905 else:
905 else:
906 self._buffers[-1].extend(args)
906 self._buffers[-1].extend(args)
907 else:
907 else:
908 self._writenobuf(*args, **opts)
908 self._writenobuf(*args, **opts)
909
909
910 def _writenobuf(self, *args, **opts):
910 def _writenobuf(self, *args, **opts):
911 if self._colormode == 'win32':
911 if self._colormode == 'win32':
912 # windows color printing is its own can of crab, defer to
912 # windows color printing is its own can of crab, defer to
913 # the color module and that is it.
913 # the color module and that is it.
914 color.win32print(self, self._write, *args, **opts)
914 color.win32print(self, self._write, *args, **opts)
915 else:
915 else:
916 msgs = args
916 msgs = args
917 if self._colormode is not None:
917 if self._colormode is not None:
918 label = opts.get(r'label', '')
918 label = opts.get(r'label', '')
919 msgs = [self.label(a, label) for a in args]
919 msgs = [self.label(a, label) for a in args]
920 self._write(*msgs, **opts)
920 self._write(*msgs, **opts)
921
921
922 def _write(self, *msgs, **opts):
922 def _write(self, *msgs, **opts):
923 self._progclear()
923 self._progclear()
924 # opencode timeblockedsection because this is a critical path
924 # opencode timeblockedsection because this is a critical path
925 starttime = util.timer()
925 starttime = util.timer()
926 try:
926 try:
927 self.fout.write(''.join(msgs))
927 self.fout.write(''.join(msgs))
928 except IOError as err:
928 except IOError as err:
929 raise error.StdioError(err)
929 raise error.StdioError(err)
930 finally:
930 finally:
931 self._blockedtimes['stdio_blocked'] += \
931 self._blockedtimes['stdio_blocked'] += \
932 (util.timer() - starttime) * 1000
932 (util.timer() - starttime) * 1000
933
933
934 def write_err(self, *args, **opts):
934 def write_err(self, *args, **opts):
935 self._progclear()
935 self._progclear()
936 if self._bufferstates and self._bufferstates[-1][0]:
936 if self._bufferstates and self._bufferstates[-1][0]:
937 self.write(*args, **opts)
937 self.write(*args, **opts)
938 elif self._colormode == 'win32':
938 elif self._colormode == 'win32':
939 # windows color printing is its own can of crab, defer to
939 # windows color printing is its own can of crab, defer to
940 # the color module and that is it.
940 # the color module and that is it.
941 color.win32print(self, self._write_err, *args, **opts)
941 color.win32print(self, self._write_err, *args, **opts)
942 else:
942 else:
943 msgs = args
943 msgs = args
944 if self._colormode is not None:
944 if self._colormode is not None:
945 label = opts.get(r'label', '')
945 label = opts.get(r'label', '')
946 msgs = [self.label(a, label) for a in args]
946 msgs = [self.label(a, label) for a in args]
947 self._write_err(*msgs, **opts)
947 self._write_err(*msgs, **opts)
948
948
949 def _write_err(self, *msgs, **opts):
949 def _write_err(self, *msgs, **opts):
950 try:
950 try:
951 with self.timeblockedsection('stdio'):
951 with self.timeblockedsection('stdio'):
952 if not getattr(self.fout, 'closed', False):
952 if not getattr(self.fout, 'closed', False):
953 self.fout.flush()
953 self.fout.flush()
954 for a in msgs:
954 for a in msgs:
955 self.ferr.write(a)
955 self.ferr.write(a)
956 # stderr may be buffered under win32 when redirected to files,
956 # stderr may be buffered under win32 when redirected to files,
957 # including stdout.
957 # including stdout.
958 if not getattr(self.ferr, 'closed', False):
958 if not getattr(self.ferr, 'closed', False):
959 self.ferr.flush()
959 self.ferr.flush()
960 except IOError as inst:
960 except IOError as inst:
961 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
961 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
962 raise error.StdioError(inst)
962 raise error.StdioError(inst)
963
963
964 def flush(self):
964 def flush(self):
965 # opencode timeblockedsection because this is a critical path
965 # opencode timeblockedsection because this is a critical path
966 starttime = util.timer()
966 starttime = util.timer()
967 try:
967 try:
968 try:
968 try:
969 self.fout.flush()
969 self.fout.flush()
970 except IOError as err:
970 except IOError as err:
971 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
971 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
972 raise error.StdioError(err)
972 raise error.StdioError(err)
973 finally:
973 finally:
974 try:
974 try:
975 self.ferr.flush()
975 self.ferr.flush()
976 except IOError as err:
976 except IOError as err:
977 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
977 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
978 raise error.StdioError(err)
978 raise error.StdioError(err)
979 finally:
979 finally:
980 self._blockedtimes['stdio_blocked'] += \
980 self._blockedtimes['stdio_blocked'] += \
981 (util.timer() - starttime) * 1000
981 (util.timer() - starttime) * 1000
982
982
983 def _isatty(self, fh):
983 def _isatty(self, fh):
984 if self.configbool('ui', 'nontty'):
984 if self.configbool('ui', 'nontty'):
985 return False
985 return False
986 return util.isatty(fh)
986 return util.isatty(fh)
987
987
988 def disablepager(self):
988 def disablepager(self):
989 self._disablepager = True
989 self._disablepager = True
990
990
991 def pager(self, command):
991 def pager(self, command):
992 """Start a pager for subsequent command output.
992 """Start a pager for subsequent command output.
993
993
994 Commands which produce a long stream of output should call
994 Commands which produce a long stream of output should call
995 this function to activate the user's preferred pagination
995 this function to activate the user's preferred pagination
996 mechanism (which may be no pager). Calling this function
996 mechanism (which may be no pager). Calling this function
997 precludes any future use of interactive functionality, such as
997 precludes any future use of interactive functionality, such as
998 prompting the user or activating curses.
998 prompting the user or activating curses.
999
999
1000 Args:
1000 Args:
1001 command: The full, non-aliased name of the command. That is, "log"
1001 command: The full, non-aliased name of the command. That is, "log"
1002 not "history, "summary" not "summ", etc.
1002 not "history, "summary" not "summ", etc.
1003 """
1003 """
1004 if (self._disablepager
1004 if (self._disablepager
1005 or self.pageractive):
1005 or self.pageractive):
1006 # how pager should do is already determined
1006 # how pager should do is already determined
1007 return
1007 return
1008
1008
1009 if not command.startswith('internal-always-') and (
1009 if not command.startswith('internal-always-') and (
1010 # explicit --pager=on (= 'internal-always-' prefix) should
1010 # explicit --pager=on (= 'internal-always-' prefix) should
1011 # take precedence over disabling factors below
1011 # take precedence over disabling factors below
1012 command in self.configlist('pager', 'ignore')
1012 command in self.configlist('pager', 'ignore')
1013 or not self.configbool('ui', 'paginate')
1013 or not self.configbool('ui', 'paginate')
1014 or not self.configbool('pager', 'attend-' + command, True)
1014 or not self.configbool('pager', 'attend-' + command, True)
1015 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1015 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1016 # formatted() will need some adjustment.
1016 # formatted() will need some adjustment.
1017 or not self.formatted()
1017 or not self.formatted()
1018 or self.plain()
1018 or self.plain()
1019 or self._buffers
1019 or self._buffers
1020 # TODO: expose debugger-enabled on the UI object
1020 # TODO: expose debugger-enabled on the UI object
1021 or '--debugger' in pycompat.sysargv):
1021 or '--debugger' in pycompat.sysargv):
1022 # We only want to paginate if the ui appears to be
1022 # We only want to paginate if the ui appears to be
1023 # interactive, the user didn't say HGPLAIN or
1023 # interactive, the user didn't say HGPLAIN or
1024 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1024 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1025 return
1025 return
1026
1026
1027 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1027 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1028 if not pagercmd:
1028 if not pagercmd:
1029 return
1029 return
1030
1030
1031 pagerenv = {}
1031 pagerenv = {}
1032 for name, value in rcutil.defaultpagerenv().items():
1032 for name, value in rcutil.defaultpagerenv().items():
1033 if name not in encoding.environ:
1033 if name not in encoding.environ:
1034 pagerenv[name] = value
1034 pagerenv[name] = value
1035
1035
1036 self.debug('starting pager for command %r\n' % command)
1036 self.debug('starting pager for command %r\n' % command)
1037 self.flush()
1037 self.flush()
1038
1038
1039 wasformatted = self.formatted()
1039 wasformatted = self.formatted()
1040 if util.safehasattr(signal, "SIGPIPE"):
1040 if util.safehasattr(signal, "SIGPIPE"):
1041 signal.signal(signal.SIGPIPE, _catchterm)
1041 signal.signal(signal.SIGPIPE, _catchterm)
1042 if self._runpager(pagercmd, pagerenv):
1042 if self._runpager(pagercmd, pagerenv):
1043 self.pageractive = True
1043 self.pageractive = True
1044 # Preserve the formatted-ness of the UI. This is important
1044 # Preserve the formatted-ness of the UI. This is important
1045 # because we mess with stdout, which might confuse
1045 # because we mess with stdout, which might confuse
1046 # auto-detection of things being formatted.
1046 # auto-detection of things being formatted.
1047 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1047 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1048 self.setconfig('ui', 'interactive', False, 'pager')
1048 self.setconfig('ui', 'interactive', False, 'pager')
1049
1049
1050 # If pagermode differs from color.mode, reconfigure color now that
1050 # If pagermode differs from color.mode, reconfigure color now that
1051 # pageractive is set.
1051 # pageractive is set.
1052 cm = self._colormode
1052 cm = self._colormode
1053 if cm != self.config('color', 'pagermode', cm):
1053 if cm != self.config('color', 'pagermode', cm):
1054 color.setup(self)
1054 color.setup(self)
1055 else:
1055 else:
1056 # If the pager can't be spawned in dispatch when --pager=on is
1056 # 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
1057 # given, don't try again when the command runs, to avoid a duplicate
1058 # warning about a missing pager command.
1058 # warning about a missing pager command.
1059 self.disablepager()
1059 self.disablepager()
1060
1060
1061 def _runpager(self, command, env=None):
1061 def _runpager(self, command, env=None):
1062 """Actually start the pager and set up file descriptors.
1062 """Actually start the pager and set up file descriptors.
1063
1063
1064 This is separate in part so that extensions (like chg) can
1064 This is separate in part so that extensions (like chg) can
1065 override how a pager is invoked.
1065 override how a pager is invoked.
1066 """
1066 """
1067 if command == 'cat':
1067 if command == 'cat':
1068 # Save ourselves some work.
1068 # Save ourselves some work.
1069 return False
1069 return False
1070 # If the command doesn't contain any of these characters, we
1070 # If the command doesn't contain any of these characters, we
1071 # assume it's a binary and exec it directly. This means for
1071 # assume it's a binary and exec it directly. This means for
1072 # simple pager command configurations, we can degrade
1072 # simple pager command configurations, we can degrade
1073 # gracefully and tell the user about their broken pager.
1073 # gracefully and tell the user about their broken pager.
1074 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1074 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1075
1075
1076 if pycompat.iswindows and not shell:
1076 if pycompat.iswindows and not shell:
1077 # Window's built-in `more` cannot be invoked with shell=False, but
1077 # Window's built-in `more` cannot be invoked with shell=False, but
1078 # its `more.com` can. Hide this implementation detail from the
1078 # its `more.com` can. Hide this implementation detail from the
1079 # user so we can also get sane bad PAGER behavior. MSYS has
1079 # 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
1080 # `more.exe`, so do a cmd.exe style resolution of the executable to
1081 # determine which one to use.
1081 # determine which one to use.
1082 fullcmd = util.findexe(command)
1082 fullcmd = util.findexe(command)
1083 if not fullcmd:
1083 if not fullcmd:
1084 self.warn(_("missing pager command '%s', skipping pager\n")
1084 self.warn(_("missing pager command '%s', skipping pager\n")
1085 % command)
1085 % command)
1086 return False
1086 return False
1087
1087
1088 command = fullcmd
1088 command = fullcmd
1089
1089
1090 try:
1090 try:
1091 pager = subprocess.Popen(
1091 pager = subprocess.Popen(
1092 command, shell=shell, bufsize=-1,
1092 command, shell=shell, bufsize=-1,
1093 close_fds=util.closefds, stdin=subprocess.PIPE,
1093 close_fds=util.closefds, stdin=subprocess.PIPE,
1094 stdout=util.stdout, stderr=util.stderr,
1094 stdout=util.stdout, stderr=util.stderr,
1095 env=util.shellenviron(env))
1095 env=util.shellenviron(env))
1096 except OSError as e:
1096 except OSError as e:
1097 if e.errno == errno.ENOENT and not shell:
1097 if e.errno == errno.ENOENT and not shell:
1098 self.warn(_("missing pager command '%s', skipping pager\n")
1098 self.warn(_("missing pager command '%s', skipping pager\n")
1099 % command)
1099 % command)
1100 return False
1100 return False
1101 raise
1101 raise
1102
1102
1103 # back up original file descriptors
1103 # back up original file descriptors
1104 stdoutfd = os.dup(util.stdout.fileno())
1104 stdoutfd = os.dup(util.stdout.fileno())
1105 stderrfd = os.dup(util.stderr.fileno())
1105 stderrfd = os.dup(util.stderr.fileno())
1106
1106
1107 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
1107 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
1108 if self._isatty(util.stderr):
1108 if self._isatty(util.stderr):
1109 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
1109 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
1110
1110
1111 @self.atexit
1111 @self.atexit
1112 def killpager():
1112 def killpager():
1113 if util.safehasattr(signal, "SIGINT"):
1113 if util.safehasattr(signal, "SIGINT"):
1114 signal.signal(signal.SIGINT, signal.SIG_IGN)
1114 signal.signal(signal.SIGINT, signal.SIG_IGN)
1115 # restore original fds, closing pager.stdin copies in the process
1115 # restore original fds, closing pager.stdin copies in the process
1116 os.dup2(stdoutfd, util.stdout.fileno())
1116 os.dup2(stdoutfd, util.stdout.fileno())
1117 os.dup2(stderrfd, util.stderr.fileno())
1117 os.dup2(stderrfd, util.stderr.fileno())
1118 pager.stdin.close()
1118 pager.stdin.close()
1119 pager.wait()
1119 pager.wait()
1120
1120
1121 return True
1121 return True
1122
1122
1123 @property
1123 @property
1124 def _exithandlers(self):
1124 def _exithandlers(self):
1125 return _reqexithandlers
1125 return _reqexithandlers
1126
1126
1127 def atexit(self, func, *args, **kwargs):
1127 def atexit(self, func, *args, **kwargs):
1128 '''register a function to run after dispatching a request
1128 '''register a function to run after dispatching a request
1129
1129
1130 Handlers do not stay registered across request boundaries.'''
1130 Handlers do not stay registered across request boundaries.'''
1131 self._exithandlers.append((func, args, kwargs))
1131 self._exithandlers.append((func, args, kwargs))
1132 return func
1132 return func
1133
1133
1134 def interface(self, feature):
1134 def interface(self, feature):
1135 """what interface to use for interactive console features?
1135 """what interface to use for interactive console features?
1136
1136
1137 The interface is controlled by the value of `ui.interface` but also by
1137 The interface is controlled by the value of `ui.interface` but also by
1138 the value of feature-specific configuration. For example:
1138 the value of feature-specific configuration. For example:
1139
1139
1140 ui.interface.histedit = text
1140 ui.interface.histedit = text
1141 ui.interface.chunkselector = curses
1141 ui.interface.chunkselector = curses
1142
1142
1143 Here the features are "histedit" and "chunkselector".
1143 Here the features are "histedit" and "chunkselector".
1144
1144
1145 The configuration above means that the default interfaces for commands
1145 The configuration above means that the default interfaces for commands
1146 is curses, the interface for histedit is text and the interface for
1146 is curses, the interface for histedit is text and the interface for
1147 selecting chunk is crecord (the best curses interface available).
1147 selecting chunk is crecord (the best curses interface available).
1148
1148
1149 Consider the following example:
1149 Consider the following example:
1150 ui.interface = curses
1150 ui.interface = curses
1151 ui.interface.histedit = text
1151 ui.interface.histedit = text
1152
1152
1153 Then histedit will use the text interface and chunkselector will use
1153 Then histedit will use the text interface and chunkselector will use
1154 the default curses interface (crecord at the moment).
1154 the default curses interface (crecord at the moment).
1155 """
1155 """
1156 alldefaults = frozenset(["text", "curses"])
1156 alldefaults = frozenset(["text", "curses"])
1157
1157
1158 featureinterfaces = {
1158 featureinterfaces = {
1159 "chunkselector": [
1159 "chunkselector": [
1160 "text",
1160 "text",
1161 "curses",
1161 "curses",
1162 ]
1162 ]
1163 }
1163 }
1164
1164
1165 # Feature-specific interface
1165 # Feature-specific interface
1166 if feature not in featureinterfaces.keys():
1166 if feature not in featureinterfaces.keys():
1167 # Programming error, not user error
1167 # Programming error, not user error
1168 raise ValueError("Unknown feature requested %s" % feature)
1168 raise ValueError("Unknown feature requested %s" % feature)
1169
1169
1170 availableinterfaces = frozenset(featureinterfaces[feature])
1170 availableinterfaces = frozenset(featureinterfaces[feature])
1171 if alldefaults > availableinterfaces:
1171 if alldefaults > availableinterfaces:
1172 # Programming error, not user error. We need a use case to
1172 # Programming error, not user error. We need a use case to
1173 # define the right thing to do here.
1173 # define the right thing to do here.
1174 raise ValueError(
1174 raise ValueError(
1175 "Feature %s does not handle all default interfaces" %
1175 "Feature %s does not handle all default interfaces" %
1176 feature)
1176 feature)
1177
1177
1178 if self.plain():
1178 if self.plain():
1179 return "text"
1179 return "text"
1180
1180
1181 # Default interface for all the features
1181 # Default interface for all the features
1182 defaultinterface = "text"
1182 defaultinterface = "text"
1183 i = self.config("ui", "interface")
1183 i = self.config("ui", "interface")
1184 if i in alldefaults:
1184 if i in alldefaults:
1185 defaultinterface = i
1185 defaultinterface = i
1186
1186
1187 choseninterface = defaultinterface
1187 choseninterface = defaultinterface
1188 f = self.config("ui", "interface.%s" % feature)
1188 f = self.config("ui", "interface.%s" % feature)
1189 if f in availableinterfaces:
1189 if f in availableinterfaces:
1190 choseninterface = f
1190 choseninterface = f
1191
1191
1192 if i is not None and defaultinterface != i:
1192 if i is not None and defaultinterface != i:
1193 if f is not None:
1193 if f is not None:
1194 self.warn(_("invalid value for ui.interface: %s\n") %
1194 self.warn(_("invalid value for ui.interface: %s\n") %
1195 (i,))
1195 (i,))
1196 else:
1196 else:
1197 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1197 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1198 (i, choseninterface))
1198 (i, choseninterface))
1199 if f is not None and choseninterface != f:
1199 if f is not None and choseninterface != f:
1200 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1200 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1201 (feature, f, choseninterface))
1201 (feature, f, choseninterface))
1202
1202
1203 return choseninterface
1203 return choseninterface
1204
1204
1205 def interactive(self):
1205 def interactive(self):
1206 '''is interactive input allowed?
1206 '''is interactive input allowed?
1207
1207
1208 An interactive session is a session where input can be reasonably read
1208 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
1209 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
1210 from stdin should fail with an error, unless a sensible default has been
1211 specified.
1211 specified.
1212
1212
1213 Interactiveness is triggered by the value of the `ui.interactive'
1213 Interactiveness is triggered by the value of the `ui.interactive'
1214 configuration variable or - if it is unset - when `sys.stdin' points
1214 configuration variable or - if it is unset - when `sys.stdin' points
1215 to a terminal device.
1215 to a terminal device.
1216
1216
1217 This function refers to input only; for output, see `ui.formatted()'.
1217 This function refers to input only; for output, see `ui.formatted()'.
1218 '''
1218 '''
1219 i = self.configbool("ui", "interactive")
1219 i = self.configbool("ui", "interactive")
1220 if i is None:
1220 if i is None:
1221 # some environments replace stdin without implementing isatty
1221 # some environments replace stdin without implementing isatty
1222 # usually those are non-interactive
1222 # usually those are non-interactive
1223 return self._isatty(self.fin)
1223 return self._isatty(self.fin)
1224
1224
1225 return i
1225 return i
1226
1226
1227 def termwidth(self):
1227 def termwidth(self):
1228 '''how wide is the terminal in columns?
1228 '''how wide is the terminal in columns?
1229 '''
1229 '''
1230 if 'COLUMNS' in encoding.environ:
1230 if 'COLUMNS' in encoding.environ:
1231 try:
1231 try:
1232 return int(encoding.environ['COLUMNS'])
1232 return int(encoding.environ['COLUMNS'])
1233 except ValueError:
1233 except ValueError:
1234 pass
1234 pass
1235 return scmutil.termsize(self)[0]
1235 return scmutil.termsize(self)[0]
1236
1236
1237 def formatted(self):
1237 def formatted(self):
1238 '''should formatted output be used?
1238 '''should formatted output be used?
1239
1239
1240 It is often desirable to format the output to suite the output medium.
1240 It is often desirable to format the output to suite the output medium.
1241 Examples of this are truncating long lines or colorizing messages.
1241 Examples of this are truncating long lines or colorizing messages.
1242 However, this is not often not desirable when piping output into other
1242 However, this is not often not desirable when piping output into other
1243 utilities, e.g. `grep'.
1243 utilities, e.g. `grep'.
1244
1244
1245 Formatted output is triggered by the value of the `ui.formatted'
1245 Formatted output is triggered by the value of the `ui.formatted'
1246 configuration variable or - if it is unset - when `sys.stdout' points
1246 configuration variable or - if it is unset - when `sys.stdout' points
1247 to a terminal device. Please note that `ui.formatted' should be
1247 to a terminal device. Please note that `ui.formatted' should be
1248 considered an implementation detail; it is not intended for use outside
1248 considered an implementation detail; it is not intended for use outside
1249 Mercurial or its extensions.
1249 Mercurial or its extensions.
1250
1250
1251 This function refers to output only; for input, see `ui.interactive()'.
1251 This function refers to output only; for input, see `ui.interactive()'.
1252 This function always returns false when in plain mode, see `ui.plain()'.
1252 This function always returns false when in plain mode, see `ui.plain()'.
1253 '''
1253 '''
1254 if self.plain():
1254 if self.plain():
1255 return False
1255 return False
1256
1256
1257 i = self.configbool("ui", "formatted")
1257 i = self.configbool("ui", "formatted")
1258 if i is None:
1258 if i is None:
1259 # some environments replace stdout without implementing isatty
1259 # some environments replace stdout without implementing isatty
1260 # usually those are non-interactive
1260 # usually those are non-interactive
1261 return self._isatty(self.fout)
1261 return self._isatty(self.fout)
1262
1262
1263 return i
1263 return i
1264
1264
1265 def _readline(self):
1265 def _readline(self):
1266 if self._isatty(self.fin):
1266 if self._isatty(self.fin):
1267 try:
1267 try:
1268 # magically add command line editing support, where
1268 # magically add command line editing support, where
1269 # available
1269 # available
1270 import readline
1270 import readline
1271 # force demandimport to really load the module
1271 # force demandimport to really load the module
1272 readline.read_history_file
1272 readline.read_history_file
1273 # windows sometimes raises something other than ImportError
1273 # windows sometimes raises something other than ImportError
1274 except Exception:
1274 except Exception:
1275 pass
1275 pass
1276
1276
1277 # prompt ' ' must exist; otherwise readline may delete entire line
1277 # prompt ' ' must exist; otherwise readline may delete entire line
1278 # - http://bugs.python.org/issue12833
1278 # - http://bugs.python.org/issue12833
1279 with self.timeblockedsection('stdio'):
1279 with self.timeblockedsection('stdio'):
1280 line = util.bytesinput(self.fin, self.fout, r' ')
1280 line = util.bytesinput(self.fin, self.fout, r' ')
1281
1281
1282 # When stdin is in binary mode on Windows, it can cause
1282 # When stdin is in binary mode on Windows, it can cause
1283 # raw_input() to emit an extra trailing carriage return
1283 # raw_input() to emit an extra trailing carriage return
1284 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1284 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1285 line = line[:-1]
1285 line = line[:-1]
1286 return line
1286 return line
1287
1287
1288 def prompt(self, msg, default="y"):
1288 def prompt(self, msg, default="y"):
1289 """Prompt user with msg, read response.
1289 """Prompt user with msg, read response.
1290 If ui is not interactive, the default is returned.
1290 If ui is not interactive, the default is returned.
1291 """
1291 """
1292 if not self.interactive():
1292 if not self.interactive():
1293 self.write(msg, ' ', default or '', "\n")
1293 self.write(msg, ' ', default or '', "\n")
1294 return default
1294 return default
1295 self._writenobuf(msg, label='ui.prompt')
1295 self._writenobuf(msg, label='ui.prompt')
1296 self.flush()
1296 self.flush()
1297 try:
1297 try:
1298 r = self._readline()
1298 r = self._readline()
1299 if not r:
1299 if not r:
1300 r = default
1300 r = default
1301 if self.configbool('ui', 'promptecho'):
1301 if self.configbool('ui', 'promptecho'):
1302 self.write(r, "\n")
1302 self.write(r, "\n")
1303 return r
1303 return r
1304 except EOFError:
1304 except EOFError:
1305 raise error.ResponseExpected()
1305 raise error.ResponseExpected()
1306
1306
1307 @staticmethod
1307 @staticmethod
1308 def extractchoices(prompt):
1308 def extractchoices(prompt):
1309 """Extract prompt message and list of choices from specified prompt.
1309 """Extract prompt message and list of choices from specified prompt.
1310
1310
1311 This returns tuple "(message, choices)", and "choices" is the
1311 This returns tuple "(message, choices)", and "choices" is the
1312 list of tuple "(response character, text without &)".
1312 list of tuple "(response character, text without &)".
1313
1313
1314 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1314 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1315 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1315 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1316 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1316 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1317 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1317 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1318 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1318 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1319 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1319 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1320 """
1320 """
1321
1321
1322 # Sadly, the prompt string may have been built with a filename
1322 # Sadly, the prompt string may have been built with a filename
1323 # containing "$$" so let's try to find the first valid-looking
1323 # containing "$$" so let's try to find the first valid-looking
1324 # prompt to start parsing. Sadly, we also can't rely on
1324 # prompt to start parsing. Sadly, we also can't rely on
1325 # choices containing spaces, ASCII, or basically anything
1325 # choices containing spaces, ASCII, or basically anything
1326 # except an ampersand followed by a character.
1326 # except an ampersand followed by a character.
1327 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1327 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1328 msg = m.group(1)
1328 msg = m.group(1)
1329 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1329 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1330 def choicetuple(s):
1330 def choicetuple(s):
1331 ampidx = s.index('&')
1331 ampidx = s.index('&')
1332 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1332 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1333 return (msg, [choicetuple(s) for s in choices])
1333 return (msg, [choicetuple(s) for s in choices])
1334
1334
1335 def promptchoice(self, prompt, default=0):
1335 def promptchoice(self, prompt, default=0):
1336 """Prompt user with a message, read response, and ensure it matches
1336 """Prompt user with a message, read response, and ensure it matches
1337 one of the provided choices. The prompt is formatted as follows:
1337 one of the provided choices. The prompt is formatted as follows:
1338
1338
1339 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1339 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1340
1340
1341 The index of the choice is returned. Responses are case
1341 The index of the choice is returned. Responses are case
1342 insensitive. If ui is not interactive, the default is
1342 insensitive. If ui is not interactive, the default is
1343 returned.
1343 returned.
1344 """
1344 """
1345
1345
1346 msg, choices = self.extractchoices(prompt)
1346 msg, choices = self.extractchoices(prompt)
1347 resps = [r for r, t in choices]
1347 resps = [r for r, t in choices]
1348 while True:
1348 while True:
1349 r = self.prompt(msg, resps[default])
1349 r = self.prompt(msg, resps[default])
1350 if r.lower() in resps:
1350 if r.lower() in resps:
1351 return resps.index(r.lower())
1351 return resps.index(r.lower())
1352 self.write(_("unrecognized response\n"))
1352 self.write(_("unrecognized response\n"))
1353
1353
1354 def getpass(self, prompt=None, default=None):
1354 def getpass(self, prompt=None, default=None):
1355 if not self.interactive():
1355 if not self.interactive():
1356 return default
1356 return default
1357 try:
1357 try:
1358 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1358 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1359 # disable getpass() only if explicitly specified. it's still valid
1359 # disable getpass() only if explicitly specified. it's still valid
1360 # to interact with tty even if fin is not a tty.
1360 # to interact with tty even if fin is not a tty.
1361 with self.timeblockedsection('stdio'):
1361 with self.timeblockedsection('stdio'):
1362 if self.configbool('ui', 'nontty'):
1362 if self.configbool('ui', 'nontty'):
1363 l = self.fin.readline()
1363 l = self.fin.readline()
1364 if not l:
1364 if not l:
1365 raise EOFError
1365 raise EOFError
1366 return l.rstrip('\n')
1366 return l.rstrip('\n')
1367 else:
1367 else:
1368 return getpass.getpass('')
1368 return getpass.getpass('')
1369 except EOFError:
1369 except EOFError:
1370 raise error.ResponseExpected()
1370 raise error.ResponseExpected()
1371 def status(self, *msg, **opts):
1371 def status(self, *msg, **opts):
1372 '''write status message to output (if ui.quiet is False)
1372 '''write status message to output (if ui.quiet is False)
1373
1373
1374 This adds an output label of "ui.status".
1374 This adds an output label of "ui.status".
1375 '''
1375 '''
1376 if not self.quiet:
1376 if not self.quiet:
1377 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1377 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1378 self.write(*msg, **opts)
1378 self.write(*msg, **opts)
1379 def warn(self, *msg, **opts):
1379 def warn(self, *msg, **opts):
1380 '''write warning message to output (stderr)
1380 '''write warning message to output (stderr)
1381
1381
1382 This adds an output label of "ui.warning".
1382 This adds an output label of "ui.warning".
1383 '''
1383 '''
1384 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1384 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1385 self.write_err(*msg, **opts)
1385 self.write_err(*msg, **opts)
1386 def note(self, *msg, **opts):
1386 def note(self, *msg, **opts):
1387 '''write note to output (if ui.verbose is True)
1387 '''write note to output (if ui.verbose is True)
1388
1388
1389 This adds an output label of "ui.note".
1389 This adds an output label of "ui.note".
1390 '''
1390 '''
1391 if self.verbose:
1391 if self.verbose:
1392 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1392 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1393 self.write(*msg, **opts)
1393 self.write(*msg, **opts)
1394 def debug(self, *msg, **opts):
1394 def debug(self, *msg, **opts):
1395 '''write debug message to output (if ui.debugflag is True)
1395 '''write debug message to output (if ui.debugflag is True)
1396
1396
1397 This adds an output label of "ui.debug".
1397 This adds an output label of "ui.debug".
1398 '''
1398 '''
1399 if self.debugflag:
1399 if self.debugflag:
1400 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1400 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1401 self.write(*msg, **opts)
1401 self.write(*msg, **opts)
1402
1402
1403 def edit(self, text, user, extra=None, editform=None, pending=None,
1403 def edit(self, text, user, extra=None, editform=None, pending=None,
1404 repopath=None, action=None):
1404 repopath=None, action=None):
1405 if action is None:
1405 if action is None:
1406 self.develwarn('action is None but will soon be a required '
1406 self.develwarn('action is None but will soon be a required '
1407 'parameter to ui.edit()')
1407 'parameter to ui.edit()')
1408 extra_defaults = {
1408 extra_defaults = {
1409 'prefix': 'editor',
1409 'prefix': 'editor',
1410 'suffix': '.txt',
1410 'suffix': '.txt',
1411 }
1411 }
1412 if extra is not None:
1412 if extra is not None:
1413 if extra.get('suffix') is not None:
1413 if extra.get('suffix') is not None:
1414 self.develwarn('extra.suffix is not None but will soon be '
1414 self.develwarn('extra.suffix is not None but will soon be '
1415 'ignored by ui.edit()')
1415 'ignored by ui.edit()')
1416 extra_defaults.update(extra)
1416 extra_defaults.update(extra)
1417 extra = extra_defaults
1417 extra = extra_defaults
1418
1418
1419 if action == 'diff':
1419 if action == 'diff':
1420 suffix = '.diff'
1420 suffix = '.diff'
1421 elif action:
1421 elif action:
1422 suffix = '.%s.hg.txt' % action
1422 suffix = '.%s.hg.txt' % action
1423 else:
1423 else:
1424 suffix = extra['suffix']
1424 suffix = extra['suffix']
1425
1425
1426 rdir = None
1426 rdir = None
1427 if self.configbool('experimental', 'editortmpinhg'):
1427 if self.configbool('experimental', 'editortmpinhg'):
1428 rdir = repopath
1428 rdir = repopath
1429 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1429 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1430 suffix=suffix,
1430 suffix=suffix,
1431 dir=rdir)
1431 dir=rdir)
1432 try:
1432 try:
1433 f = os.fdopen(fd, r'wb')
1433 f = os.fdopen(fd, r'wb')
1434 f.write(util.tonativeeol(text))
1434 f.write(util.tonativeeol(text))
1435 f.close()
1435 f.close()
1436
1436
1437 environ = {'HGUSER': user}
1437 environ = {'HGUSER': user}
1438 if 'transplant_source' in extra:
1438 if 'transplant_source' in extra:
1439 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1439 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1440 for label in ('intermediate-source', 'source', 'rebase_source'):
1440 for label in ('intermediate-source', 'source', 'rebase_source'):
1441 if label in extra:
1441 if label in extra:
1442 environ.update({'HGREVISION': extra[label]})
1442 environ.update({'HGREVISION': extra[label]})
1443 break
1443 break
1444 if editform:
1444 if editform:
1445 environ.update({'HGEDITFORM': editform})
1445 environ.update({'HGEDITFORM': editform})
1446 if pending:
1446 if pending:
1447 environ.update({'HG_PENDING': pending})
1447 environ.update({'HG_PENDING': pending})
1448
1448
1449 editor = self.geteditor()
1449 editor = self.geteditor()
1450
1450
1451 self.system("%s \"%s\"" % (editor, name),
1451 self.system("%s \"%s\"" % (editor, name),
1452 environ=environ,
1452 environ=environ,
1453 onerr=error.Abort, errprefix=_("edit failed"),
1453 onerr=error.Abort, errprefix=_("edit failed"),
1454 blockedtag='editor')
1454 blockedtag='editor')
1455
1455
1456 f = open(name, r'rb')
1456 f = open(name, r'rb')
1457 t = util.fromnativeeol(f.read())
1457 t = util.fromnativeeol(f.read())
1458 f.close()
1458 f.close()
1459 finally:
1459 finally:
1460 os.unlink(name)
1460 os.unlink(name)
1461
1461
1462 return t
1462 return t
1463
1463
1464 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1464 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1465 blockedtag=None):
1465 blockedtag=None):
1466 '''execute shell command with appropriate output stream. command
1466 '''execute shell command with appropriate output stream. command
1467 output will be redirected if fout is not stdout.
1467 output will be redirected if fout is not stdout.
1468
1468
1469 if command fails and onerr is None, return status, else raise onerr
1469 if command fails and onerr is None, return status, else raise onerr
1470 object as exception.
1470 object as exception.
1471 '''
1471 '''
1472 if blockedtag is None:
1472 if blockedtag is None:
1473 # Long cmds tend to be because of an absolute path on cmd. Keep
1473 # Long cmds tend to be because of an absolute path on cmd. Keep
1474 # the tail end instead
1474 # the tail end instead
1475 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1475 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1476 blockedtag = 'unknown_system_' + cmdsuffix
1476 blockedtag = 'unknown_system_' + cmdsuffix
1477 out = self.fout
1477 out = self.fout
1478 if any(s[1] for s in self._bufferstates):
1478 if any(s[1] for s in self._bufferstates):
1479 out = self
1479 out = self
1480 with self.timeblockedsection(blockedtag):
1480 with self.timeblockedsection(blockedtag):
1481 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1481 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1482 if rc and onerr:
1482 if rc and onerr:
1483 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1483 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1484 util.explainexit(rc)[0])
1484 util.explainexit(rc)[0])
1485 if errprefix:
1485 if errprefix:
1486 errmsg = '%s: %s' % (errprefix, errmsg)
1486 errmsg = '%s: %s' % (errprefix, errmsg)
1487 raise onerr(errmsg)
1487 raise onerr(errmsg)
1488 return rc
1488 return rc
1489
1489
1490 def _runsystem(self, cmd, environ, cwd, out):
1490 def _runsystem(self, cmd, environ, cwd, out):
1491 """actually execute the given shell command (can be overridden by
1491 """actually execute the given shell command (can be overridden by
1492 extensions like chg)"""
1492 extensions like chg)"""
1493 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1493 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1494
1494
1495 def traceback(self, exc=None, force=False):
1495 def traceback(self, exc=None, force=False):
1496 '''print exception traceback if traceback printing enabled or forced.
1496 '''print exception traceback if traceback printing enabled or forced.
1497 only to call in exception handler. returns true if traceback
1497 only to call in exception handler. returns true if traceback
1498 printed.'''
1498 printed.'''
1499 if self.tracebackflag or force:
1499 if self.tracebackflag or force:
1500 if exc is None:
1500 if exc is None:
1501 exc = sys.exc_info()
1501 exc = sys.exc_info()
1502 cause = getattr(exc[1], 'cause', None)
1502 cause = getattr(exc[1], 'cause', None)
1503
1503
1504 if cause is not None:
1504 if cause is not None:
1505 causetb = traceback.format_tb(cause[2])
1505 causetb = traceback.format_tb(cause[2])
1506 exctb = traceback.format_tb(exc[2])
1506 exctb = traceback.format_tb(exc[2])
1507 exconly = traceback.format_exception_only(cause[0], cause[1])
1507 exconly = traceback.format_exception_only(cause[0], cause[1])
1508
1508
1509 # exclude frame where 'exc' was chained and rethrown from exctb
1509 # exclude frame where 'exc' was chained and rethrown from exctb
1510 self.write_err('Traceback (most recent call last):\n',
1510 self.write_err('Traceback (most recent call last):\n',
1511 ''.join(exctb[:-1]),
1511 ''.join(exctb[:-1]),
1512 ''.join(causetb),
1512 ''.join(causetb),
1513 ''.join(exconly))
1513 ''.join(exconly))
1514 else:
1514 else:
1515 output = traceback.format_exception(exc[0], exc[1], exc[2])
1515 output = traceback.format_exception(exc[0], exc[1], exc[2])
1516 self.write_err(encoding.strtolocal(r''.join(output)))
1516 self.write_err(encoding.strtolocal(r''.join(output)))
1517 return self.tracebackflag or force
1517 return self.tracebackflag or force
1518
1518
1519 def geteditor(self):
1519 def geteditor(self):
1520 '''return editor to use'''
1520 '''return editor to use'''
1521 if pycompat.sysplatform == 'plan9':
1521 if pycompat.sysplatform == 'plan9':
1522 # vi is the MIPS instruction simulator on Plan 9. We
1522 # vi is the MIPS instruction simulator on Plan 9. We
1523 # instead default to E to plumb commit messages to
1523 # instead default to E to plumb commit messages to
1524 # avoid confusion.
1524 # avoid confusion.
1525 editor = 'E'
1525 editor = 'E'
1526 else:
1526 else:
1527 editor = 'vi'
1527 editor = 'vi'
1528 return (encoding.environ.get("HGEDITOR") or
1528 return (encoding.environ.get("HGEDITOR") or
1529 self.config("ui", "editor", editor))
1529 self.config("ui", "editor", editor))
1530
1530
1531 @util.propertycache
1531 @util.propertycache
1532 def _progbar(self):
1532 def _progbar(self):
1533 """setup the progbar singleton to the ui object"""
1533 """setup the progbar singleton to the ui object"""
1534 if (self.quiet or self.debugflag
1534 if (self.quiet or self.debugflag
1535 or self.configbool('progress', 'disable')
1535 or self.configbool('progress', 'disable')
1536 or not progress.shouldprint(self)):
1536 or not progress.shouldprint(self)):
1537 return None
1537 return None
1538 return getprogbar(self)
1538 return getprogbar(self)
1539
1539
1540 def _progclear(self):
1540 def _progclear(self):
1541 """clear progress bar output if any. use it before any output"""
1541 """clear progress bar output if any. use it before any output"""
1542 if not haveprogbar(): # nothing loaded yet
1542 if not haveprogbar(): # nothing loaded yet
1543 return
1543 return
1544 if self._progbar is not None and self._progbar.printed:
1544 if self._progbar is not None and self._progbar.printed:
1545 self._progbar.clear()
1545 self._progbar.clear()
1546
1546
1547 def progress(self, topic, pos, item="", unit="", total=None):
1547 def progress(self, topic, pos, item="", unit="", total=None):
1548 '''show a progress message
1548 '''show a progress message
1549
1549
1550 By default a textual progress bar will be displayed if an operation
1550 By default a textual progress bar will be displayed if an operation
1551 takes too long. 'topic' is the current operation, 'item' is a
1551 takes too long. 'topic' is the current operation, 'item' is a
1552 non-numeric marker of the current position (i.e. the currently
1552 non-numeric marker of the current position (i.e. the currently
1553 in-process file), 'pos' is the current numeric position (i.e.
1553 in-process file), 'pos' is the current numeric position (i.e.
1554 revision, bytes, etc.), unit is a corresponding unit label,
1554 revision, bytes, etc.), unit is a corresponding unit label,
1555 and total is the highest expected pos.
1555 and total is the highest expected pos.
1556
1556
1557 Multiple nested topics may be active at a time.
1557 Multiple nested topics may be active at a time.
1558
1558
1559 All topics should be marked closed by setting pos to None at
1559 All topics should be marked closed by setting pos to None at
1560 termination.
1560 termination.
1561 '''
1561 '''
1562 if self._progbar is not None:
1562 if self._progbar is not None:
1563 self._progbar.progress(topic, pos, item=item, unit=unit,
1563 self._progbar.progress(topic, pos, item=item, unit=unit,
1564 total=total)
1564 total=total)
1565 if pos is None or not self.configbool('progress', 'debug'):
1565 if pos is None or not self.configbool('progress', 'debug'):
1566 return
1566 return
1567
1567
1568 if unit:
1568 if unit:
1569 unit = ' ' + unit
1569 unit = ' ' + unit
1570 if item:
1570 if item:
1571 item = ' ' + item
1571 item = ' ' + item
1572
1572
1573 if total:
1573 if total:
1574 pct = 100.0 * pos / total
1574 pct = 100.0 * pos / total
1575 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1575 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1576 % (topic, item, pos, total, unit, pct))
1576 % (topic, item, pos, total, unit, pct))
1577 else:
1577 else:
1578 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1578 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1579
1579
1580 def log(self, service, *msg, **opts):
1580 def log(self, service, *msg, **opts):
1581 '''hook for logging facility extensions
1581 '''hook for logging facility extensions
1582
1582
1583 service should be a readily-identifiable subsystem, which will
1583 service should be a readily-identifiable subsystem, which will
1584 allow filtering.
1584 allow filtering.
1585
1585
1586 *msg should be a newline-terminated format string to log, and
1586 *msg should be a newline-terminated format string to log, and
1587 then any values to %-format into that format string.
1587 then any values to %-format into that format string.
1588
1588
1589 **opts currently has no defined meanings.
1589 **opts currently has no defined meanings.
1590 '''
1590 '''
1591
1591
1592 def label(self, msg, label):
1592 def label(self, msg, label):
1593 '''style msg based on supplied label
1593 '''style msg based on supplied label
1594
1594
1595 If some color mode is enabled, this will add the necessary control
1595 If some color mode is enabled, this will add the necessary control
1596 characters to apply such color. In addition, 'debug' color mode adds
1596 characters to apply such color. In addition, 'debug' color mode adds
1597 markup showing which label affects a piece of text.
1597 markup showing which label affects a piece of text.
1598
1598
1599 ui.write(s, 'label') is equivalent to
1599 ui.write(s, 'label') is equivalent to
1600 ui.write(ui.label(s, 'label')).
1600 ui.write(ui.label(s, 'label')).
1601 '''
1601 '''
1602 if self._colormode is not None:
1602 if self._colormode is not None:
1603 return color.colorlabel(self, msg, label)
1603 return color.colorlabel(self, msg, label)
1604 return msg
1604 return msg
1605
1605
1606 def develwarn(self, msg, stacklevel=1, config=None):
1606 def develwarn(self, msg, stacklevel=1, config=None):
1607 """issue a developer warning message
1607 """issue a developer warning message
1608
1608
1609 Use 'stacklevel' to report the offender some layers further up in the
1609 Use 'stacklevel' to report the offender some layers further up in the
1610 stack.
1610 stack.
1611 """
1611 """
1612 if not self.configbool('devel', 'all-warnings'):
1612 if not self.configbool('devel', 'all-warnings'):
1613 if config is None or not self.configbool('devel', config):
1613 if config is None or not self.configbool('devel', config):
1614 return
1614 return
1615 msg = 'devel-warn: ' + msg
1615 msg = 'devel-warn: ' + msg
1616 stacklevel += 1 # get in develwarn
1616 stacklevel += 1 # get in develwarn
1617 if self.tracebackflag:
1617 if self.tracebackflag:
1618 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1618 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1619 self.log('develwarn', '%s at:\n%s' %
1619 self.log('develwarn', '%s at:\n%s' %
1620 (msg, ''.join(util.getstackframes(stacklevel))))
1620 (msg, ''.join(util.getstackframes(stacklevel))))
1621 else:
1621 else:
1622 curframe = inspect.currentframe()
1622 curframe = inspect.currentframe()
1623 calframe = inspect.getouterframes(curframe, 2)
1623 calframe = inspect.getouterframes(curframe, 2)
1624 fname, lineno, fmsg = calframe[stacklevel][1:4]
1624 fname, lineno, fmsg = calframe[stacklevel][1:4]
1625 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
1625 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
1626 self.write_err('%s at: %s:%d (%s)\n'
1626 self.write_err('%s at: %s:%d (%s)\n'
1627 % (msg, fname, lineno, fmsg))
1627 % (msg, fname, lineno, fmsg))
1628 self.log('develwarn', '%s at: %s:%d (%s)\n',
1628 self.log('develwarn', '%s at: %s:%d (%s)\n',
1629 msg, fname, lineno, fmsg)
1629 msg, fname, lineno, fmsg)
1630 curframe = calframe = None # avoid cycles
1630 curframe = calframe = None # avoid cycles
1631
1631
1632 def deprecwarn(self, msg, version, stacklevel=2):
1632 def deprecwarn(self, msg, version, stacklevel=2):
1633 """issue a deprecation warning
1633 """issue a deprecation warning
1634
1634
1635 - msg: message explaining what is deprecated and how to upgrade,
1635 - msg: message explaining what is deprecated and how to upgrade,
1636 - version: last version where the API will be supported,
1636 - version: last version where the API will be supported,
1637 """
1637 """
1638 if not (self.configbool('devel', 'all-warnings')
1638 if not (self.configbool('devel', 'all-warnings')
1639 or self.configbool('devel', 'deprec-warn')):
1639 or self.configbool('devel', 'deprec-warn')):
1640 return
1640 return
1641 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1641 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1642 " update your code.)") % version
1642 " update your code.)") % version
1643 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1643 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1644
1644
1645 def exportableenviron(self):
1645 def exportableenviron(self):
1646 """The environment variables that are safe to export, e.g. through
1646 """The environment variables that are safe to export, e.g. through
1647 hgweb.
1647 hgweb.
1648 """
1648 """
1649 return self._exportableenviron
1649 return self._exportableenviron
1650
1650
1651 @contextlib.contextmanager
1651 @contextlib.contextmanager
1652 def configoverride(self, overrides, source=""):
1652 def configoverride(self, overrides, source=""):
1653 """Context manager for temporary config overrides
1653 """Context manager for temporary config overrides
1654 `overrides` must be a dict of the following structure:
1654 `overrides` must be a dict of the following structure:
1655 {(section, name) : value}"""
1655 {(section, name) : value}"""
1656 backups = {}
1656 backups = {}
1657 try:
1657 try:
1658 for (section, name), value in overrides.items():
1658 for (section, name), value in overrides.items():
1659 backups[(section, name)] = self.backupconfig(section, name)
1659 backups[(section, name)] = self.backupconfig(section, name)
1660 self.setconfig(section, name, value, source)
1660 self.setconfig(section, name, value, source)
1661 yield
1661 yield
1662 finally:
1662 finally:
1663 for __, backup in backups.items():
1663 for __, backup in backups.items():
1664 self.restoreconfig(backup)
1664 self.restoreconfig(backup)
1665 # just restoring ui.quiet config to the previous value is not enough
1665 # just restoring ui.quiet config to the previous value is not enough
1666 # as it does not update ui.quiet class member
1666 # as it does not update ui.quiet class member
1667 if ('ui', 'quiet') in overrides:
1667 if ('ui', 'quiet') in overrides:
1668 self.fixconfig(section='ui')
1668 self.fixconfig(section='ui')
1669
1669
1670 class paths(dict):
1670 class paths(dict):
1671 """Represents a collection of paths and their configs.
1671 """Represents a collection of paths and their configs.
1672
1672
1673 Data is initially derived from ui instances and the config files they have
1673 Data is initially derived from ui instances and the config files they have
1674 loaded.
1674 loaded.
1675 """
1675 """
1676 def __init__(self, ui):
1676 def __init__(self, ui):
1677 dict.__init__(self)
1677 dict.__init__(self)
1678
1678
1679 for name, loc in ui.configitems('paths', ignoresub=True):
1679 for name, loc in ui.configitems('paths', ignoresub=True):
1680 # No location is the same as not existing.
1680 # No location is the same as not existing.
1681 if not loc:
1681 if not loc:
1682 continue
1682 continue
1683 loc, sub = ui.configsuboptions('paths', name)
1683 loc, sub = ui.configsuboptions('paths', name)
1684 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1684 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1685
1685
1686 def getpath(self, name, default=None):
1686 def getpath(self, name, default=None):
1687 """Return a ``path`` from a string, falling back to default.
1687 """Return a ``path`` from a string, falling back to default.
1688
1688
1689 ``name`` can be a named path or locations. Locations are filesystem
1689 ``name`` can be a named path or locations. Locations are filesystem
1690 paths or URIs.
1690 paths or URIs.
1691
1691
1692 Returns None if ``name`` is not a registered path, a URI, or a local
1692 Returns None if ``name`` is not a registered path, a URI, or a local
1693 path to a repo.
1693 path to a repo.
1694 """
1694 """
1695 # Only fall back to default if no path was requested.
1695 # Only fall back to default if no path was requested.
1696 if name is None:
1696 if name is None:
1697 if not default:
1697 if not default:
1698 default = ()
1698 default = ()
1699 elif not isinstance(default, (tuple, list)):
1699 elif not isinstance(default, (tuple, list)):
1700 default = (default,)
1700 default = (default,)
1701 for k in default:
1701 for k in default:
1702 try:
1702 try:
1703 return self[k]
1703 return self[k]
1704 except KeyError:
1704 except KeyError:
1705 continue
1705 continue
1706 return None
1706 return None
1707
1707
1708 # Most likely empty string.
1708 # Most likely empty string.
1709 # This may need to raise in the future.
1709 # This may need to raise in the future.
1710 if not name:
1710 if not name:
1711 return None
1711 return None
1712
1712
1713 try:
1713 try:
1714 return self[name]
1714 return self[name]
1715 except KeyError:
1715 except KeyError:
1716 # Try to resolve as a local path or URI.
1716 # Try to resolve as a local path or URI.
1717 try:
1717 try:
1718 # We don't pass sub-options in, so no need to pass ui instance.
1718 # We don't pass sub-options in, so no need to pass ui instance.
1719 return path(None, None, rawloc=name)
1719 return path(None, None, rawloc=name)
1720 except ValueError:
1720 except ValueError:
1721 raise error.RepoError(_('repository %s does not exist') %
1721 raise error.RepoError(_('repository %s does not exist') %
1722 name)
1722 name)
1723
1723
1724 _pathsuboptions = {}
1724 _pathsuboptions = {}
1725
1725
1726 def pathsuboption(option, attr):
1726 def pathsuboption(option, attr):
1727 """Decorator used to declare a path sub-option.
1727 """Decorator used to declare a path sub-option.
1728
1728
1729 Arguments are the sub-option name and the attribute it should set on
1729 Arguments are the sub-option name and the attribute it should set on
1730 ``path`` instances.
1730 ``path`` instances.
1731
1731
1732 The decorated function will receive as arguments a ``ui`` instance,
1732 The decorated function will receive as arguments a ``ui`` instance,
1733 ``path`` instance, and the string value of this option from the config.
1733 ``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``
1734 The function should return the value that will be set on the ``path``
1735 instance.
1735 instance.
1736
1736
1737 This decorator can be used to perform additional verification of
1737 This decorator can be used to perform additional verification of
1738 sub-options and to change the type of sub-options.
1738 sub-options and to change the type of sub-options.
1739 """
1739 """
1740 def register(func):
1740 def register(func):
1741 _pathsuboptions[option] = (attr, func)
1741 _pathsuboptions[option] = (attr, func)
1742 return func
1742 return func
1743 return register
1743 return register
1744
1744
1745 @pathsuboption('pushurl', 'pushloc')
1745 @pathsuboption('pushurl', 'pushloc')
1746 def pushurlpathoption(ui, path, value):
1746 def pushurlpathoption(ui, path, value):
1747 u = util.url(value)
1747 u = util.url(value)
1748 # Actually require a URL.
1748 # Actually require a URL.
1749 if not u.scheme:
1749 if not u.scheme:
1750 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1750 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1751 return None
1751 return None
1752
1752
1753 # Don't support the #foo syntax in the push URL to declare branch to
1753 # Don't support the #foo syntax in the push URL to declare branch to
1754 # push.
1754 # push.
1755 if u.fragment:
1755 if u.fragment:
1756 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1756 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1757 'ignoring)\n') % path.name)
1757 'ignoring)\n') % path.name)
1758 u.fragment = None
1758 u.fragment = None
1759
1759
1760 return str(u)
1760 return bytes(u)
1761
1761
1762 @pathsuboption('pushrev', 'pushrev')
1762 @pathsuboption('pushrev', 'pushrev')
1763 def pushrevpathoption(ui, path, value):
1763 def pushrevpathoption(ui, path, value):
1764 return value
1764 return value
1765
1765
1766 class path(object):
1766 class path(object):
1767 """Represents an individual path and its configuration."""
1767 """Represents an individual path and its configuration."""
1768
1768
1769 def __init__(self, ui, name, rawloc=None, suboptions=None):
1769 def __init__(self, ui, name, rawloc=None, suboptions=None):
1770 """Construct a path from its config options.
1770 """Construct a path from its config options.
1771
1771
1772 ``ui`` is the ``ui`` instance the path is coming from.
1772 ``ui`` is the ``ui`` instance the path is coming from.
1773 ``name`` is the symbolic name of the path.
1773 ``name`` is the symbolic name of the path.
1774 ``rawloc`` is the raw location, as defined in the config.
1774 ``rawloc`` is the raw location, as defined in the config.
1775 ``pushloc`` is the raw locations pushes should be made to.
1775 ``pushloc`` is the raw locations pushes should be made to.
1776
1776
1777 If ``name`` is not defined, we require that the location be a) a local
1777 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,
1778 filesystem path with a .hg directory or b) a URL. If not,
1779 ``ValueError`` is raised.
1779 ``ValueError`` is raised.
1780 """
1780 """
1781 if not rawloc:
1781 if not rawloc:
1782 raise ValueError('rawloc must be defined')
1782 raise ValueError('rawloc must be defined')
1783
1783
1784 # Locations may define branches via syntax <base>#<branch>.
1784 # Locations may define branches via syntax <base>#<branch>.
1785 u = util.url(rawloc)
1785 u = util.url(rawloc)
1786 branch = None
1786 branch = None
1787 if u.fragment:
1787 if u.fragment:
1788 branch = u.fragment
1788 branch = u.fragment
1789 u.fragment = None
1789 u.fragment = None
1790
1790
1791 self.url = u
1791 self.url = u
1792 self.branch = branch
1792 self.branch = branch
1793
1793
1794 self.name = name
1794 self.name = name
1795 self.rawloc = rawloc
1795 self.rawloc = rawloc
1796 self.loc = '%s' % u
1796 self.loc = '%s' % u
1797
1797
1798 # When given a raw location but not a symbolic name, validate the
1798 # When given a raw location but not a symbolic name, validate the
1799 # location is valid.
1799 # location is valid.
1800 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1800 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 '
1801 raise ValueError('location is not a URL or path to a local '
1802 'repo: %s' % rawloc)
1802 'repo: %s' % rawloc)
1803
1803
1804 suboptions = suboptions or {}
1804 suboptions = suboptions or {}
1805
1805
1806 # Now process the sub-options. If a sub-option is registered, its
1806 # 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
1807 # attribute will always be present. The value will be None if there
1808 # was no valid sub-option.
1808 # was no valid sub-option.
1809 for suboption, (attr, func) in _pathsuboptions.iteritems():
1809 for suboption, (attr, func) in _pathsuboptions.iteritems():
1810 if suboption not in suboptions:
1810 if suboption not in suboptions:
1811 setattr(self, attr, None)
1811 setattr(self, attr, None)
1812 continue
1812 continue
1813
1813
1814 value = func(ui, self, suboptions[suboption])
1814 value = func(ui, self, suboptions[suboption])
1815 setattr(self, attr, value)
1815 setattr(self, attr, value)
1816
1816
1817 def _isvalidlocalpath(self, path):
1817 def _isvalidlocalpath(self, path):
1818 """Returns True if the given path is a potentially valid repository.
1818 """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
1819 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
1820 'valid' in this case (like when pulling from a git repo into a hg
1821 one)."""
1821 one)."""
1822 return os.path.isdir(os.path.join(path, '.hg'))
1822 return os.path.isdir(os.path.join(path, '.hg'))
1823
1823
1824 @property
1824 @property
1825 def suboptions(self):
1825 def suboptions(self):
1826 """Return sub-options and their values for this path.
1826 """Return sub-options and their values for this path.
1827
1827
1828 This is intended to be used for presentation purposes.
1828 This is intended to be used for presentation purposes.
1829 """
1829 """
1830 d = {}
1830 d = {}
1831 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1831 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1832 value = getattr(self, attr)
1832 value = getattr(self, attr)
1833 if value is not None:
1833 if value is not None:
1834 d[subopt] = value
1834 d[subopt] = value
1835 return d
1835 return d
1836
1836
1837 # we instantiate one globally shared progress bar to avoid
1837 # we instantiate one globally shared progress bar to avoid
1838 # competing progress bars when multiple UI objects get created
1838 # competing progress bars when multiple UI objects get created
1839 _progresssingleton = None
1839 _progresssingleton = None
1840
1840
1841 def getprogbar(ui):
1841 def getprogbar(ui):
1842 global _progresssingleton
1842 global _progresssingleton
1843 if _progresssingleton is None:
1843 if _progresssingleton is None:
1844 # passing 'ui' object to the singleton is fishy,
1844 # passing 'ui' object to the singleton is fishy,
1845 # this is how the extension used to work but feel free to rework it.
1845 # this is how the extension used to work but feel free to rework it.
1846 _progresssingleton = progress.progbar(ui)
1846 _progresssingleton = progress.progbar(ui)
1847 return _progresssingleton
1847 return _progresssingleton
1848
1848
1849 def haveprogbar():
1849 def haveprogbar():
1850 return _progresssingleton is not None
1850 return _progresssingleton is not None
General Comments 0
You need to be logged in to leave comments. Login now