##// END OF EJS Templates
makes username mandatory
Benoit Boissinot -
r3466:8b55c0ba default
parent child Browse files
Show More
@@ -1,496 +1,495
1 HGRC(5)
1 HGRC(5)
2 =======
2 =======
3 Bryan O'Sullivan <bos@serpentine.com>
3 Bryan O'Sullivan <bos@serpentine.com>
4
4
5 NAME
5 NAME
6 ----
6 ----
7 hgrc - configuration files for Mercurial
7 hgrc - configuration files for Mercurial
8
8
9 SYNOPSIS
9 SYNOPSIS
10 --------
10 --------
11
11
12 The Mercurial system uses a set of configuration files to control
12 The Mercurial system uses a set of configuration files to control
13 aspects of its behaviour.
13 aspects of its behaviour.
14
14
15 FILES
15 FILES
16 -----
16 -----
17
17
18 Mercurial reads configuration data from several files, if they exist.
18 Mercurial reads configuration data from several files, if they exist.
19 The names of these files depend on the system on which Mercurial is
19 The names of these files depend on the system on which Mercurial is
20 installed.
20 installed.
21
21
22 (Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
22 (Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
23 (Unix) <install-root>/etc/mercurial/hgrc::
23 (Unix) <install-root>/etc/mercurial/hgrc::
24 Per-installation configuration files, searched for in the
24 Per-installation configuration files, searched for in the
25 directory where Mercurial is installed. For example, if installed
25 directory where Mercurial is installed. For example, if installed
26 in /shared/tools, Mercurial will look in
26 in /shared/tools, Mercurial will look in
27 /shared/tools/etc/mercurial/hgrc. Options in these files apply to
27 /shared/tools/etc/mercurial/hgrc. Options in these files apply to
28 all Mercurial commands executed by any user in any directory.
28 all Mercurial commands executed by any user in any directory.
29
29
30 (Unix) /etc/mercurial/hgrc.d/*.rc::
30 (Unix) /etc/mercurial/hgrc.d/*.rc::
31 (Unix) /etc/mercurial/hgrc::
31 (Unix) /etc/mercurial/hgrc::
32 (Windows) C:\Mercurial\Mercurial.ini::
32 (Windows) C:\Mercurial\Mercurial.ini::
33 Per-system configuration files, for the system on which Mercurial
33 Per-system configuration files, for the system on which Mercurial
34 is running. Options in these files apply to all Mercurial
34 is running. Options in these files apply to all Mercurial
35 commands executed by any user in any directory. Options in these
35 commands executed by any user in any directory. Options in these
36 files override per-installation options.
36 files override per-installation options.
37
37
38 (Unix) $HOME/.hgrc::
38 (Unix) $HOME/.hgrc::
39 (Windows) C:\Documents and Settings\USERNAME\Mercurial.ini::
39 (Windows) C:\Documents and Settings\USERNAME\Mercurial.ini::
40 (Windows) $HOME\Mercurial.ini::
40 (Windows) $HOME\Mercurial.ini::
41 Per-user configuration file, for the user running Mercurial.
41 Per-user configuration file, for the user running Mercurial.
42 Options in this file apply to all Mercurial commands executed by
42 Options in this file apply to all Mercurial commands executed by
43 any user in any directory. Options in this file override
43 any user in any directory. Options in this file override
44 per-installation and per-system options.
44 per-installation and per-system options.
45 On Windows system, one of these is chosen exclusively according
45 On Windows system, one of these is chosen exclusively according
46 to definition of HOME environment variable.
46 to definition of HOME environment variable.
47
47
48 (Unix, Windows) <repo>/.hg/hgrc::
48 (Unix, Windows) <repo>/.hg/hgrc::
49 Per-repository configuration options that only apply in a
49 Per-repository configuration options that only apply in a
50 particular repository. This file is not version-controlled, and
50 particular repository. This file is not version-controlled, and
51 will not get transferred during a "clone" operation. Options in
51 will not get transferred during a "clone" operation. Options in
52 this file override options in all other configuration files.
52 this file override options in all other configuration files.
53
53
54 SYNTAX
54 SYNTAX
55 ------
55 ------
56
56
57 A configuration file consists of sections, led by a "[section]" header
57 A configuration file consists of sections, led by a "[section]" header
58 and followed by "name: value" entries; "name=value" is also accepted.
58 and followed by "name: value" entries; "name=value" is also accepted.
59
59
60 [spam]
60 [spam]
61 eggs=ham
61 eggs=ham
62 green=
62 green=
63 eggs
63 eggs
64
64
65 Each line contains one entry. If the lines that follow are indented,
65 Each line contains one entry. If the lines that follow are indented,
66 they are treated as continuations of that entry.
66 they are treated as continuations of that entry.
67
67
68 Leading whitespace is removed from values. Empty lines are skipped.
68 Leading whitespace is removed from values. Empty lines are skipped.
69
69
70 The optional values can contain format strings which refer to other
70 The optional values can contain format strings which refer to other
71 values in the same section, or values in a special DEFAULT section.
71 values in the same section, or values in a special DEFAULT section.
72
72
73 Lines beginning with "#" or ";" are ignored and may be used to provide
73 Lines beginning with "#" or ";" are ignored and may be used to provide
74 comments.
74 comments.
75
75
76 SECTIONS
76 SECTIONS
77 --------
77 --------
78
78
79 This section describes the different sections that may appear in a
79 This section describes the different sections that may appear in a
80 Mercurial "hgrc" file, the purpose of each section, its possible
80 Mercurial "hgrc" file, the purpose of each section, its possible
81 keys, and their possible values.
81 keys, and their possible values.
82
82
83 decode/encode::
83 decode/encode::
84 Filters for transforming files on checkout/checkin. This would
84 Filters for transforming files on checkout/checkin. This would
85 typically be used for newline processing or other
85 typically be used for newline processing or other
86 localization/canonicalization of files.
86 localization/canonicalization of files.
87
87
88 Filters consist of a filter pattern followed by a filter command.
88 Filters consist of a filter pattern followed by a filter command.
89 Filter patterns are globs by default, rooted at the repository
89 Filter patterns are globs by default, rooted at the repository
90 root. For example, to match any file ending in ".txt" in the root
90 root. For example, to match any file ending in ".txt" in the root
91 directory only, use the pattern "*.txt". To match any file ending
91 directory only, use the pattern "*.txt". To match any file ending
92 in ".c" anywhere in the repository, use the pattern "**.c".
92 in ".c" anywhere in the repository, use the pattern "**.c".
93
93
94 The filter command can start with a specifier, either "pipe:" or
94 The filter command can start with a specifier, either "pipe:" or
95 "tempfile:". If no specifier is given, "pipe:" is used by default.
95 "tempfile:". If no specifier is given, "pipe:" is used by default.
96
96
97 A "pipe:" command must accept data on stdin and return the
97 A "pipe:" command must accept data on stdin and return the
98 transformed data on stdout.
98 transformed data on stdout.
99
99
100 Pipe example:
100 Pipe example:
101
101
102 [encode]
102 [encode]
103 # uncompress gzip files on checkin to improve delta compression
103 # uncompress gzip files on checkin to improve delta compression
104 # note: not necessarily a good idea, just an example
104 # note: not necessarily a good idea, just an example
105 *.gz = pipe: gunzip
105 *.gz = pipe: gunzip
106
106
107 [decode]
107 [decode]
108 # recompress gzip files when writing them to the working dir (we
108 # recompress gzip files when writing them to the working dir (we
109 # can safely omit "pipe:", because it's the default)
109 # can safely omit "pipe:", because it's the default)
110 *.gz = gzip
110 *.gz = gzip
111
111
112 A "tempfile:" command is a template. The string INFILE is replaced
112 A "tempfile:" command is a template. The string INFILE is replaced
113 with the name of a temporary file that contains the data to be
113 with the name of a temporary file that contains the data to be
114 filtered by the command. The string OUTFILE is replaced with the
114 filtered by the command. The string OUTFILE is replaced with the
115 name of an empty temporary file, where the filtered data must be
115 name of an empty temporary file, where the filtered data must be
116 written by the command.
116 written by the command.
117
117
118 NOTE: the tempfile mechanism is recommended for Windows systems,
118 NOTE: the tempfile mechanism is recommended for Windows systems,
119 where the standard shell I/O redirection operators often have
119 where the standard shell I/O redirection operators often have
120 strange effects. In particular, if you are doing line ending
120 strange effects. In particular, if you are doing line ending
121 conversion on Windows using the popular dos2unix and unix2dos
121 conversion on Windows using the popular dos2unix and unix2dos
122 programs, you *must* use the tempfile mechanism, as using pipes will
122 programs, you *must* use the tempfile mechanism, as using pipes will
123 corrupt the contents of your files.
123 corrupt the contents of your files.
124
124
125 Tempfile example:
125 Tempfile example:
126
126
127 [encode]
127 [encode]
128 # convert files to unix line ending conventions on checkin
128 # convert files to unix line ending conventions on checkin
129 **.txt = tempfile: dos2unix -n INFILE OUTFILE
129 **.txt = tempfile: dos2unix -n INFILE OUTFILE
130
130
131 [decode]
131 [decode]
132 # convert files to windows line ending conventions when writing
132 # convert files to windows line ending conventions when writing
133 # them to the working dir
133 # them to the working dir
134 **.txt = tempfile: unix2dos -n INFILE OUTFILE
134 **.txt = tempfile: unix2dos -n INFILE OUTFILE
135
135
136 defaults::
136 defaults::
137 Use the [defaults] section to define command defaults, i.e. the
137 Use the [defaults] section to define command defaults, i.e. the
138 default options/arguments to pass to the specified commands.
138 default options/arguments to pass to the specified commands.
139
139
140 The following example makes 'hg log' run in verbose mode, and
140 The following example makes 'hg log' run in verbose mode, and
141 'hg status' show only the modified files, by default.
141 'hg status' show only the modified files, by default.
142
142
143 [defaults]
143 [defaults]
144 log = -v
144 log = -v
145 status = -m
145 status = -m
146
146
147 The actual commands, instead of their aliases, must be used when
147 The actual commands, instead of their aliases, must be used when
148 defining command defaults. The command defaults will also be
148 defining command defaults. The command defaults will also be
149 applied to the aliases of the commands defined.
149 applied to the aliases of the commands defined.
150
150
151 email::
151 email::
152 Settings for extensions that send email messages.
152 Settings for extensions that send email messages.
153 from;;
153 from;;
154 Optional. Email address to use in "From" header and SMTP envelope
154 Optional. Email address to use in "From" header and SMTP envelope
155 of outgoing messages.
155 of outgoing messages.
156 to;;
156 to;;
157 Optional. Comma-separated list of recipients' email addresses.
157 Optional. Comma-separated list of recipients' email addresses.
158 cc;;
158 cc;;
159 Optional. Comma-separated list of carbon copy recipients'
159 Optional. Comma-separated list of carbon copy recipients'
160 email addresses.
160 email addresses.
161 bcc;;
161 bcc;;
162 Optional. Comma-separated list of blind carbon copy
162 Optional. Comma-separated list of blind carbon copy
163 recipients' email addresses. Cannot be set interactively.
163 recipients' email addresses. Cannot be set interactively.
164 method;;
164 method;;
165 Optional. Method to use to send email messages. If value is
165 Optional. Method to use to send email messages. If value is
166 "smtp" (default), use SMTP (see section "[smtp]" for
166 "smtp" (default), use SMTP (see section "[smtp]" for
167 configuration). Otherwise, use as name of program to run that
167 configuration). Otherwise, use as name of program to run that
168 acts like sendmail (takes "-f" option for sender, list of
168 acts like sendmail (takes "-f" option for sender, list of
169 recipients on command line, message on stdin). Normally, setting
169 recipients on command line, message on stdin). Normally, setting
170 this to "sendmail" or "/usr/sbin/sendmail" is enough to use
170 this to "sendmail" or "/usr/sbin/sendmail" is enough to use
171 sendmail to send messages.
171 sendmail to send messages.
172
172
173 Email example:
173 Email example:
174
174
175 [email]
175 [email]
176 from = Joseph User <joe.user@example.com>
176 from = Joseph User <joe.user@example.com>
177 method = /usr/sbin/sendmail
177 method = /usr/sbin/sendmail
178
178
179 extensions::
179 extensions::
180 Mercurial has an extension mechanism for adding new features. To
180 Mercurial has an extension mechanism for adding new features. To
181 enable an extension, create an entry for it in this section.
181 enable an extension, create an entry for it in this section.
182
182
183 If you know that the extension is already in Python's search path,
183 If you know that the extension is already in Python's search path,
184 you can give the name of the module, followed by "=", with nothing
184 you can give the name of the module, followed by "=", with nothing
185 after the "=".
185 after the "=".
186
186
187 Otherwise, give a name that you choose, followed by "=", followed by
187 Otherwise, give a name that you choose, followed by "=", followed by
188 the path to the ".py" file (including the file name extension) that
188 the path to the ".py" file (including the file name extension) that
189 defines the extension.
189 defines the extension.
190
190
191 Example for ~/.hgrc:
191 Example for ~/.hgrc:
192
192
193 [extensions]
193 [extensions]
194 # (the mq extension will get loaded from mercurial's path)
194 # (the mq extension will get loaded from mercurial's path)
195 hgext.mq =
195 hgext.mq =
196 # (this extension will get loaded from the file specified)
196 # (this extension will get loaded from the file specified)
197 myfeature = ~/.hgext/myfeature.py
197 myfeature = ~/.hgext/myfeature.py
198
198
199 hooks::
199 hooks::
200 Commands or Python functions that get automatically executed by
200 Commands or Python functions that get automatically executed by
201 various actions such as starting or finishing a commit. Multiple
201 various actions such as starting or finishing a commit. Multiple
202 hooks can be run for the same action by appending a suffix to the
202 hooks can be run for the same action by appending a suffix to the
203 action. Overriding a site-wide hook can be done by changing its
203 action. Overriding a site-wide hook can be done by changing its
204 value or setting it to an empty string.
204 value or setting it to an empty string.
205
205
206 Example .hg/hgrc:
206 Example .hg/hgrc:
207
207
208 [hooks]
208 [hooks]
209 # do not use the site-wide hook
209 # do not use the site-wide hook
210 incoming =
210 incoming =
211 incoming.email = /my/email/hook
211 incoming.email = /my/email/hook
212 incoming.autobuild = /my/build/hook
212 incoming.autobuild = /my/build/hook
213
213
214 Most hooks are run with environment variables set that give added
214 Most hooks are run with environment variables set that give added
215 useful information. For each hook below, the environment variables
215 useful information. For each hook below, the environment variables
216 it is passed are listed with names of the form "$HG_foo".
216 it is passed are listed with names of the form "$HG_foo".
217
217
218 changegroup;;
218 changegroup;;
219 Run after a changegroup has been added via push, pull or
219 Run after a changegroup has been added via push, pull or
220 unbundle. ID of the first new changeset is in $HG_NODE. URL from
220 unbundle. ID of the first new changeset is in $HG_NODE. URL from
221 which changes came is in $HG_URL.
221 which changes came is in $HG_URL.
222 commit;;
222 commit;;
223 Run after a changeset has been created in the local repository.
223 Run after a changeset has been created in the local repository.
224 ID of the newly created changeset is in $HG_NODE. Parent
224 ID of the newly created changeset is in $HG_NODE. Parent
225 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
225 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
226 incoming;;
226 incoming;;
227 Run after a changeset has been pulled, pushed, or unbundled into
227 Run after a changeset has been pulled, pushed, or unbundled into
228 the local repository. The ID of the newly arrived changeset is in
228 the local repository. The ID of the newly arrived changeset is in
229 $HG_NODE. URL that was source of changes came is in $HG_URL.
229 $HG_NODE. URL that was source of changes came is in $HG_URL.
230 outgoing;;
230 outgoing;;
231 Run after sending changes from local repository to another. ID of
231 Run after sending changes from local repository to another. ID of
232 first changeset sent is in $HG_NODE. Source of operation is in
232 first changeset sent is in $HG_NODE. Source of operation is in
233 $HG_SOURCE; see "preoutgoing" hook for description.
233 $HG_SOURCE; see "preoutgoing" hook for description.
234 prechangegroup;;
234 prechangegroup;;
235 Run before a changegroup is added via push, pull or unbundle.
235 Run before a changegroup is added via push, pull or unbundle.
236 Exit status 0 allows the changegroup to proceed. Non-zero status
236 Exit status 0 allows the changegroup to proceed. Non-zero status
237 will cause the push, pull or unbundle to fail. URL from which
237 will cause the push, pull or unbundle to fail. URL from which
238 changes will come is in $HG_URL.
238 changes will come is in $HG_URL.
239 precommit;;
239 precommit;;
240 Run before starting a local commit. Exit status 0 allows the
240 Run before starting a local commit. Exit status 0 allows the
241 commit to proceed. Non-zero status will cause the commit to fail.
241 commit to proceed. Non-zero status will cause the commit to fail.
242 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
242 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
243 preoutgoing;;
243 preoutgoing;;
244 Run before computing changes to send from the local repository to
244 Run before computing changes to send from the local repository to
245 another. Non-zero status will cause failure. This lets you
245 another. Non-zero status will cause failure. This lets you
246 prevent pull over http or ssh. Also prevents against local pull,
246 prevent pull over http or ssh. Also prevents against local pull,
247 push (outbound) or bundle commands, but not effective, since you
247 push (outbound) or bundle commands, but not effective, since you
248 can just copy files instead then. Source of operation is in
248 can just copy files instead then. Source of operation is in
249 $HG_SOURCE. If "serve", operation is happening on behalf of
249 $HG_SOURCE. If "serve", operation is happening on behalf of
250 remote ssh or http repository. If "push", "pull" or "bundle",
250 remote ssh or http repository. If "push", "pull" or "bundle",
251 operation is happening on behalf of repository on same system.
251 operation is happening on behalf of repository on same system.
252 pretag;;
252 pretag;;
253 Run before creating a tag. Exit status 0 allows the tag to be
253 Run before creating a tag. Exit status 0 allows the tag to be
254 created. Non-zero status will cause the tag to fail. ID of
254 created. Non-zero status will cause the tag to fail. ID of
255 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
255 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
256 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
256 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
257 pretxnchangegroup;;
257 pretxnchangegroup;;
258 Run after a changegroup has been added via push, pull or unbundle,
258 Run after a changegroup has been added via push, pull or unbundle,
259 but before the transaction has been committed. Changegroup is
259 but before the transaction has been committed. Changegroup is
260 visible to hook program. This lets you validate incoming changes
260 visible to hook program. This lets you validate incoming changes
261 before accepting them. Passed the ID of the first new changeset
261 before accepting them. Passed the ID of the first new changeset
262 in $HG_NODE. Exit status 0 allows the transaction to commit.
262 in $HG_NODE. Exit status 0 allows the transaction to commit.
263 Non-zero status will cause the transaction to be rolled back and
263 Non-zero status will cause the transaction to be rolled back and
264 the push, pull or unbundle will fail. URL that was source of
264 the push, pull or unbundle will fail. URL that was source of
265 changes is in $HG_URL.
265 changes is in $HG_URL.
266 pretxncommit;;
266 pretxncommit;;
267 Run after a changeset has been created but the transaction not yet
267 Run after a changeset has been created but the transaction not yet
268 committed. Changeset is visible to hook program. This lets you
268 committed. Changeset is visible to hook program. This lets you
269 validate commit message and changes. Exit status 0 allows the
269 validate commit message and changes. Exit status 0 allows the
270 commit to proceed. Non-zero status will cause the transaction to
270 commit to proceed. Non-zero status will cause the transaction to
271 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
271 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
272 IDs are in $HG_PARENT1 and $HG_PARENT2.
272 IDs are in $HG_PARENT1 and $HG_PARENT2.
273 preupdate;;
273 preupdate;;
274 Run before updating the working directory. Exit status 0 allows
274 Run before updating the working directory. Exit status 0 allows
275 the update to proceed. Non-zero status will prevent the update.
275 the update to proceed. Non-zero status will prevent the update.
276 Changeset ID of first new parent is in $HG_PARENT1. If merge, ID
276 Changeset ID of first new parent is in $HG_PARENT1. If merge, ID
277 of second new parent is in $HG_PARENT2.
277 of second new parent is in $HG_PARENT2.
278 tag;;
278 tag;;
279 Run after a tag is created. ID of tagged changeset is in
279 Run after a tag is created. ID of tagged changeset is in
280 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
280 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
281 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
281 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
282 update;;
282 update;;
283 Run after updating the working directory. Changeset ID of first
283 Run after updating the working directory. Changeset ID of first
284 new parent is in $HG_PARENT1. If merge, ID of second new parent
284 new parent is in $HG_PARENT1. If merge, ID of second new parent
285 is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update
285 is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update
286 failed (e.g. because conflicts not resolved), $HG_ERROR=1.
286 failed (e.g. because conflicts not resolved), $HG_ERROR=1.
287
287
288 Note: In earlier releases, the names of hook environment variables
288 Note: In earlier releases, the names of hook environment variables
289 did not have a "HG_" prefix. The old unprefixed names are no longer
289 did not have a "HG_" prefix. The old unprefixed names are no longer
290 provided in the environment.
290 provided in the environment.
291
291
292 The syntax for Python hooks is as follows:
292 The syntax for Python hooks is as follows:
293
293
294 hookname = python:modulename.submodule.callable
294 hookname = python:modulename.submodule.callable
295
295
296 Python hooks are run within the Mercurial process. Each hook is
296 Python hooks are run within the Mercurial process. Each hook is
297 called with at least three keyword arguments: a ui object (keyword
297 called with at least three keyword arguments: a ui object (keyword
298 "ui"), a repository object (keyword "repo"), and a "hooktype"
298 "ui"), a repository object (keyword "repo"), and a "hooktype"
299 keyword that tells what kind of hook is used. Arguments listed as
299 keyword that tells what kind of hook is used. Arguments listed as
300 environment variables above are passed as keyword arguments, with no
300 environment variables above are passed as keyword arguments, with no
301 "HG_" prefix, and names in lower case.
301 "HG_" prefix, and names in lower case.
302
302
303 A Python hook must return a "true" value to succeed. Returning a
303 A Python hook must return a "true" value to succeed. Returning a
304 "false" value or raising an exception is treated as failure of the
304 "false" value or raising an exception is treated as failure of the
305 hook.
305 hook.
306
306
307 http_proxy::
307 http_proxy::
308 Used to access web-based Mercurial repositories through a HTTP
308 Used to access web-based Mercurial repositories through a HTTP
309 proxy.
309 proxy.
310 host;;
310 host;;
311 Host name and (optional) port of the proxy server, for example
311 Host name and (optional) port of the proxy server, for example
312 "myproxy:8000".
312 "myproxy:8000".
313 no;;
313 no;;
314 Optional. Comma-separated list of host names that should bypass
314 Optional. Comma-separated list of host names that should bypass
315 the proxy.
315 the proxy.
316 passwd;;
316 passwd;;
317 Optional. Password to authenticate with at the proxy server.
317 Optional. Password to authenticate with at the proxy server.
318 user;;
318 user;;
319 Optional. User name to authenticate with at the proxy server.
319 Optional. User name to authenticate with at the proxy server.
320
320
321 smtp::
321 smtp::
322 Configuration for extensions that need to send email messages.
322 Configuration for extensions that need to send email messages.
323 host;;
323 host;;
324 Host name of mail server, e.g. "mail.example.com".
324 Host name of mail server, e.g. "mail.example.com".
325 port;;
325 port;;
326 Optional. Port to connect to on mail server. Default: 25.
326 Optional. Port to connect to on mail server. Default: 25.
327 tls;;
327 tls;;
328 Optional. Whether to connect to mail server using TLS. True or
328 Optional. Whether to connect to mail server using TLS. True or
329 False. Default: False.
329 False. Default: False.
330 username;;
330 username;;
331 Optional. User name to authenticate to SMTP server with.
331 Optional. User name to authenticate to SMTP server with.
332 If username is specified, password must also be specified.
332 If username is specified, password must also be specified.
333 Default: none.
333 Default: none.
334 password;;
334 password;;
335 Optional. Password to authenticate to SMTP server with.
335 Optional. Password to authenticate to SMTP server with.
336 If username is specified, password must also be specified.
336 If username is specified, password must also be specified.
337 Default: none.
337 Default: none.
338 local_hostname;;
338 local_hostname;;
339 Optional. It's the hostname that the sender can use to identify itself
339 Optional. It's the hostname that the sender can use to identify itself
340 to the MTA.
340 to the MTA.
341
341
342 paths::
342 paths::
343 Assigns symbolic names to repositories. The left side is the
343 Assigns symbolic names to repositories. The left side is the
344 symbolic name, and the right gives the directory or URL that is the
344 symbolic name, and the right gives the directory or URL that is the
345 location of the repository. Default paths can be declared by
345 location of the repository. Default paths can be declared by
346 setting the following entries.
346 setting the following entries.
347 default;;
347 default;;
348 Directory or URL to use when pulling if no source is specified.
348 Directory or URL to use when pulling if no source is specified.
349 Default is set to repository from which the current repository
349 Default is set to repository from which the current repository
350 was cloned.
350 was cloned.
351 default-push;;
351 default-push;;
352 Optional. Directory or URL to use when pushing if no destination
352 Optional. Directory or URL to use when pushing if no destination
353 is specified.
353 is specified.
354
354
355 server::
355 server::
356 Controls generic server settings.
356 Controls generic server settings.
357 uncompressed;;
357 uncompressed;;
358 Whether to allow clients to clone a repo using the uncompressed
358 Whether to allow clients to clone a repo using the uncompressed
359 streaming protocol. This transfers about 40% more data than a
359 streaming protocol. This transfers about 40% more data than a
360 regular clone, but uses less memory and CPU on both server and
360 regular clone, but uses less memory and CPU on both server and
361 client. Over a LAN (100Mbps or better) or a very fast WAN, an
361 client. Over a LAN (100Mbps or better) or a very fast WAN, an
362 uncompressed streaming clone is a lot faster (~10x) than a regular
362 uncompressed streaming clone is a lot faster (~10x) than a regular
363 clone. Over most WAN connections (anything slower than about
363 clone. Over most WAN connections (anything slower than about
364 6Mbps), uncompressed streaming is slower, because of the extra
364 6Mbps), uncompressed streaming is slower, because of the extra
365 data transfer overhead. Default is False.
365 data transfer overhead. Default is False.
366
366
367 ui::
367 ui::
368 User interface controls.
368 User interface controls.
369 debug;;
369 debug;;
370 Print debugging information. True or False. Default is False.
370 Print debugging information. True or False. Default is False.
371 editor;;
371 editor;;
372 The editor to use during a commit. Default is $EDITOR or "vi".
372 The editor to use during a commit. Default is $EDITOR or "vi".
373 ignore;;
373 ignore;;
374 A file to read per-user ignore patterns from. This file should be in
374 A file to read per-user ignore patterns from. This file should be in
375 the same format as a repository-wide .hgignore file. This option
375 the same format as a repository-wide .hgignore file. This option
376 supports hook syntax, so if you want to specify multiple ignore
376 supports hook syntax, so if you want to specify multiple ignore
377 files, you can do so by setting something like
377 files, you can do so by setting something like
378 "ignore.other = ~/.hgignore2". For details of the ignore file
378 "ignore.other = ~/.hgignore2". For details of the ignore file
379 format, see the hgignore(5) man page.
379 format, see the hgignore(5) man page.
380 interactive;;
380 interactive;;
381 Allow to prompt the user. True or False. Default is True.
381 Allow to prompt the user. True or False. Default is True.
382 logtemplate;;
382 logtemplate;;
383 Template string for commands that print changesets.
383 Template string for commands that print changesets.
384 style;;
384 style;;
385 Name of style to use for command output.
385 Name of style to use for command output.
386 merge;;
386 merge;;
387 The conflict resolution program to use during a manual merge.
387 The conflict resolution program to use during a manual merge.
388 Default is "hgmerge".
388 Default is "hgmerge".
389 quiet;;
389 quiet;;
390 Reduce the amount of output printed. True or False. Default is False.
390 Reduce the amount of output printed. True or False. Default is False.
391 remotecmd;;
391 remotecmd;;
392 remote command to use for clone/push/pull operations. Default is 'hg'.
392 remote command to use for clone/push/pull operations. Default is 'hg'.
393 ssh;;
393 ssh;;
394 command to use for SSH connections. Default is 'ssh'.
394 command to use for SSH connections. Default is 'ssh'.
395 strict;;
395 strict;;
396 Require exact command names, instead of allowing unambiguous
396 Require exact command names, instead of allowing unambiguous
397 abbreviations. True or False. Default is False.
397 abbreviations. True or False. Default is False.
398 timeout;;
398 timeout;;
399 The timeout used when a lock is held (in seconds), a negative value
399 The timeout used when a lock is held (in seconds), a negative value
400 means no timeout. Default is 600.
400 means no timeout. Default is 600.
401 username;;
401 username;;
402 The committer of a changeset created when running "commit".
402 The committer of a changeset created when running "commit".
403 Typically a person's name and email address, e.g. "Fred Widget
403 Typically a person's name and email address, e.g. "Fred Widget
404 <fred@example.com>". Default is $EMAIL or username@hostname, unless
404 <fred@example.com>". Default is $EMAIL. If no default is found,
405 username is set to an empty string, which enforces specifying the
405 the username have to be specified manually.
406 username manually.
407 verbose;;
406 verbose;;
408 Increase the amount of output printed. True or False. Default is False.
407 Increase the amount of output printed. True or False. Default is False.
409
408
410
409
411 web::
410 web::
412 Web interface configuration.
411 Web interface configuration.
413 accesslog;;
412 accesslog;;
414 Where to output the access log. Default is stdout.
413 Where to output the access log. Default is stdout.
415 address;;
414 address;;
416 Interface address to bind to. Default is all.
415 Interface address to bind to. Default is all.
417 allow_archive;;
416 allow_archive;;
418 List of archive format (bz2, gz, zip) allowed for downloading.
417 List of archive format (bz2, gz, zip) allowed for downloading.
419 Default is empty.
418 Default is empty.
420 allowbz2;;
419 allowbz2;;
421 (DEPRECATED) Whether to allow .tar.bz2 downloading of repo revisions.
420 (DEPRECATED) Whether to allow .tar.bz2 downloading of repo revisions.
422 Default is false.
421 Default is false.
423 allowgz;;
422 allowgz;;
424 (DEPRECATED) Whether to allow .tar.gz downloading of repo revisions.
423 (DEPRECATED) Whether to allow .tar.gz downloading of repo revisions.
425 Default is false.
424 Default is false.
426 allowpull;;
425 allowpull;;
427 Whether to allow pulling from the repository. Default is true.
426 Whether to allow pulling from the repository. Default is true.
428 allow_push;;
427 allow_push;;
429 Whether to allow pushing to the repository. If empty or not set,
428 Whether to allow pushing to the repository. If empty or not set,
430 push is not allowed. If the special value "*", any remote user
429 push is not allowed. If the special value "*", any remote user
431 can push, including unauthenticated users. Otherwise, the remote
430 can push, including unauthenticated users. Otherwise, the remote
432 user must have been authenticated, and the authenticated user name
431 user must have been authenticated, and the authenticated user name
433 must be present in this list (separated by whitespace or ",").
432 must be present in this list (separated by whitespace or ",").
434 The contents of the allow_push list are examined after the
433 The contents of the allow_push list are examined after the
435 deny_push list.
434 deny_push list.
436 allowzip;;
435 allowzip;;
437 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
436 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
438 Default is false. This feature creates temporary files.
437 Default is false. This feature creates temporary files.
439 baseurl;;
438 baseurl;;
440 Base URL to use when publishing URLs in other locations, so
439 Base URL to use when publishing URLs in other locations, so
441 third-party tools like email notification hooks can construct URLs.
440 third-party tools like email notification hooks can construct URLs.
442 Example: "http://hgserver/repos/"
441 Example: "http://hgserver/repos/"
443 contact;;
442 contact;;
444 Name or email address of the person in charge of the repository.
443 Name or email address of the person in charge of the repository.
445 Default is "unknown".
444 Default is "unknown".
446 deny_push;;
445 deny_push;;
447 Whether to deny pushing to the repository. If empty or not set,
446 Whether to deny pushing to the repository. If empty or not set,
448 push is not denied. If the special value "*", all remote users
447 push is not denied. If the special value "*", all remote users
449 are denied push. Otherwise, unauthenticated users are all denied,
448 are denied push. Otherwise, unauthenticated users are all denied,
450 and any authenticated user name present in this list (separated by
449 and any authenticated user name present in this list (separated by
451 whitespace or ",") is also denied. The contents of the deny_push
450 whitespace or ",") is also denied. The contents of the deny_push
452 list are examined before the allow_push list.
451 list are examined before the allow_push list.
453 description;;
452 description;;
454 Textual description of the repository's purpose or contents.
453 Textual description of the repository's purpose or contents.
455 Default is "unknown".
454 Default is "unknown".
456 errorlog;;
455 errorlog;;
457 Where to output the error log. Default is stderr.
456 Where to output the error log. Default is stderr.
458 ipv6;;
457 ipv6;;
459 Whether to use IPv6. Default is false.
458 Whether to use IPv6. Default is false.
460 name;;
459 name;;
461 Repository name to use in the web interface. Default is current
460 Repository name to use in the web interface. Default is current
462 working directory.
461 working directory.
463 maxchanges;;
462 maxchanges;;
464 Maximum number of changes to list on the changelog. Default is 10.
463 Maximum number of changes to list on the changelog. Default is 10.
465 maxfiles;;
464 maxfiles;;
466 Maximum number of files to list per changeset. Default is 10.
465 Maximum number of files to list per changeset. Default is 10.
467 port;;
466 port;;
468 Port to listen on. Default is 8000.
467 Port to listen on. Default is 8000.
469 push_ssl;;
468 push_ssl;;
470 Whether to require that inbound pushes be transported over SSL to
469 Whether to require that inbound pushes be transported over SSL to
471 prevent password sniffing. Default is true.
470 prevent password sniffing. Default is true.
472 stripes;;
471 stripes;;
473 How many lines a "zebra stripe" should span in multiline output.
472 How many lines a "zebra stripe" should span in multiline output.
474 Default is 1; set to 0 to disable.
473 Default is 1; set to 0 to disable.
475 style;;
474 style;;
476 Which template map style to use.
475 Which template map style to use.
477 templates;;
476 templates;;
478 Where to find the HTML templates. Default is install path.
477 Where to find the HTML templates. Default is install path.
479
478
480
479
481 AUTHOR
480 AUTHOR
482 ------
481 ------
483 Bryan O'Sullivan <bos@serpentine.com>.
482 Bryan O'Sullivan <bos@serpentine.com>.
484
483
485 Mercurial was written by Matt Mackall <mpm@selenic.com>.
484 Mercurial was written by Matt Mackall <mpm@selenic.com>.
486
485
487 SEE ALSO
486 SEE ALSO
488 --------
487 --------
489 hg(1), hgignore(5)
488 hg(1), hgignore(5)
490
489
491 COPYING
490 COPYING
492 -------
491 -------
493 This manual page is copyright 2005 Bryan O'Sullivan.
492 This manual page is copyright 2005 Bryan O'Sullivan.
494 Mercurial is copyright 2005, 2006 Matt Mackall.
493 Mercurial is copyright 2005, 2006 Matt Mackall.
495 Free use of this software is granted under the terms of the GNU General
494 Free use of this software is granted under the terms of the GNU General
496 Public License (GPL).
495 Public License (GPL).
@@ -1,342 +1,337
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import gettext as _
8 from i18n import gettext as _
9 from demandload import *
9 from demandload import *
10 demandload(globals(), "errno getpass os re socket sys tempfile")
10 demandload(globals(), "errno getpass os re socket sys tempfile")
11 demandload(globals(), "ConfigParser traceback util")
11 demandload(globals(), "ConfigParser traceback util")
12
12
13 def dupconfig(orig):
13 def dupconfig(orig):
14 new = util.configparser(orig.defaults())
14 new = util.configparser(orig.defaults())
15 updateconfig(orig, new)
15 updateconfig(orig, new)
16 return new
16 return new
17
17
18 def updateconfig(source, dest, sections=None):
18 def updateconfig(source, dest, sections=None):
19 if not sections:
19 if not sections:
20 sections = source.sections()
20 sections = source.sections()
21 for section in sections:
21 for section in sections:
22 if not dest.has_section(section):
22 if not dest.has_section(section):
23 dest.add_section(section)
23 dest.add_section(section)
24 for name, value in source.items(section, raw=True):
24 for name, value in source.items(section, raw=True):
25 dest.set(section, name, value)
25 dest.set(section, name, value)
26
26
27 class ui(object):
27 class ui(object):
28 def __init__(self, verbose=False, debug=False, quiet=False,
28 def __init__(self, verbose=False, debug=False, quiet=False,
29 interactive=True, traceback=False, parentui=None):
29 interactive=True, traceback=False, parentui=None):
30 self.overlay = None
30 self.overlay = None
31 self.header = []
31 self.header = []
32 self.prev_header = []
32 self.prev_header = []
33 if parentui is None:
33 if parentui is None:
34 # this is the parent of all ui children
34 # this is the parent of all ui children
35 self.parentui = None
35 self.parentui = None
36 self.readhooks = []
36 self.readhooks = []
37 self.quiet = quiet
37 self.quiet = quiet
38 self.verbose = verbose
38 self.verbose = verbose
39 self.debugflag = debug
39 self.debugflag = debug
40 self.interactive = interactive
40 self.interactive = interactive
41 self.traceback = traceback
41 self.traceback = traceback
42 self.cdata = util.configparser()
42 self.cdata = util.configparser()
43 self.readconfig(util.rcpath())
43 self.readconfig(util.rcpath())
44 self.updateopts(verbose, debug, quiet, interactive)
44 self.updateopts(verbose, debug, quiet, interactive)
45 else:
45 else:
46 # parentui may point to an ui object which is already a child
46 # parentui may point to an ui object which is already a child
47 self.parentui = parentui.parentui or parentui
47 self.parentui = parentui.parentui or parentui
48 self.readhooks = self.parentui.readhooks[:]
48 self.readhooks = self.parentui.readhooks[:]
49 self.cdata = dupconfig(self.parentui.cdata)
49 self.cdata = dupconfig(self.parentui.cdata)
50 if self.parentui.overlay:
50 if self.parentui.overlay:
51 self.overlay = dupconfig(self.parentui.overlay)
51 self.overlay = dupconfig(self.parentui.overlay)
52
52
53 def __getattr__(self, key):
53 def __getattr__(self, key):
54 return getattr(self.parentui, key)
54 return getattr(self.parentui, key)
55
55
56 def updateopts(self, verbose=False, debug=False, quiet=False,
56 def updateopts(self, verbose=False, debug=False, quiet=False,
57 interactive=True, traceback=False, config=[]):
57 interactive=True, traceback=False, config=[]):
58 for section, name, value in config:
58 for section, name, value in config:
59 self.setconfig(section, name, value)
59 self.setconfig(section, name, value)
60
60
61 if quiet or verbose or debug:
61 if quiet or verbose or debug:
62 self.setconfig('ui', 'quiet', str(bool(quiet)))
62 self.setconfig('ui', 'quiet', str(bool(quiet)))
63 self.setconfig('ui', 'verbose', str(bool(verbose)))
63 self.setconfig('ui', 'verbose', str(bool(verbose)))
64 self.setconfig('ui', 'debug', str(bool(debug)))
64 self.setconfig('ui', 'debug', str(bool(debug)))
65
65
66 self.verbosity_constraints()
66 self.verbosity_constraints()
67
67
68 if not interactive:
68 if not interactive:
69 self.setconfig('ui', 'interactive', 'False')
69 self.setconfig('ui', 'interactive', 'False')
70 self.interactive = False
70 self.interactive = False
71
71
72 self.traceback = self.traceback or traceback
72 self.traceback = self.traceback or traceback
73
73
74 def verbosity_constraints(self):
74 def verbosity_constraints(self):
75 self.quiet = self.configbool('ui', 'quiet')
75 self.quiet = self.configbool('ui', 'quiet')
76 self.verbose = self.configbool('ui', 'verbose')
76 self.verbose = self.configbool('ui', 'verbose')
77 self.debugflag = self.configbool('ui', 'debug')
77 self.debugflag = self.configbool('ui', 'debug')
78
78
79 if self.debugflag:
79 if self.debugflag:
80 self.verbose = True
80 self.verbose = True
81 self.quiet = False
81 self.quiet = False
82 elif self.verbose and self.quiet:
82 elif self.verbose and self.quiet:
83 self.quiet = self.verbose = False
83 self.quiet = self.verbose = False
84
84
85 def readconfig(self, fn, root=None):
85 def readconfig(self, fn, root=None):
86 if isinstance(fn, basestring):
86 if isinstance(fn, basestring):
87 fn = [fn]
87 fn = [fn]
88 for f in fn:
88 for f in fn:
89 try:
89 try:
90 self.cdata.read(f)
90 self.cdata.read(f)
91 except ConfigParser.ParsingError, inst:
91 except ConfigParser.ParsingError, inst:
92 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
92 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
93 # override data from config files with data set with ui.setconfig
93 # override data from config files with data set with ui.setconfig
94 if self.overlay:
94 if self.overlay:
95 updateconfig(self.overlay, self.cdata)
95 updateconfig(self.overlay, self.cdata)
96 if root is None:
96 if root is None:
97 root = os.path.expanduser('~')
97 root = os.path.expanduser('~')
98 self.fixconfig(root=root)
98 self.fixconfig(root=root)
99 for hook in self.readhooks:
99 for hook in self.readhooks:
100 hook(self)
100 hook(self)
101
101
102 def addreadhook(self, hook):
102 def addreadhook(self, hook):
103 self.readhooks.append(hook)
103 self.readhooks.append(hook)
104
104
105 def readsections(self, filename, *sections):
105 def readsections(self, filename, *sections):
106 "read filename and add only the specified sections to the config data"
106 "read filename and add only the specified sections to the config data"
107 if not sections:
107 if not sections:
108 return
108 return
109
109
110 cdata = util.configparser()
110 cdata = util.configparser()
111 try:
111 try:
112 cdata.read(filename)
112 cdata.read(filename)
113 except ConfigParser.ParsingError, inst:
113 except ConfigParser.ParsingError, inst:
114 raise util.Abort(_("failed to parse %s\n%s") % (f, inst))
114 raise util.Abort(_("failed to parse %s\n%s") % (f, inst))
115
115
116 for section in sections:
116 for section in sections:
117 if not cdata.has_section(section):
117 if not cdata.has_section(section):
118 cdata.add_section(section)
118 cdata.add_section(section)
119
119
120 updateconfig(cdata, self.cdata, sections)
120 updateconfig(cdata, self.cdata, sections)
121
121
122 def fixconfig(self, section=None, name=None, value=None, root=None):
122 def fixconfig(self, section=None, name=None, value=None, root=None):
123 # translate paths relative to root (or home) into absolute paths
123 # translate paths relative to root (or home) into absolute paths
124 if section is None or section == 'paths':
124 if section is None or section == 'paths':
125 if root is None:
125 if root is None:
126 root = os.getcwd()
126 root = os.getcwd()
127 items = section and [(name, value)] or []
127 items = section and [(name, value)] or []
128 for cdata in self.cdata, self.overlay:
128 for cdata in self.cdata, self.overlay:
129 if not cdata: continue
129 if not cdata: continue
130 if not items and cdata.has_section('paths'):
130 if not items and cdata.has_section('paths'):
131 pathsitems = cdata.items('paths')
131 pathsitems = cdata.items('paths')
132 else:
132 else:
133 pathsitems = items
133 pathsitems = items
134 for n, path in pathsitems:
134 for n, path in pathsitems:
135 if path and "://" not in path and not os.path.isabs(path):
135 if path and "://" not in path and not os.path.isabs(path):
136 cdata.set("paths", n, os.path.join(root, path))
136 cdata.set("paths", n, os.path.join(root, path))
137
137
138 # update quiet/verbose/debug and interactive status
138 # update quiet/verbose/debug and interactive status
139 if section is None or section == 'ui':
139 if section is None or section == 'ui':
140 if name is None or name in ('quiet', 'verbose', 'debug'):
140 if name is None or name in ('quiet', 'verbose', 'debug'):
141 self.verbosity_constraints()
141 self.verbosity_constraints()
142
142
143 if name is None or name == 'interactive':
143 if name is None or name == 'interactive':
144 self.interactive = self.configbool("ui", "interactive", True)
144 self.interactive = self.configbool("ui", "interactive", True)
145
145
146 def setconfig(self, section, name, value):
146 def setconfig(self, section, name, value):
147 if not self.overlay:
147 if not self.overlay:
148 self.overlay = util.configparser()
148 self.overlay = util.configparser()
149 for cdata in (self.overlay, self.cdata):
149 for cdata in (self.overlay, self.cdata):
150 if not cdata.has_section(section):
150 if not cdata.has_section(section):
151 cdata.add_section(section)
151 cdata.add_section(section)
152 cdata.set(section, name, value)
152 cdata.set(section, name, value)
153 self.fixconfig(section, name, value)
153 self.fixconfig(section, name, value)
154
154
155 def _config(self, section, name, default, funcname):
155 def _config(self, section, name, default, funcname):
156 if self.cdata.has_option(section, name):
156 if self.cdata.has_option(section, name):
157 try:
157 try:
158 func = getattr(self.cdata, funcname)
158 func = getattr(self.cdata, funcname)
159 return func(section, name)
159 return func(section, name)
160 except ConfigParser.InterpolationError, inst:
160 except ConfigParser.InterpolationError, inst:
161 raise util.Abort(_("Error in configuration section [%s] "
161 raise util.Abort(_("Error in configuration section [%s] "
162 "parameter '%s':\n%s")
162 "parameter '%s':\n%s")
163 % (section, name, inst))
163 % (section, name, inst))
164 return default
164 return default
165
165
166 def config(self, section, name, default=None):
166 def config(self, section, name, default=None):
167 return self._config(section, name, default, 'get')
167 return self._config(section, name, default, 'get')
168
168
169 def configbool(self, section, name, default=False):
169 def configbool(self, section, name, default=False):
170 return self._config(section, name, default, 'getboolean')
170 return self._config(section, name, default, 'getboolean')
171
171
172 def configlist(self, section, name, default=None):
172 def configlist(self, section, name, default=None):
173 """Return a list of comma/space separated strings"""
173 """Return a list of comma/space separated strings"""
174 result = self.config(section, name)
174 result = self.config(section, name)
175 if result is None:
175 if result is None:
176 result = default or []
176 result = default or []
177 if isinstance(result, basestring):
177 if isinstance(result, basestring):
178 result = result.replace(",", " ").split()
178 result = result.replace(",", " ").split()
179 return result
179 return result
180
180
181 def has_config(self, section):
181 def has_config(self, section):
182 '''tell whether section exists in config.'''
182 '''tell whether section exists in config.'''
183 return self.cdata.has_section(section)
183 return self.cdata.has_section(section)
184
184
185 def configitems(self, section):
185 def configitems(self, section):
186 items = {}
186 items = {}
187 if self.cdata.has_section(section):
187 if self.cdata.has_section(section):
188 try:
188 try:
189 items.update(dict(self.cdata.items(section)))
189 items.update(dict(self.cdata.items(section)))
190 except ConfigParser.InterpolationError, inst:
190 except ConfigParser.InterpolationError, inst:
191 raise util.Abort(_("Error in configuration section [%s]:\n%s")
191 raise util.Abort(_("Error in configuration section [%s]:\n%s")
192 % (section, inst))
192 % (section, inst))
193 x = items.items()
193 x = items.items()
194 x.sort()
194 x.sort()
195 return x
195 return x
196
196
197 def walkconfig(self):
197 def walkconfig(self):
198 sections = self.cdata.sections()
198 sections = self.cdata.sections()
199 sections.sort()
199 sections.sort()
200 for section in sections:
200 for section in sections:
201 for name, value in self.configitems(section):
201 for name, value in self.configitems(section):
202 yield section, name, value.replace('\n', '\\n')
202 yield section, name, value.replace('\n', '\\n')
203
203
204 def extensions(self):
204 def extensions(self):
205 result = self.configitems("extensions")
205 result = self.configitems("extensions")
206 for i, (key, value) in enumerate(result):
206 for i, (key, value) in enumerate(result):
207 if value:
207 if value:
208 result[i] = (key, os.path.expanduser(value))
208 result[i] = (key, os.path.expanduser(value))
209 return result
209 return result
210
210
211 def hgignorefiles(self):
211 def hgignorefiles(self):
212 result = []
212 result = []
213 for key, value in self.configitems("ui"):
213 for key, value in self.configitems("ui"):
214 if key == 'ignore' or key.startswith('ignore.'):
214 if key == 'ignore' or key.startswith('ignore.'):
215 result.append(os.path.expanduser(value))
215 result.append(os.path.expanduser(value))
216 return result
216 return result
217
217
218 def configrevlog(self):
218 def configrevlog(self):
219 result = {}
219 result = {}
220 for key, value in self.configitems("revlog"):
220 for key, value in self.configitems("revlog"):
221 result[key.lower()] = value
221 result[key.lower()] = value
222 return result
222 return result
223
223
224 def username(self):
224 def username(self):
225 """Return default username to be used in commits.
225 """Return default username to be used in commits.
226
226
227 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
227 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
228 and stop searching if one of these is set.
228 and stop searching if one of these is set.
229 Abort if found username is an empty string to force specifying
229 Abort if no username is found, to force specifying the commit user
230 the commit user elsewhere, e.g. with line option or repo hgrc.
230 with line option or repo hgrc.
231 If not found, use ($LOGNAME or $USER or $LNAME or
232 $USERNAME) +"@full.hostname".
233 """
231 """
234 user = os.environ.get("HGUSER")
232 user = os.environ.get("HGUSER")
235 if user is None:
233 if user is None:
236 user = self.config("ui", "username")
234 user = self.config("ui", "username")
237 if user is None:
235 if user is None:
238 user = os.environ.get("EMAIL")
236 user = os.environ.get("EMAIL")
239 if user is None:
237 if user is None:
240 try:
238 raise util.Abort(_("No default username available, use -u"))
241 user = '%s@%s' % (util.getuser(), socket.getfqdn())
242 except KeyError:
243 raise util.Abort(_("Please specify a username."))
244 return user
239 return user
245
240
246 def shortuser(self, user):
241 def shortuser(self, user):
247 """Return a short representation of a user name or email address."""
242 """Return a short representation of a user name or email address."""
248 if not self.verbose: user = util.shortuser(user)
243 if not self.verbose: user = util.shortuser(user)
249 return user
244 return user
250
245
251 def expandpath(self, loc, default=None):
246 def expandpath(self, loc, default=None):
252 """Return repository location relative to cwd or from [paths]"""
247 """Return repository location relative to cwd or from [paths]"""
253 if "://" in loc or os.path.isdir(loc):
248 if "://" in loc or os.path.isdir(loc):
254 return loc
249 return loc
255
250
256 path = self.config("paths", loc)
251 path = self.config("paths", loc)
257 if not path and default is not None:
252 if not path and default is not None:
258 path = self.config("paths", default)
253 path = self.config("paths", default)
259 return path or loc
254 return path or loc
260
255
261 def write(self, *args):
256 def write(self, *args):
262 if self.header:
257 if self.header:
263 if self.header != self.prev_header:
258 if self.header != self.prev_header:
264 self.prev_header = self.header
259 self.prev_header = self.header
265 self.write(*self.header)
260 self.write(*self.header)
266 self.header = []
261 self.header = []
267 for a in args:
262 for a in args:
268 sys.stdout.write(str(a))
263 sys.stdout.write(str(a))
269
264
270 def write_header(self, *args):
265 def write_header(self, *args):
271 for a in args:
266 for a in args:
272 self.header.append(str(a))
267 self.header.append(str(a))
273
268
274 def write_err(self, *args):
269 def write_err(self, *args):
275 try:
270 try:
276 if not sys.stdout.closed: sys.stdout.flush()
271 if not sys.stdout.closed: sys.stdout.flush()
277 for a in args:
272 for a in args:
278 sys.stderr.write(str(a))
273 sys.stderr.write(str(a))
279 except IOError, inst:
274 except IOError, inst:
280 if inst.errno != errno.EPIPE:
275 if inst.errno != errno.EPIPE:
281 raise
276 raise
282
277
283 def flush(self):
278 def flush(self):
284 try: sys.stdout.flush()
279 try: sys.stdout.flush()
285 except: pass
280 except: pass
286 try: sys.stderr.flush()
281 try: sys.stderr.flush()
287 except: pass
282 except: pass
288
283
289 def readline(self):
284 def readline(self):
290 return sys.stdin.readline()[:-1]
285 return sys.stdin.readline()[:-1]
291 def prompt(self, msg, pat=None, default="y"):
286 def prompt(self, msg, pat=None, default="y"):
292 if not self.interactive: return default
287 if not self.interactive: return default
293 while 1:
288 while 1:
294 self.write(msg, " ")
289 self.write(msg, " ")
295 r = self.readline()
290 r = self.readline()
296 if not pat or re.match(pat, r):
291 if not pat or re.match(pat, r):
297 return r
292 return r
298 else:
293 else:
299 self.write(_("unrecognized response\n"))
294 self.write(_("unrecognized response\n"))
300 def getpass(self, prompt=None, default=None):
295 def getpass(self, prompt=None, default=None):
301 if not self.interactive: return default
296 if not self.interactive: return default
302 return getpass.getpass(prompt or _('password: '))
297 return getpass.getpass(prompt or _('password: '))
303 def status(self, *msg):
298 def status(self, *msg):
304 if not self.quiet: self.write(*msg)
299 if not self.quiet: self.write(*msg)
305 def warn(self, *msg):
300 def warn(self, *msg):
306 self.write_err(*msg)
301 self.write_err(*msg)
307 def note(self, *msg):
302 def note(self, *msg):
308 if self.verbose: self.write(*msg)
303 if self.verbose: self.write(*msg)
309 def debug(self, *msg):
304 def debug(self, *msg):
310 if self.debugflag: self.write(*msg)
305 if self.debugflag: self.write(*msg)
311 def edit(self, text, user):
306 def edit(self, text, user):
312 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
307 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
313 text=True)
308 text=True)
314 try:
309 try:
315 f = os.fdopen(fd, "w")
310 f = os.fdopen(fd, "w")
316 f.write(text)
311 f.write(text)
317 f.close()
312 f.close()
318
313
319 editor = (os.environ.get("HGEDITOR") or
314 editor = (os.environ.get("HGEDITOR") or
320 self.config("ui", "editor") or
315 self.config("ui", "editor") or
321 os.environ.get("EDITOR", "vi"))
316 os.environ.get("EDITOR", "vi"))
322
317
323 util.system("%s \"%s\"" % (editor, name),
318 util.system("%s \"%s\"" % (editor, name),
324 environ={'HGUSER': user},
319 environ={'HGUSER': user},
325 onerr=util.Abort, errprefix=_("edit failed"))
320 onerr=util.Abort, errprefix=_("edit failed"))
326
321
327 f = open(name)
322 f = open(name)
328 t = f.read()
323 t = f.read()
329 f.close()
324 f.close()
330 t = re.sub("(?m)^HG:.*\n", "", t)
325 t = re.sub("(?m)^HG:.*\n", "", t)
331 finally:
326 finally:
332 os.unlink(name)
327 os.unlink(name)
333
328
334 return t
329 return t
335
330
336 def print_exc(self):
331 def print_exc(self):
337 '''print exception traceback if traceback printing enabled.
332 '''print exception traceback if traceback printing enabled.
338 only to call in exception handler. returns true if traceback
333 only to call in exception handler. returns true if traceback
339 printed.'''
334 printed.'''
340 if self.traceback:
335 if self.traceback:
341 traceback.print_exc()
336 traceback.print_exc()
342 return self.traceback
337 return self.traceback
@@ -1,1035 +1,1021
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import gettext as _
15 from i18n import gettext as _
16 from demandload import *
16 from demandload import *
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 demandload(globals(), "os threading time calendar ConfigParser")
18 demandload(globals(), "os threading time calendar ConfigParser")
19
19
20 # used by parsedate
20 # used by parsedate
21 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
21 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
22 '%a %b %d %H:%M:%S %Y')
22 '%a %b %d %H:%M:%S %Y')
23
23
24 class SignalInterrupt(Exception):
24 class SignalInterrupt(Exception):
25 """Exception raised on SIGTERM and SIGHUP."""
25 """Exception raised on SIGTERM and SIGHUP."""
26
26
27 # like SafeConfigParser but with case-sensitive keys
27 # like SafeConfigParser but with case-sensitive keys
28 class configparser(ConfigParser.SafeConfigParser):
28 class configparser(ConfigParser.SafeConfigParser):
29 def optionxform(self, optionstr):
29 def optionxform(self, optionstr):
30 return optionstr
30 return optionstr
31
31
32 def cachefunc(func):
32 def cachefunc(func):
33 '''cache the result of function calls'''
33 '''cache the result of function calls'''
34 # XXX doesn't handle keywords args
34 # XXX doesn't handle keywords args
35 cache = {}
35 cache = {}
36 if func.func_code.co_argcount == 1:
36 if func.func_code.co_argcount == 1:
37 # we gain a small amount of time because
37 # we gain a small amount of time because
38 # we don't need to pack/unpack the list
38 # we don't need to pack/unpack the list
39 def f(arg):
39 def f(arg):
40 if arg not in cache:
40 if arg not in cache:
41 cache[arg] = func(arg)
41 cache[arg] = func(arg)
42 return cache[arg]
42 return cache[arg]
43 else:
43 else:
44 def f(*args):
44 def f(*args):
45 if args not in cache:
45 if args not in cache:
46 cache[args] = func(*args)
46 cache[args] = func(*args)
47 return cache[args]
47 return cache[args]
48
48
49 return f
49 return f
50
50
51 def pipefilter(s, cmd):
51 def pipefilter(s, cmd):
52 '''filter string S through command CMD, returning its output'''
52 '''filter string S through command CMD, returning its output'''
53 (pout, pin) = popen2.popen2(cmd, -1, 'b')
53 (pout, pin) = popen2.popen2(cmd, -1, 'b')
54 def writer():
54 def writer():
55 try:
55 try:
56 pin.write(s)
56 pin.write(s)
57 pin.close()
57 pin.close()
58 except IOError, inst:
58 except IOError, inst:
59 if inst.errno != errno.EPIPE:
59 if inst.errno != errno.EPIPE:
60 raise
60 raise
61
61
62 # we should use select instead on UNIX, but this will work on most
62 # we should use select instead on UNIX, but this will work on most
63 # systems, including Windows
63 # systems, including Windows
64 w = threading.Thread(target=writer)
64 w = threading.Thread(target=writer)
65 w.start()
65 w.start()
66 f = pout.read()
66 f = pout.read()
67 pout.close()
67 pout.close()
68 w.join()
68 w.join()
69 return f
69 return f
70
70
71 def tempfilter(s, cmd):
71 def tempfilter(s, cmd):
72 '''filter string S through a pair of temporary files with CMD.
72 '''filter string S through a pair of temporary files with CMD.
73 CMD is used as a template to create the real command to be run,
73 CMD is used as a template to create the real command to be run,
74 with the strings INFILE and OUTFILE replaced by the real names of
74 with the strings INFILE and OUTFILE replaced by the real names of
75 the temporary files generated.'''
75 the temporary files generated.'''
76 inname, outname = None, None
76 inname, outname = None, None
77 try:
77 try:
78 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
78 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
79 fp = os.fdopen(infd, 'wb')
79 fp = os.fdopen(infd, 'wb')
80 fp.write(s)
80 fp.write(s)
81 fp.close()
81 fp.close()
82 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
82 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
83 os.close(outfd)
83 os.close(outfd)
84 cmd = cmd.replace('INFILE', inname)
84 cmd = cmd.replace('INFILE', inname)
85 cmd = cmd.replace('OUTFILE', outname)
85 cmd = cmd.replace('OUTFILE', outname)
86 code = os.system(cmd)
86 code = os.system(cmd)
87 if code: raise Abort(_("command '%s' failed: %s") %
87 if code: raise Abort(_("command '%s' failed: %s") %
88 (cmd, explain_exit(code)))
88 (cmd, explain_exit(code)))
89 return open(outname, 'rb').read()
89 return open(outname, 'rb').read()
90 finally:
90 finally:
91 try:
91 try:
92 if inname: os.unlink(inname)
92 if inname: os.unlink(inname)
93 except: pass
93 except: pass
94 try:
94 try:
95 if outname: os.unlink(outname)
95 if outname: os.unlink(outname)
96 except: pass
96 except: pass
97
97
98 filtertable = {
98 filtertable = {
99 'tempfile:': tempfilter,
99 'tempfile:': tempfilter,
100 'pipe:': pipefilter,
100 'pipe:': pipefilter,
101 }
101 }
102
102
103 def filter(s, cmd):
103 def filter(s, cmd):
104 "filter a string through a command that transforms its input to its output"
104 "filter a string through a command that transforms its input to its output"
105 for name, fn in filtertable.iteritems():
105 for name, fn in filtertable.iteritems():
106 if cmd.startswith(name):
106 if cmd.startswith(name):
107 return fn(s, cmd[len(name):].lstrip())
107 return fn(s, cmd[len(name):].lstrip())
108 return pipefilter(s, cmd)
108 return pipefilter(s, cmd)
109
109
110 def find_in_path(name, path, default=None):
110 def find_in_path(name, path, default=None):
111 '''find name in search path. path can be string (will be split
111 '''find name in search path. path can be string (will be split
112 with os.pathsep), or iterable thing that returns strings. if name
112 with os.pathsep), or iterable thing that returns strings. if name
113 found, return path to name. else return default.'''
113 found, return path to name. else return default.'''
114 if isinstance(path, str):
114 if isinstance(path, str):
115 path = path.split(os.pathsep)
115 path = path.split(os.pathsep)
116 for p in path:
116 for p in path:
117 p_name = os.path.join(p, name)
117 p_name = os.path.join(p, name)
118 if os.path.exists(p_name):
118 if os.path.exists(p_name):
119 return p_name
119 return p_name
120 return default
120 return default
121
121
122 def binary(s):
122 def binary(s):
123 """return true if a string is binary data using diff's heuristic"""
123 """return true if a string is binary data using diff's heuristic"""
124 if s and '\0' in s[:4096]:
124 if s and '\0' in s[:4096]:
125 return True
125 return True
126 return False
126 return False
127
127
128 def unique(g):
128 def unique(g):
129 """return the uniq elements of iterable g"""
129 """return the uniq elements of iterable g"""
130 seen = {}
130 seen = {}
131 for f in g:
131 for f in g:
132 if f not in seen:
132 if f not in seen:
133 seen[f] = 1
133 seen[f] = 1
134 yield f
134 yield f
135
135
136 class Abort(Exception):
136 class Abort(Exception):
137 """Raised if a command needs to print an error and exit."""
137 """Raised if a command needs to print an error and exit."""
138
138
139 def always(fn): return True
139 def always(fn): return True
140 def never(fn): return False
140 def never(fn): return False
141
141
142 def patkind(name, dflt_pat='glob'):
142 def patkind(name, dflt_pat='glob'):
143 """Split a string into an optional pattern kind prefix and the
143 """Split a string into an optional pattern kind prefix and the
144 actual pattern."""
144 actual pattern."""
145 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
145 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
146 if name.startswith(prefix + ':'): return name.split(':', 1)
146 if name.startswith(prefix + ':'): return name.split(':', 1)
147 return dflt_pat, name
147 return dflt_pat, name
148
148
149 def globre(pat, head='^', tail='$'):
149 def globre(pat, head='^', tail='$'):
150 "convert a glob pattern into a regexp"
150 "convert a glob pattern into a regexp"
151 i, n = 0, len(pat)
151 i, n = 0, len(pat)
152 res = ''
152 res = ''
153 group = False
153 group = False
154 def peek(): return i < n and pat[i]
154 def peek(): return i < n and pat[i]
155 while i < n:
155 while i < n:
156 c = pat[i]
156 c = pat[i]
157 i = i+1
157 i = i+1
158 if c == '*':
158 if c == '*':
159 if peek() == '*':
159 if peek() == '*':
160 i += 1
160 i += 1
161 res += '.*'
161 res += '.*'
162 else:
162 else:
163 res += '[^/]*'
163 res += '[^/]*'
164 elif c == '?':
164 elif c == '?':
165 res += '.'
165 res += '.'
166 elif c == '[':
166 elif c == '[':
167 j = i
167 j = i
168 if j < n and pat[j] in '!]':
168 if j < n and pat[j] in '!]':
169 j += 1
169 j += 1
170 while j < n and pat[j] != ']':
170 while j < n and pat[j] != ']':
171 j += 1
171 j += 1
172 if j >= n:
172 if j >= n:
173 res += '\\['
173 res += '\\['
174 else:
174 else:
175 stuff = pat[i:j].replace('\\','\\\\')
175 stuff = pat[i:j].replace('\\','\\\\')
176 i = j + 1
176 i = j + 1
177 if stuff[0] == '!':
177 if stuff[0] == '!':
178 stuff = '^' + stuff[1:]
178 stuff = '^' + stuff[1:]
179 elif stuff[0] == '^':
179 elif stuff[0] == '^':
180 stuff = '\\' + stuff
180 stuff = '\\' + stuff
181 res = '%s[%s]' % (res, stuff)
181 res = '%s[%s]' % (res, stuff)
182 elif c == '{':
182 elif c == '{':
183 group = True
183 group = True
184 res += '(?:'
184 res += '(?:'
185 elif c == '}' and group:
185 elif c == '}' and group:
186 res += ')'
186 res += ')'
187 group = False
187 group = False
188 elif c == ',' and group:
188 elif c == ',' and group:
189 res += '|'
189 res += '|'
190 elif c == '\\':
190 elif c == '\\':
191 p = peek()
191 p = peek()
192 if p:
192 if p:
193 i += 1
193 i += 1
194 res += re.escape(p)
194 res += re.escape(p)
195 else:
195 else:
196 res += re.escape(c)
196 res += re.escape(c)
197 else:
197 else:
198 res += re.escape(c)
198 res += re.escape(c)
199 return head + res + tail
199 return head + res + tail
200
200
201 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
201 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
202
202
203 def pathto(n1, n2):
203 def pathto(n1, n2):
204 '''return the relative path from one place to another.
204 '''return the relative path from one place to another.
205 this returns a path in the form used by the local filesystem, not hg.'''
205 this returns a path in the form used by the local filesystem, not hg.'''
206 if not n1: return localpath(n2)
206 if not n1: return localpath(n2)
207 a, b = n1.split('/'), n2.split('/')
207 a, b = n1.split('/'), n2.split('/')
208 a.reverse()
208 a.reverse()
209 b.reverse()
209 b.reverse()
210 while a and b and a[-1] == b[-1]:
210 while a and b and a[-1] == b[-1]:
211 a.pop()
211 a.pop()
212 b.pop()
212 b.pop()
213 b.reverse()
213 b.reverse()
214 return os.sep.join((['..'] * len(a)) + b)
214 return os.sep.join((['..'] * len(a)) + b)
215
215
216 def canonpath(root, cwd, myname):
216 def canonpath(root, cwd, myname):
217 """return the canonical path of myname, given cwd and root"""
217 """return the canonical path of myname, given cwd and root"""
218 if root == os.sep:
218 if root == os.sep:
219 rootsep = os.sep
219 rootsep = os.sep
220 elif root.endswith(os.sep):
220 elif root.endswith(os.sep):
221 rootsep = root
221 rootsep = root
222 else:
222 else:
223 rootsep = root + os.sep
223 rootsep = root + os.sep
224 name = myname
224 name = myname
225 if not os.path.isabs(name):
225 if not os.path.isabs(name):
226 name = os.path.join(root, cwd, name)
226 name = os.path.join(root, cwd, name)
227 name = os.path.normpath(name)
227 name = os.path.normpath(name)
228 if name != rootsep and name.startswith(rootsep):
228 if name != rootsep and name.startswith(rootsep):
229 name = name[len(rootsep):]
229 name = name[len(rootsep):]
230 audit_path(name)
230 audit_path(name)
231 return pconvert(name)
231 return pconvert(name)
232 elif name == root:
232 elif name == root:
233 return ''
233 return ''
234 else:
234 else:
235 # Determine whether `name' is in the hierarchy at or beneath `root',
235 # Determine whether `name' is in the hierarchy at or beneath `root',
236 # by iterating name=dirname(name) until that causes no change (can't
236 # by iterating name=dirname(name) until that causes no change (can't
237 # check name == '/', because that doesn't work on windows). For each
237 # check name == '/', because that doesn't work on windows). For each
238 # `name', compare dev/inode numbers. If they match, the list `rel'
238 # `name', compare dev/inode numbers. If they match, the list `rel'
239 # holds the reversed list of components making up the relative file
239 # holds the reversed list of components making up the relative file
240 # name we want.
240 # name we want.
241 root_st = os.stat(root)
241 root_st = os.stat(root)
242 rel = []
242 rel = []
243 while True:
243 while True:
244 try:
244 try:
245 name_st = os.stat(name)
245 name_st = os.stat(name)
246 except OSError:
246 except OSError:
247 break
247 break
248 if samestat(name_st, root_st):
248 if samestat(name_st, root_st):
249 rel.reverse()
249 rel.reverse()
250 name = os.path.join(*rel)
250 name = os.path.join(*rel)
251 audit_path(name)
251 audit_path(name)
252 return pconvert(name)
252 return pconvert(name)
253 dirname, basename = os.path.split(name)
253 dirname, basename = os.path.split(name)
254 rel.append(basename)
254 rel.append(basename)
255 if dirname == name:
255 if dirname == name:
256 break
256 break
257 name = dirname
257 name = dirname
258
258
259 raise Abort('%s not under root' % myname)
259 raise Abort('%s not under root' % myname)
260
260
261 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
261 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
262 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
262 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
263
263
264 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
264 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
265 if os.name == 'nt':
265 if os.name == 'nt':
266 dflt_pat = 'glob'
266 dflt_pat = 'glob'
267 else:
267 else:
268 dflt_pat = 'relpath'
268 dflt_pat = 'relpath'
269 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
269 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
270
270
271 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
271 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
272 """build a function to match a set of file patterns
272 """build a function to match a set of file patterns
273
273
274 arguments:
274 arguments:
275 canonroot - the canonical root of the tree you're matching against
275 canonroot - the canonical root of the tree you're matching against
276 cwd - the current working directory, if relevant
276 cwd - the current working directory, if relevant
277 names - patterns to find
277 names - patterns to find
278 inc - patterns to include
278 inc - patterns to include
279 exc - patterns to exclude
279 exc - patterns to exclude
280 head - a regex to prepend to patterns to control whether a match is rooted
280 head - a regex to prepend to patterns to control whether a match is rooted
281
281
282 a pattern is one of:
282 a pattern is one of:
283 'glob:<rooted glob>'
283 'glob:<rooted glob>'
284 're:<rooted regexp>'
284 're:<rooted regexp>'
285 'path:<rooted path>'
285 'path:<rooted path>'
286 'relglob:<relative glob>'
286 'relglob:<relative glob>'
287 'relpath:<relative path>'
287 'relpath:<relative path>'
288 'relre:<relative regexp>'
288 'relre:<relative regexp>'
289 '<rooted path or regexp>'
289 '<rooted path or regexp>'
290
290
291 returns:
291 returns:
292 a 3-tuple containing
292 a 3-tuple containing
293 - list of explicit non-pattern names passed in
293 - list of explicit non-pattern names passed in
294 - a bool match(filename) function
294 - a bool match(filename) function
295 - a bool indicating if any patterns were passed in
295 - a bool indicating if any patterns were passed in
296
296
297 todo:
297 todo:
298 make head regex a rooted bool
298 make head regex a rooted bool
299 """
299 """
300
300
301 def contains_glob(name):
301 def contains_glob(name):
302 for c in name:
302 for c in name:
303 if c in _globchars: return True
303 if c in _globchars: return True
304 return False
304 return False
305
305
306 def regex(kind, name, tail):
306 def regex(kind, name, tail):
307 '''convert a pattern into a regular expression'''
307 '''convert a pattern into a regular expression'''
308 if kind == 're':
308 if kind == 're':
309 return name
309 return name
310 elif kind == 'path':
310 elif kind == 'path':
311 return '^' + re.escape(name) + '(?:/|$)'
311 return '^' + re.escape(name) + '(?:/|$)'
312 elif kind == 'relglob':
312 elif kind == 'relglob':
313 return head + globre(name, '(?:|.*/)', tail)
313 return head + globre(name, '(?:|.*/)', tail)
314 elif kind == 'relpath':
314 elif kind == 'relpath':
315 return head + re.escape(name) + tail
315 return head + re.escape(name) + tail
316 elif kind == 'relre':
316 elif kind == 'relre':
317 if name.startswith('^'):
317 if name.startswith('^'):
318 return name
318 return name
319 return '.*' + name
319 return '.*' + name
320 return head + globre(name, '', tail)
320 return head + globre(name, '', tail)
321
321
322 def matchfn(pats, tail):
322 def matchfn(pats, tail):
323 """build a matching function from a set of patterns"""
323 """build a matching function from a set of patterns"""
324 if not pats:
324 if not pats:
325 return
325 return
326 matches = []
326 matches = []
327 for k, p in pats:
327 for k, p in pats:
328 try:
328 try:
329 pat = '(?:%s)' % regex(k, p, tail)
329 pat = '(?:%s)' % regex(k, p, tail)
330 matches.append(re.compile(pat).match)
330 matches.append(re.compile(pat).match)
331 except re.error:
331 except re.error:
332 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
332 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
333 else: raise Abort("invalid pattern (%s): %s" % (k, p))
333 else: raise Abort("invalid pattern (%s): %s" % (k, p))
334
334
335 def buildfn(text):
335 def buildfn(text):
336 for m in matches:
336 for m in matches:
337 r = m(text)
337 r = m(text)
338 if r:
338 if r:
339 return r
339 return r
340
340
341 return buildfn
341 return buildfn
342
342
343 def globprefix(pat):
343 def globprefix(pat):
344 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
344 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
345 root = []
345 root = []
346 for p in pat.split(os.sep):
346 for p in pat.split(os.sep):
347 if contains_glob(p): break
347 if contains_glob(p): break
348 root.append(p)
348 root.append(p)
349 return '/'.join(root)
349 return '/'.join(root)
350
350
351 pats = []
351 pats = []
352 files = []
352 files = []
353 roots = []
353 roots = []
354 for kind, name in [patkind(p, dflt_pat) for p in names]:
354 for kind, name in [patkind(p, dflt_pat) for p in names]:
355 if kind in ('glob', 'relpath'):
355 if kind in ('glob', 'relpath'):
356 name = canonpath(canonroot, cwd, name)
356 name = canonpath(canonroot, cwd, name)
357 if name == '':
357 if name == '':
358 kind, name = 'glob', '**'
358 kind, name = 'glob', '**'
359 if kind in ('glob', 'path', 're'):
359 if kind in ('glob', 'path', 're'):
360 pats.append((kind, name))
360 pats.append((kind, name))
361 if kind == 'glob':
361 if kind == 'glob':
362 root = globprefix(name)
362 root = globprefix(name)
363 if root: roots.append(root)
363 if root: roots.append(root)
364 elif kind == 'relpath':
364 elif kind == 'relpath':
365 files.append((kind, name))
365 files.append((kind, name))
366 roots.append(name)
366 roots.append(name)
367
367
368 patmatch = matchfn(pats, '$') or always
368 patmatch = matchfn(pats, '$') or always
369 filematch = matchfn(files, '(?:/|$)') or always
369 filematch = matchfn(files, '(?:/|$)') or always
370 incmatch = always
370 incmatch = always
371 if inc:
371 if inc:
372 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
372 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
373 incmatch = matchfn(inckinds, '(?:/|$)')
373 incmatch = matchfn(inckinds, '(?:/|$)')
374 excmatch = lambda fn: False
374 excmatch = lambda fn: False
375 if exc:
375 if exc:
376 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
376 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
377 excmatch = matchfn(exckinds, '(?:/|$)')
377 excmatch = matchfn(exckinds, '(?:/|$)')
378
378
379 return (roots,
379 return (roots,
380 lambda fn: (incmatch(fn) and not excmatch(fn) and
380 lambda fn: (incmatch(fn) and not excmatch(fn) and
381 (fn.endswith('/') or
381 (fn.endswith('/') or
382 (not pats and not files) or
382 (not pats and not files) or
383 (pats and patmatch(fn)) or
383 (pats and patmatch(fn)) or
384 (files and filematch(fn)))),
384 (files and filematch(fn)))),
385 (inc or exc or (pats and pats != [('glob', '**')])) and True)
385 (inc or exc or (pats and pats != [('glob', '**')])) and True)
386
386
387 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
387 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
388 '''enhanced shell command execution.
388 '''enhanced shell command execution.
389 run with environment maybe modified, maybe in different dir.
389 run with environment maybe modified, maybe in different dir.
390
390
391 if command fails and onerr is None, return status. if ui object,
391 if command fails and onerr is None, return status. if ui object,
392 print error message and return status, else raise onerr object as
392 print error message and return status, else raise onerr object as
393 exception.'''
393 exception.'''
394 def py2shell(val):
394 def py2shell(val):
395 'convert python object into string that is useful to shell'
395 'convert python object into string that is useful to shell'
396 if val in (None, False):
396 if val in (None, False):
397 return '0'
397 return '0'
398 if val == True:
398 if val == True:
399 return '1'
399 return '1'
400 return str(val)
400 return str(val)
401 oldenv = {}
401 oldenv = {}
402 for k in environ:
402 for k in environ:
403 oldenv[k] = os.environ.get(k)
403 oldenv[k] = os.environ.get(k)
404 if cwd is not None:
404 if cwd is not None:
405 oldcwd = os.getcwd()
405 oldcwd = os.getcwd()
406 try:
406 try:
407 for k, v in environ.iteritems():
407 for k, v in environ.iteritems():
408 os.environ[k] = py2shell(v)
408 os.environ[k] = py2shell(v)
409 if cwd is not None and oldcwd != cwd:
409 if cwd is not None and oldcwd != cwd:
410 os.chdir(cwd)
410 os.chdir(cwd)
411 rc = os.system(cmd)
411 rc = os.system(cmd)
412 if rc and onerr:
412 if rc and onerr:
413 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
413 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
414 explain_exit(rc)[0])
414 explain_exit(rc)[0])
415 if errprefix:
415 if errprefix:
416 errmsg = '%s: %s' % (errprefix, errmsg)
416 errmsg = '%s: %s' % (errprefix, errmsg)
417 try:
417 try:
418 onerr.warn(errmsg + '\n')
418 onerr.warn(errmsg + '\n')
419 except AttributeError:
419 except AttributeError:
420 raise onerr(errmsg)
420 raise onerr(errmsg)
421 return rc
421 return rc
422 finally:
422 finally:
423 for k, v in oldenv.iteritems():
423 for k, v in oldenv.iteritems():
424 if v is None:
424 if v is None:
425 del os.environ[k]
425 del os.environ[k]
426 else:
426 else:
427 os.environ[k] = v
427 os.environ[k] = v
428 if cwd is not None and oldcwd != cwd:
428 if cwd is not None and oldcwd != cwd:
429 os.chdir(oldcwd)
429 os.chdir(oldcwd)
430
430
431 def rename(src, dst):
431 def rename(src, dst):
432 """forcibly rename a file"""
432 """forcibly rename a file"""
433 try:
433 try:
434 os.rename(src, dst)
434 os.rename(src, dst)
435 except OSError, err:
435 except OSError, err:
436 # on windows, rename to existing file is not allowed, so we
436 # on windows, rename to existing file is not allowed, so we
437 # must delete destination first. but if file is open, unlink
437 # must delete destination first. but if file is open, unlink
438 # schedules it for delete but does not delete it. rename
438 # schedules it for delete but does not delete it. rename
439 # happens immediately even for open files, so we create
439 # happens immediately even for open files, so we create
440 # temporary file, delete it, rename destination to that name,
440 # temporary file, delete it, rename destination to that name,
441 # then delete that. then rename is safe to do.
441 # then delete that. then rename is safe to do.
442 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
442 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
443 os.close(fd)
443 os.close(fd)
444 os.unlink(temp)
444 os.unlink(temp)
445 os.rename(dst, temp)
445 os.rename(dst, temp)
446 os.unlink(temp)
446 os.unlink(temp)
447 os.rename(src, dst)
447 os.rename(src, dst)
448
448
449 def unlink(f):
449 def unlink(f):
450 """unlink and remove the directory if it is empty"""
450 """unlink and remove the directory if it is empty"""
451 os.unlink(f)
451 os.unlink(f)
452 # try removing directories that might now be empty
452 # try removing directories that might now be empty
453 try:
453 try:
454 os.removedirs(os.path.dirname(f))
454 os.removedirs(os.path.dirname(f))
455 except OSError:
455 except OSError:
456 pass
456 pass
457
457
458 def copyfiles(src, dst, hardlink=None):
458 def copyfiles(src, dst, hardlink=None):
459 """Copy a directory tree using hardlinks if possible"""
459 """Copy a directory tree using hardlinks if possible"""
460
460
461 if hardlink is None:
461 if hardlink is None:
462 hardlink = (os.stat(src).st_dev ==
462 hardlink = (os.stat(src).st_dev ==
463 os.stat(os.path.dirname(dst)).st_dev)
463 os.stat(os.path.dirname(dst)).st_dev)
464
464
465 if os.path.isdir(src):
465 if os.path.isdir(src):
466 os.mkdir(dst)
466 os.mkdir(dst)
467 for name in os.listdir(src):
467 for name in os.listdir(src):
468 srcname = os.path.join(src, name)
468 srcname = os.path.join(src, name)
469 dstname = os.path.join(dst, name)
469 dstname = os.path.join(dst, name)
470 copyfiles(srcname, dstname, hardlink)
470 copyfiles(srcname, dstname, hardlink)
471 else:
471 else:
472 if hardlink:
472 if hardlink:
473 try:
473 try:
474 os_link(src, dst)
474 os_link(src, dst)
475 except (IOError, OSError):
475 except (IOError, OSError):
476 hardlink = False
476 hardlink = False
477 shutil.copy(src, dst)
477 shutil.copy(src, dst)
478 else:
478 else:
479 shutil.copy(src, dst)
479 shutil.copy(src, dst)
480
480
481 def audit_path(path):
481 def audit_path(path):
482 """Abort if path contains dangerous components"""
482 """Abort if path contains dangerous components"""
483 parts = os.path.normcase(path).split(os.sep)
483 parts = os.path.normcase(path).split(os.sep)
484 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
484 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
485 or os.pardir in parts):
485 or os.pardir in parts):
486 raise Abort(_("path contains illegal component: %s\n") % path)
486 raise Abort(_("path contains illegal component: %s\n") % path)
487
487
488 def _makelock_file(info, pathname):
488 def _makelock_file(info, pathname):
489 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
489 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
490 os.write(ld, info)
490 os.write(ld, info)
491 os.close(ld)
491 os.close(ld)
492
492
493 def _readlock_file(pathname):
493 def _readlock_file(pathname):
494 return posixfile(pathname).read()
494 return posixfile(pathname).read()
495
495
496 def nlinks(pathname):
496 def nlinks(pathname):
497 """Return number of hardlinks for the given file."""
497 """Return number of hardlinks for the given file."""
498 return os.lstat(pathname).st_nlink
498 return os.lstat(pathname).st_nlink
499
499
500 if hasattr(os, 'link'):
500 if hasattr(os, 'link'):
501 os_link = os.link
501 os_link = os.link
502 else:
502 else:
503 def os_link(src, dst):
503 def os_link(src, dst):
504 raise OSError(0, _("Hardlinks not supported"))
504 raise OSError(0, _("Hardlinks not supported"))
505
505
506 def fstat(fp):
506 def fstat(fp):
507 '''stat file object that may not have fileno method.'''
507 '''stat file object that may not have fileno method.'''
508 try:
508 try:
509 return os.fstat(fp.fileno())
509 return os.fstat(fp.fileno())
510 except AttributeError:
510 except AttributeError:
511 return os.stat(fp.name)
511 return os.stat(fp.name)
512
512
513 posixfile = file
513 posixfile = file
514
514
515 def is_win_9x():
515 def is_win_9x():
516 '''return true if run on windows 95, 98 or me.'''
516 '''return true if run on windows 95, 98 or me.'''
517 try:
517 try:
518 return sys.getwindowsversion()[3] == 1
518 return sys.getwindowsversion()[3] == 1
519 except AttributeError:
519 except AttributeError:
520 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
520 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
521
521
522 getuser_fallback = None
523
524 def getuser():
525 '''return name of current user'''
526 try:
527 return getpass.getuser()
528 except ImportError:
529 # import of pwd will fail on windows - try fallback
530 if getuser_fallback:
531 return getuser_fallback()
532 # raised if win32api not available
533 raise Abort(_('user name not available - set USERNAME '
534 'environment variable'))
535
536 # Platform specific variants
522 # Platform specific variants
537 if os.name == 'nt':
523 if os.name == 'nt':
538 demandload(globals(), "msvcrt")
524 demandload(globals(), "msvcrt")
539 nulldev = 'NUL:'
525 nulldev = 'NUL:'
540
526
541 class winstdout:
527 class winstdout:
542 '''stdout on windows misbehaves if sent through a pipe'''
528 '''stdout on windows misbehaves if sent through a pipe'''
543
529
544 def __init__(self, fp):
530 def __init__(self, fp):
545 self.fp = fp
531 self.fp = fp
546
532
547 def __getattr__(self, key):
533 def __getattr__(self, key):
548 return getattr(self.fp, key)
534 return getattr(self.fp, key)
549
535
550 def close(self):
536 def close(self):
551 try:
537 try:
552 self.fp.close()
538 self.fp.close()
553 except: pass
539 except: pass
554
540
555 def write(self, s):
541 def write(self, s):
556 try:
542 try:
557 return self.fp.write(s)
543 return self.fp.write(s)
558 except IOError, inst:
544 except IOError, inst:
559 if inst.errno != 0: raise
545 if inst.errno != 0: raise
560 self.close()
546 self.close()
561 raise IOError(errno.EPIPE, 'Broken pipe')
547 raise IOError(errno.EPIPE, 'Broken pipe')
562
548
563 sys.stdout = winstdout(sys.stdout)
549 sys.stdout = winstdout(sys.stdout)
564
550
565 def system_rcpath():
551 def system_rcpath():
566 try:
552 try:
567 return system_rcpath_win32()
553 return system_rcpath_win32()
568 except:
554 except:
569 return [r'c:\mercurial\mercurial.ini']
555 return [r'c:\mercurial\mercurial.ini']
570
556
571 def os_rcpath():
557 def os_rcpath():
572 '''return default os-specific hgrc search path'''
558 '''return default os-specific hgrc search path'''
573 path = system_rcpath()
559 path = system_rcpath()
574 path.append(user_rcpath())
560 path.append(user_rcpath())
575 userprofile = os.environ.get('USERPROFILE')
561 userprofile = os.environ.get('USERPROFILE')
576 if userprofile:
562 if userprofile:
577 path.append(os.path.join(userprofile, 'mercurial.ini'))
563 path.append(os.path.join(userprofile, 'mercurial.ini'))
578 return path
564 return path
579
565
580 def user_rcpath():
566 def user_rcpath():
581 '''return os-specific hgrc search path to the user dir'''
567 '''return os-specific hgrc search path to the user dir'''
582 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
568 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
583
569
584 def parse_patch_output(output_line):
570 def parse_patch_output(output_line):
585 """parses the output produced by patch and returns the file name"""
571 """parses the output produced by patch and returns the file name"""
586 pf = output_line[14:]
572 pf = output_line[14:]
587 if pf[0] == '`':
573 if pf[0] == '`':
588 pf = pf[1:-1] # Remove the quotes
574 pf = pf[1:-1] # Remove the quotes
589 return pf
575 return pf
590
576
591 def testpid(pid):
577 def testpid(pid):
592 '''return False if pid dead, True if running or not known'''
578 '''return False if pid dead, True if running or not known'''
593 return True
579 return True
594
580
595 def is_exec(f, last):
581 def is_exec(f, last):
596 return last
582 return last
597
583
598 def set_exec(f, mode):
584 def set_exec(f, mode):
599 pass
585 pass
600
586
601 def set_binary(fd):
587 def set_binary(fd):
602 msvcrt.setmode(fd.fileno(), os.O_BINARY)
588 msvcrt.setmode(fd.fileno(), os.O_BINARY)
603
589
604 def pconvert(path):
590 def pconvert(path):
605 return path.replace("\\", "/")
591 return path.replace("\\", "/")
606
592
607 def localpath(path):
593 def localpath(path):
608 return path.replace('/', '\\')
594 return path.replace('/', '\\')
609
595
610 def normpath(path):
596 def normpath(path):
611 return pconvert(os.path.normpath(path))
597 return pconvert(os.path.normpath(path))
612
598
613 makelock = _makelock_file
599 makelock = _makelock_file
614 readlock = _readlock_file
600 readlock = _readlock_file
615
601
616 def samestat(s1, s2):
602 def samestat(s1, s2):
617 return False
603 return False
618
604
619 def shellquote(s):
605 def shellquote(s):
620 return '"%s"' % s.replace('"', '\\"')
606 return '"%s"' % s.replace('"', '\\"')
621
607
622 def explain_exit(code):
608 def explain_exit(code):
623 return _("exited with status %d") % code, code
609 return _("exited with status %d") % code, code
624
610
625 try:
611 try:
626 # override functions with win32 versions if possible
612 # override functions with win32 versions if possible
627 from util_win32 import *
613 from util_win32 import *
628 if not is_win_9x():
614 if not is_win_9x():
629 posixfile = posixfile_nt
615 posixfile = posixfile_nt
630 except ImportError:
616 except ImportError:
631 pass
617 pass
632
618
633 else:
619 else:
634 nulldev = '/dev/null'
620 nulldev = '/dev/null'
635
621
636 def rcfiles(path):
622 def rcfiles(path):
637 rcs = [os.path.join(path, 'hgrc')]
623 rcs = [os.path.join(path, 'hgrc')]
638 rcdir = os.path.join(path, 'hgrc.d')
624 rcdir = os.path.join(path, 'hgrc.d')
639 try:
625 try:
640 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
626 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
641 if f.endswith(".rc")])
627 if f.endswith(".rc")])
642 except OSError:
628 except OSError:
643 pass
629 pass
644 return rcs
630 return rcs
645
631
646 def os_rcpath():
632 def os_rcpath():
647 '''return default os-specific hgrc search path'''
633 '''return default os-specific hgrc search path'''
648 path = []
634 path = []
649 # old mod_python does not set sys.argv
635 # old mod_python does not set sys.argv
650 if len(getattr(sys, 'argv', [])) > 0:
636 if len(getattr(sys, 'argv', [])) > 0:
651 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
637 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
652 '/../etc/mercurial'))
638 '/../etc/mercurial'))
653 path.extend(rcfiles('/etc/mercurial'))
639 path.extend(rcfiles('/etc/mercurial'))
654 path.append(os.path.expanduser('~/.hgrc'))
640 path.append(os.path.expanduser('~/.hgrc'))
655 path = [os.path.normpath(f) for f in path]
641 path = [os.path.normpath(f) for f in path]
656 return path
642 return path
657
643
658 def parse_patch_output(output_line):
644 def parse_patch_output(output_line):
659 """parses the output produced by patch and returns the file name"""
645 """parses the output produced by patch and returns the file name"""
660 pf = output_line[14:]
646 pf = output_line[14:]
661 if pf.startswith("'") and pf.endswith("'") and " " in pf:
647 if pf.startswith("'") and pf.endswith("'") and " " in pf:
662 pf = pf[1:-1] # Remove the quotes
648 pf = pf[1:-1] # Remove the quotes
663 return pf
649 return pf
664
650
665 def is_exec(f, last):
651 def is_exec(f, last):
666 """check whether a file is executable"""
652 """check whether a file is executable"""
667 return (os.lstat(f).st_mode & 0100 != 0)
653 return (os.lstat(f).st_mode & 0100 != 0)
668
654
669 def set_exec(f, mode):
655 def set_exec(f, mode):
670 s = os.lstat(f).st_mode
656 s = os.lstat(f).st_mode
671 if (s & 0100 != 0) == mode:
657 if (s & 0100 != 0) == mode:
672 return
658 return
673 if mode:
659 if mode:
674 # Turn on +x for every +r bit when making a file executable
660 # Turn on +x for every +r bit when making a file executable
675 # and obey umask.
661 # and obey umask.
676 umask = os.umask(0)
662 umask = os.umask(0)
677 os.umask(umask)
663 os.umask(umask)
678 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
664 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
679 else:
665 else:
680 os.chmod(f, s & 0666)
666 os.chmod(f, s & 0666)
681
667
682 def set_binary(fd):
668 def set_binary(fd):
683 pass
669 pass
684
670
685 def pconvert(path):
671 def pconvert(path):
686 return path
672 return path
687
673
688 def localpath(path):
674 def localpath(path):
689 return path
675 return path
690
676
691 normpath = os.path.normpath
677 normpath = os.path.normpath
692 samestat = os.path.samestat
678 samestat = os.path.samestat
693
679
694 def makelock(info, pathname):
680 def makelock(info, pathname):
695 try:
681 try:
696 os.symlink(info, pathname)
682 os.symlink(info, pathname)
697 except OSError, why:
683 except OSError, why:
698 if why.errno == errno.EEXIST:
684 if why.errno == errno.EEXIST:
699 raise
685 raise
700 else:
686 else:
701 _makelock_file(info, pathname)
687 _makelock_file(info, pathname)
702
688
703 def readlock(pathname):
689 def readlock(pathname):
704 try:
690 try:
705 return os.readlink(pathname)
691 return os.readlink(pathname)
706 except OSError, why:
692 except OSError, why:
707 if why.errno == errno.EINVAL:
693 if why.errno == errno.EINVAL:
708 return _readlock_file(pathname)
694 return _readlock_file(pathname)
709 else:
695 else:
710 raise
696 raise
711
697
712 def shellquote(s):
698 def shellquote(s):
713 return "'%s'" % s.replace("'", "'\\''")
699 return "'%s'" % s.replace("'", "'\\''")
714
700
715 def testpid(pid):
701 def testpid(pid):
716 '''return False if pid dead, True if running or not sure'''
702 '''return False if pid dead, True if running or not sure'''
717 try:
703 try:
718 os.kill(pid, 0)
704 os.kill(pid, 0)
719 return True
705 return True
720 except OSError, inst:
706 except OSError, inst:
721 return inst.errno != errno.ESRCH
707 return inst.errno != errno.ESRCH
722
708
723 def explain_exit(code):
709 def explain_exit(code):
724 """return a 2-tuple (desc, code) describing a process's status"""
710 """return a 2-tuple (desc, code) describing a process's status"""
725 if os.WIFEXITED(code):
711 if os.WIFEXITED(code):
726 val = os.WEXITSTATUS(code)
712 val = os.WEXITSTATUS(code)
727 return _("exited with status %d") % val, val
713 return _("exited with status %d") % val, val
728 elif os.WIFSIGNALED(code):
714 elif os.WIFSIGNALED(code):
729 val = os.WTERMSIG(code)
715 val = os.WTERMSIG(code)
730 return _("killed by signal %d") % val, val
716 return _("killed by signal %d") % val, val
731 elif os.WIFSTOPPED(code):
717 elif os.WIFSTOPPED(code):
732 val = os.WSTOPSIG(code)
718 val = os.WSTOPSIG(code)
733 return _("stopped by signal %d") % val, val
719 return _("stopped by signal %d") % val, val
734 raise ValueError(_("invalid exit code"))
720 raise ValueError(_("invalid exit code"))
735
721
736 def opener(base, audit=True):
722 def opener(base, audit=True):
737 """
723 """
738 return a function that opens files relative to base
724 return a function that opens files relative to base
739
725
740 this function is used to hide the details of COW semantics and
726 this function is used to hide the details of COW semantics and
741 remote file access from higher level code.
727 remote file access from higher level code.
742 """
728 """
743 p = base
729 p = base
744 audit_p = audit
730 audit_p = audit
745
731
746 def mktempcopy(name):
732 def mktempcopy(name):
747 d, fn = os.path.split(name)
733 d, fn = os.path.split(name)
748 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
734 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
749 os.close(fd)
735 os.close(fd)
750 ofp = posixfile(temp, "wb")
736 ofp = posixfile(temp, "wb")
751 try:
737 try:
752 try:
738 try:
753 ifp = posixfile(name, "rb")
739 ifp = posixfile(name, "rb")
754 except IOError, inst:
740 except IOError, inst:
755 if not getattr(inst, 'filename', None):
741 if not getattr(inst, 'filename', None):
756 inst.filename = name
742 inst.filename = name
757 raise
743 raise
758 for chunk in filechunkiter(ifp):
744 for chunk in filechunkiter(ifp):
759 ofp.write(chunk)
745 ofp.write(chunk)
760 ifp.close()
746 ifp.close()
761 ofp.close()
747 ofp.close()
762 except:
748 except:
763 try: os.unlink(temp)
749 try: os.unlink(temp)
764 except: pass
750 except: pass
765 raise
751 raise
766 st = os.lstat(name)
752 st = os.lstat(name)
767 os.chmod(temp, st.st_mode)
753 os.chmod(temp, st.st_mode)
768 return temp
754 return temp
769
755
770 class atomictempfile(posixfile):
756 class atomictempfile(posixfile):
771 """the file will only be copied when rename is called"""
757 """the file will only be copied when rename is called"""
772 def __init__(self, name, mode):
758 def __init__(self, name, mode):
773 self.__name = name
759 self.__name = name
774 self.temp = mktempcopy(name)
760 self.temp = mktempcopy(name)
775 posixfile.__init__(self, self.temp, mode)
761 posixfile.__init__(self, self.temp, mode)
776 def rename(self):
762 def rename(self):
777 if not self.closed:
763 if not self.closed:
778 posixfile.close(self)
764 posixfile.close(self)
779 rename(self.temp, localpath(self.__name))
765 rename(self.temp, localpath(self.__name))
780 def __del__(self):
766 def __del__(self):
781 if not self.closed:
767 if not self.closed:
782 try:
768 try:
783 os.unlink(self.temp)
769 os.unlink(self.temp)
784 except: pass
770 except: pass
785 posixfile.close(self)
771 posixfile.close(self)
786
772
787 class atomicfile(atomictempfile):
773 class atomicfile(atomictempfile):
788 """the file will only be copied on close"""
774 """the file will only be copied on close"""
789 def __init__(self, name, mode):
775 def __init__(self, name, mode):
790 atomictempfile.__init__(self, name, mode)
776 atomictempfile.__init__(self, name, mode)
791 def close(self):
777 def close(self):
792 self.rename()
778 self.rename()
793 def __del__(self):
779 def __del__(self):
794 self.rename()
780 self.rename()
795
781
796 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
782 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
797 if audit_p:
783 if audit_p:
798 audit_path(path)
784 audit_path(path)
799 f = os.path.join(p, path)
785 f = os.path.join(p, path)
800
786
801 if not text:
787 if not text:
802 mode += "b" # for that other OS
788 mode += "b" # for that other OS
803
789
804 if mode[0] != "r":
790 if mode[0] != "r":
805 try:
791 try:
806 nlink = nlinks(f)
792 nlink = nlinks(f)
807 except OSError:
793 except OSError:
808 d = os.path.dirname(f)
794 d = os.path.dirname(f)
809 if not os.path.isdir(d):
795 if not os.path.isdir(d):
810 os.makedirs(d)
796 os.makedirs(d)
811 else:
797 else:
812 if atomic:
798 if atomic:
813 return atomicfile(f, mode)
799 return atomicfile(f, mode)
814 elif atomictemp:
800 elif atomictemp:
815 return atomictempfile(f, mode)
801 return atomictempfile(f, mode)
816 if nlink > 1:
802 if nlink > 1:
817 rename(mktempcopy(f), f)
803 rename(mktempcopy(f), f)
818 return posixfile(f, mode)
804 return posixfile(f, mode)
819
805
820 return o
806 return o
821
807
822 class chunkbuffer(object):
808 class chunkbuffer(object):
823 """Allow arbitrary sized chunks of data to be efficiently read from an
809 """Allow arbitrary sized chunks of data to be efficiently read from an
824 iterator over chunks of arbitrary size."""
810 iterator over chunks of arbitrary size."""
825
811
826 def __init__(self, in_iter, targetsize = 2**16):
812 def __init__(self, in_iter, targetsize = 2**16):
827 """in_iter is the iterator that's iterating over the input chunks.
813 """in_iter is the iterator that's iterating over the input chunks.
828 targetsize is how big a buffer to try to maintain."""
814 targetsize is how big a buffer to try to maintain."""
829 self.in_iter = iter(in_iter)
815 self.in_iter = iter(in_iter)
830 self.buf = ''
816 self.buf = ''
831 self.targetsize = int(targetsize)
817 self.targetsize = int(targetsize)
832 if self.targetsize <= 0:
818 if self.targetsize <= 0:
833 raise ValueError(_("targetsize must be greater than 0, was %d") %
819 raise ValueError(_("targetsize must be greater than 0, was %d") %
834 targetsize)
820 targetsize)
835 self.iterempty = False
821 self.iterempty = False
836
822
837 def fillbuf(self):
823 def fillbuf(self):
838 """Ignore target size; read every chunk from iterator until empty."""
824 """Ignore target size; read every chunk from iterator until empty."""
839 if not self.iterempty:
825 if not self.iterempty:
840 collector = cStringIO.StringIO()
826 collector = cStringIO.StringIO()
841 collector.write(self.buf)
827 collector.write(self.buf)
842 for ch in self.in_iter:
828 for ch in self.in_iter:
843 collector.write(ch)
829 collector.write(ch)
844 self.buf = collector.getvalue()
830 self.buf = collector.getvalue()
845 self.iterempty = True
831 self.iterempty = True
846
832
847 def read(self, l):
833 def read(self, l):
848 """Read L bytes of data from the iterator of chunks of data.
834 """Read L bytes of data from the iterator of chunks of data.
849 Returns less than L bytes if the iterator runs dry."""
835 Returns less than L bytes if the iterator runs dry."""
850 if l > len(self.buf) and not self.iterempty:
836 if l > len(self.buf) and not self.iterempty:
851 # Clamp to a multiple of self.targetsize
837 # Clamp to a multiple of self.targetsize
852 targetsize = self.targetsize * ((l // self.targetsize) + 1)
838 targetsize = self.targetsize * ((l // self.targetsize) + 1)
853 collector = cStringIO.StringIO()
839 collector = cStringIO.StringIO()
854 collector.write(self.buf)
840 collector.write(self.buf)
855 collected = len(self.buf)
841 collected = len(self.buf)
856 for chunk in self.in_iter:
842 for chunk in self.in_iter:
857 collector.write(chunk)
843 collector.write(chunk)
858 collected += len(chunk)
844 collected += len(chunk)
859 if collected >= targetsize:
845 if collected >= targetsize:
860 break
846 break
861 if collected < targetsize:
847 if collected < targetsize:
862 self.iterempty = True
848 self.iterempty = True
863 self.buf = collector.getvalue()
849 self.buf = collector.getvalue()
864 s, self.buf = self.buf[:l], buffer(self.buf, l)
850 s, self.buf = self.buf[:l], buffer(self.buf, l)
865 return s
851 return s
866
852
867 def filechunkiter(f, size=65536, limit=None):
853 def filechunkiter(f, size=65536, limit=None):
868 """Create a generator that produces the data in the file size
854 """Create a generator that produces the data in the file size
869 (default 65536) bytes at a time, up to optional limit (default is
855 (default 65536) bytes at a time, up to optional limit (default is
870 to read all data). Chunks may be less than size bytes if the
856 to read all data). Chunks may be less than size bytes if the
871 chunk is the last chunk in the file, or the file is a socket or
857 chunk is the last chunk in the file, or the file is a socket or
872 some other type of file that sometimes reads less data than is
858 some other type of file that sometimes reads less data than is
873 requested."""
859 requested."""
874 assert size >= 0
860 assert size >= 0
875 assert limit is None or limit >= 0
861 assert limit is None or limit >= 0
876 while True:
862 while True:
877 if limit is None: nbytes = size
863 if limit is None: nbytes = size
878 else: nbytes = min(limit, size)
864 else: nbytes = min(limit, size)
879 s = nbytes and f.read(nbytes)
865 s = nbytes and f.read(nbytes)
880 if not s: break
866 if not s: break
881 if limit: limit -= len(s)
867 if limit: limit -= len(s)
882 yield s
868 yield s
883
869
884 def makedate():
870 def makedate():
885 lt = time.localtime()
871 lt = time.localtime()
886 if lt[8] == 1 and time.daylight:
872 if lt[8] == 1 and time.daylight:
887 tz = time.altzone
873 tz = time.altzone
888 else:
874 else:
889 tz = time.timezone
875 tz = time.timezone
890 return time.mktime(lt), tz
876 return time.mktime(lt), tz
891
877
892 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
878 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
893 """represent a (unixtime, offset) tuple as a localized time.
879 """represent a (unixtime, offset) tuple as a localized time.
894 unixtime is seconds since the epoch, and offset is the time zone's
880 unixtime is seconds since the epoch, and offset is the time zone's
895 number of seconds away from UTC. if timezone is false, do not
881 number of seconds away from UTC. if timezone is false, do not
896 append time zone to string."""
882 append time zone to string."""
897 t, tz = date or makedate()
883 t, tz = date or makedate()
898 s = time.strftime(format, time.gmtime(float(t) - tz))
884 s = time.strftime(format, time.gmtime(float(t) - tz))
899 if timezone:
885 if timezone:
900 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
886 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
901 return s
887 return s
902
888
903 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
889 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
904 """parse a localized time string and return a (unixtime, offset) tuple.
890 """parse a localized time string and return a (unixtime, offset) tuple.
905 if the string cannot be parsed, ValueError is raised."""
891 if the string cannot be parsed, ValueError is raised."""
906 def hastimezone(string):
892 def hastimezone(string):
907 return (string[-4:].isdigit() and
893 return (string[-4:].isdigit() and
908 (string[-5] == '+' or string[-5] == '-') and
894 (string[-5] == '+' or string[-5] == '-') and
909 string[-6].isspace())
895 string[-6].isspace())
910
896
911 # NOTE: unixtime = localunixtime + offset
897 # NOTE: unixtime = localunixtime + offset
912 if hastimezone(string):
898 if hastimezone(string):
913 date, tz = string[:-6], string[-5:]
899 date, tz = string[:-6], string[-5:]
914 tz = int(tz)
900 tz = int(tz)
915 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
901 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
916 else:
902 else:
917 date, offset = string, None
903 date, offset = string, None
918 timetuple = time.strptime(date, format)
904 timetuple = time.strptime(date, format)
919 localunixtime = int(calendar.timegm(timetuple))
905 localunixtime = int(calendar.timegm(timetuple))
920 if offset is None:
906 if offset is None:
921 # local timezone
907 # local timezone
922 unixtime = int(time.mktime(timetuple))
908 unixtime = int(time.mktime(timetuple))
923 offset = unixtime - localunixtime
909 offset = unixtime - localunixtime
924 else:
910 else:
925 unixtime = localunixtime + offset
911 unixtime = localunixtime + offset
926 return unixtime, offset
912 return unixtime, offset
927
913
928 def parsedate(string, formats=None):
914 def parsedate(string, formats=None):
929 """parse a localized time string and return a (unixtime, offset) tuple.
915 """parse a localized time string and return a (unixtime, offset) tuple.
930 The date may be a "unixtime offset" string or in one of the specified
916 The date may be a "unixtime offset" string or in one of the specified
931 formats."""
917 formats."""
932 if not formats:
918 if not formats:
933 formats = defaultdateformats
919 formats = defaultdateformats
934 try:
920 try:
935 when, offset = map(int, string.split(' '))
921 when, offset = map(int, string.split(' '))
936 except ValueError:
922 except ValueError:
937 for format in formats:
923 for format in formats:
938 try:
924 try:
939 when, offset = strdate(string, format)
925 when, offset = strdate(string, format)
940 except ValueError:
926 except ValueError:
941 pass
927 pass
942 else:
928 else:
943 break
929 break
944 else:
930 else:
945 raise ValueError(_('invalid date: %r '
931 raise ValueError(_('invalid date: %r '
946 'see hg(1) manual page for details')
932 'see hg(1) manual page for details')
947 % string)
933 % string)
948 # validate explicit (probably user-specified) date and
934 # validate explicit (probably user-specified) date and
949 # time zone offset. values must fit in signed 32 bits for
935 # time zone offset. values must fit in signed 32 bits for
950 # current 32-bit linux runtimes. timezones go from UTC-12
936 # current 32-bit linux runtimes. timezones go from UTC-12
951 # to UTC+14
937 # to UTC+14
952 if abs(when) > 0x7fffffff:
938 if abs(when) > 0x7fffffff:
953 raise ValueError(_('date exceeds 32 bits: %d') % when)
939 raise ValueError(_('date exceeds 32 bits: %d') % when)
954 if offset < -50400 or offset > 43200:
940 if offset < -50400 or offset > 43200:
955 raise ValueError(_('impossible time zone offset: %d') % offset)
941 raise ValueError(_('impossible time zone offset: %d') % offset)
956 return when, offset
942 return when, offset
957
943
958 def shortuser(user):
944 def shortuser(user):
959 """Return a short representation of a user name or email address."""
945 """Return a short representation of a user name or email address."""
960 f = user.find('@')
946 f = user.find('@')
961 if f >= 0:
947 if f >= 0:
962 user = user[:f]
948 user = user[:f]
963 f = user.find('<')
949 f = user.find('<')
964 if f >= 0:
950 if f >= 0:
965 user = user[f+1:]
951 user = user[f+1:]
966 f = user.find(' ')
952 f = user.find(' ')
967 if f >= 0:
953 if f >= 0:
968 user = user[:f]
954 user = user[:f]
969 return user
955 return user
970
956
971 def walkrepos(path):
957 def walkrepos(path):
972 '''yield every hg repository under path, recursively.'''
958 '''yield every hg repository under path, recursively.'''
973 def errhandler(err):
959 def errhandler(err):
974 if err.filename == path:
960 if err.filename == path:
975 raise err
961 raise err
976
962
977 for root, dirs, files in os.walk(path, onerror=errhandler):
963 for root, dirs, files in os.walk(path, onerror=errhandler):
978 for d in dirs:
964 for d in dirs:
979 if d == '.hg':
965 if d == '.hg':
980 yield root
966 yield root
981 dirs[:] = []
967 dirs[:] = []
982 break
968 break
983
969
984 _rcpath = None
970 _rcpath = None
985
971
986 def rcpath():
972 def rcpath():
987 '''return hgrc search path. if env var HGRCPATH is set, use it.
973 '''return hgrc search path. if env var HGRCPATH is set, use it.
988 for each item in path, if directory, use files ending in .rc,
974 for each item in path, if directory, use files ending in .rc,
989 else use item.
975 else use item.
990 make HGRCPATH empty to only look in .hg/hgrc of current repo.
976 make HGRCPATH empty to only look in .hg/hgrc of current repo.
991 if no HGRCPATH, use default os-specific path.'''
977 if no HGRCPATH, use default os-specific path.'''
992 global _rcpath
978 global _rcpath
993 if _rcpath is None:
979 if _rcpath is None:
994 if 'HGRCPATH' in os.environ:
980 if 'HGRCPATH' in os.environ:
995 _rcpath = []
981 _rcpath = []
996 for p in os.environ['HGRCPATH'].split(os.pathsep):
982 for p in os.environ['HGRCPATH'].split(os.pathsep):
997 if not p: continue
983 if not p: continue
998 if os.path.isdir(p):
984 if os.path.isdir(p):
999 for f in os.listdir(p):
985 for f in os.listdir(p):
1000 if f.endswith('.rc'):
986 if f.endswith('.rc'):
1001 _rcpath.append(os.path.join(p, f))
987 _rcpath.append(os.path.join(p, f))
1002 else:
988 else:
1003 _rcpath.append(p)
989 _rcpath.append(p)
1004 else:
990 else:
1005 _rcpath = os_rcpath()
991 _rcpath = os_rcpath()
1006 return _rcpath
992 return _rcpath
1007
993
1008 def bytecount(nbytes):
994 def bytecount(nbytes):
1009 '''return byte count formatted as readable string, with units'''
995 '''return byte count formatted as readable string, with units'''
1010
996
1011 units = (
997 units = (
1012 (100, 1<<30, _('%.0f GB')),
998 (100, 1<<30, _('%.0f GB')),
1013 (10, 1<<30, _('%.1f GB')),
999 (10, 1<<30, _('%.1f GB')),
1014 (1, 1<<30, _('%.2f GB')),
1000 (1, 1<<30, _('%.2f GB')),
1015 (100, 1<<20, _('%.0f MB')),
1001 (100, 1<<20, _('%.0f MB')),
1016 (10, 1<<20, _('%.1f MB')),
1002 (10, 1<<20, _('%.1f MB')),
1017 (1, 1<<20, _('%.2f MB')),
1003 (1, 1<<20, _('%.2f MB')),
1018 (100, 1<<10, _('%.0f KB')),
1004 (100, 1<<10, _('%.0f KB')),
1019 (10, 1<<10, _('%.1f KB')),
1005 (10, 1<<10, _('%.1f KB')),
1020 (1, 1<<10, _('%.2f KB')),
1006 (1, 1<<10, _('%.2f KB')),
1021 (1, 1, _('%.0f bytes')),
1007 (1, 1, _('%.0f bytes')),
1022 )
1008 )
1023
1009
1024 for multiplier, divisor, format in units:
1010 for multiplier, divisor, format in units:
1025 if nbytes >= divisor * multiplier:
1011 if nbytes >= divisor * multiplier:
1026 return format % (nbytes / float(divisor))
1012 return format % (nbytes / float(divisor))
1027 return units[-1][2] % nbytes
1013 return units[-1][2] % nbytes
1028
1014
1029 def drop_scheme(scheme, path):
1015 def drop_scheme(scheme, path):
1030 sc = scheme + ':'
1016 sc = scheme + ':'
1031 if path.startswith(sc):
1017 if path.startswith(sc):
1032 path = path[len(sc):]
1018 path = path[len(sc):]
1033 if path.startswith('//'):
1019 if path.startswith('//'):
1034 path = path[2:]
1020 path = path[2:]
1035 return path
1021 return path
@@ -1,301 +1,299
1 # util_win32.py - utility functions that use win32 API
1 # util_win32.py - utility functions that use win32 API
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of
6 # This software may be used and distributed according to the terms of
7 # the GNU General Public License, incorporated herein by reference.
7 # the GNU General Public License, incorporated herein by reference.
8
8
9 # Mark Hammond's win32all package allows better functionality on
9 # Mark Hammond's win32all package allows better functionality on
10 # Windows. this module overrides definitions in util.py. if not
10 # Windows. this module overrides definitions in util.py. if not
11 # available, import of this module will fail, and generic code will be
11 # available, import of this module will fail, and generic code will be
12 # used.
12 # used.
13
13
14 import win32api
14 import win32api
15
15
16 from demandload import *
16 from demandload import *
17 from i18n import gettext as _
17 from i18n import gettext as _
18 demandload(globals(), 'errno os pywintypes win32con win32file win32process')
18 demandload(globals(), 'errno os pywintypes win32con win32file win32process')
19 demandload(globals(), 'cStringIO win32com.shell:shell,shellcon winerror')
19 demandload(globals(), 'cStringIO win32com.shell:shell,shellcon winerror')
20
20
21 class WinError:
21 class WinError:
22 winerror_map = {
22 winerror_map = {
23 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
23 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
24 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
24 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
25 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
25 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
26 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
26 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
27 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
27 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
28 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
28 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
29 winerror.ERROR_BAD_COMMAND: errno.EIO,
29 winerror.ERROR_BAD_COMMAND: errno.EIO,
30 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
30 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
31 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
31 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
32 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
32 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
33 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
33 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
34 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
34 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
35 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
35 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
36 winerror.ERROR_BAD_PIPE: errno.EPIPE,
36 winerror.ERROR_BAD_PIPE: errno.EPIPE,
37 winerror.ERROR_BAD_UNIT: errno.ENODEV,
37 winerror.ERROR_BAD_UNIT: errno.ENODEV,
38 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
38 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
39 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
39 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
40 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
40 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
41 winerror.ERROR_BUSY: errno.EBUSY,
41 winerror.ERROR_BUSY: errno.EBUSY,
42 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
42 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
43 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
43 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
44 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
44 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
45 winerror.ERROR_CANTOPEN: errno.EIO,
45 winerror.ERROR_CANTOPEN: errno.EIO,
46 winerror.ERROR_CANTREAD: errno.EIO,
46 winerror.ERROR_CANTREAD: errno.EIO,
47 winerror.ERROR_CANTWRITE: errno.EIO,
47 winerror.ERROR_CANTWRITE: errno.EIO,
48 winerror.ERROR_CRC: errno.EIO,
48 winerror.ERROR_CRC: errno.EIO,
49 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
49 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
50 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
50 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
51 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
51 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
52 winerror.ERROR_DIRECTORY: errno.EINVAL,
52 winerror.ERROR_DIRECTORY: errno.EINVAL,
53 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
53 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
54 winerror.ERROR_DISK_CHANGE: errno.EIO,
54 winerror.ERROR_DISK_CHANGE: errno.EIO,
55 winerror.ERROR_DISK_FULL: errno.ENOSPC,
55 winerror.ERROR_DISK_FULL: errno.ENOSPC,
56 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
56 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
57 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
57 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
58 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
58 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
59 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
59 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
60 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
60 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
61 winerror.ERROR_FILE_INVALID: errno.ENODEV,
61 winerror.ERROR_FILE_INVALID: errno.ENODEV,
62 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
62 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
63 winerror.ERROR_GEN_FAILURE: errno.EIO,
63 winerror.ERROR_GEN_FAILURE: errno.EIO,
64 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
64 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
65 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
65 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
66 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
66 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
67 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
67 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
68 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
68 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
69 winerror.ERROR_INVALID_DATA: errno.EINVAL,
69 winerror.ERROR_INVALID_DATA: errno.EINVAL,
70 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
70 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
71 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
71 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
72 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
72 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
73 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
73 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
74 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
74 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
75 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
75 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
76 winerror.ERROR_INVALID_NAME: errno.EINVAL,
76 winerror.ERROR_INVALID_NAME: errno.EINVAL,
77 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
77 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
78 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
78 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
79 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
79 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
80 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
80 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
81 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
81 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
82 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
82 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
83 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
83 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
84 winerror.ERROR_IO_DEVICE: errno.EIO,
84 winerror.ERROR_IO_DEVICE: errno.EIO,
85 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
85 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
86 winerror.ERROR_LOCKED: errno.EBUSY,
86 winerror.ERROR_LOCKED: errno.EBUSY,
87 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
87 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
88 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
88 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
89 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
89 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
90 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
90 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
91 winerror.ERROR_MORE_DATA: errno.EPIPE,
91 winerror.ERROR_MORE_DATA: errno.EPIPE,
92 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
92 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
93 winerror.ERROR_NOACCESS: errno.EFAULT,
93 winerror.ERROR_NOACCESS: errno.EFAULT,
94 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
94 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
95 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
95 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
96 winerror.ERROR_NOT_READY: errno.EAGAIN,
96 winerror.ERROR_NOT_READY: errno.EAGAIN,
97 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
97 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
98 winerror.ERROR_NO_DATA: errno.EPIPE,
98 winerror.ERROR_NO_DATA: errno.EPIPE,
99 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
99 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
100 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
100 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
101 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
101 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
102 winerror.ERROR_OPEN_FAILED: errno.EIO,
102 winerror.ERROR_OPEN_FAILED: errno.EIO,
103 winerror.ERROR_OPEN_FILES: errno.EBUSY,
103 winerror.ERROR_OPEN_FILES: errno.EBUSY,
104 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
104 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
105 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
105 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
106 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
106 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
107 winerror.ERROR_PATH_BUSY: errno.EBUSY,
107 winerror.ERROR_PATH_BUSY: errno.EBUSY,
108 winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT,
108 winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT,
109 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
109 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
110 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
110 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
111 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
111 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
112 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
112 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
113 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
113 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
114 winerror.ERROR_READ_FAULT: errno.EIO,
114 winerror.ERROR_READ_FAULT: errno.EIO,
115 winerror.ERROR_SEEK: errno.EIO,
115 winerror.ERROR_SEEK: errno.EIO,
116 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
116 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
117 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
117 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
118 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
118 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
119 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
119 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
120 winerror.ERROR_SWAPERROR: errno.ENOENT,
120 winerror.ERROR_SWAPERROR: errno.ENOENT,
121 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
121 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
122 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
122 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
123 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
123 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
124 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
124 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
125 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
125 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
126 winerror.ERROR_WRITE_FAULT: errno.EIO,
126 winerror.ERROR_WRITE_FAULT: errno.EIO,
127 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
127 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
128 }
128 }
129
129
130 def __init__(self, err):
130 def __init__(self, err):
131 self.win_errno, self.win_function, self.win_strerror = err
131 self.win_errno, self.win_function, self.win_strerror = err
132 if self.win_strerror.endswith('.'):
132 if self.win_strerror.endswith('.'):
133 self.win_strerror = self.win_strerror[:-1]
133 self.win_strerror = self.win_strerror[:-1]
134
134
135 class WinIOError(WinError, IOError):
135 class WinIOError(WinError, IOError):
136 def __init__(self, err, filename=None):
136 def __init__(self, err, filename=None):
137 WinError.__init__(self, err)
137 WinError.__init__(self, err)
138 IOError.__init__(self, self.winerror_map.get(self.win_errno, 0),
138 IOError.__init__(self, self.winerror_map.get(self.win_errno, 0),
139 self.win_strerror)
139 self.win_strerror)
140 self.filename = filename
140 self.filename = filename
141
141
142 class WinOSError(WinError, OSError):
142 class WinOSError(WinError, OSError):
143 def __init__(self, err):
143 def __init__(self, err):
144 WinError.__init__(self, err)
144 WinError.__init__(self, err)
145 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
145 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
146 self.win_strerror)
146 self.win_strerror)
147
147
148 def os_link(src, dst):
148 def os_link(src, dst):
149 # NB will only succeed on NTFS
149 # NB will only succeed on NTFS
150 try:
150 try:
151 win32file.CreateHardLink(dst, src)
151 win32file.CreateHardLink(dst, src)
152 except pywintypes.error, details:
152 except pywintypes.error, details:
153 raise WinOSError(details)
153 raise WinOSError(details)
154
154
155 def nlinks(pathname):
155 def nlinks(pathname):
156 """Return number of hardlinks for the given file."""
156 """Return number of hardlinks for the given file."""
157 try:
157 try:
158 fh = win32file.CreateFile(pathname,
158 fh = win32file.CreateFile(pathname,
159 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
159 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
160 None, win32file.OPEN_EXISTING, 0, None)
160 None, win32file.OPEN_EXISTING, 0, None)
161 res = win32file.GetFileInformationByHandle(fh)
161 res = win32file.GetFileInformationByHandle(fh)
162 fh.Close()
162 fh.Close()
163 return res[7]
163 return res[7]
164 except pywintypes.error:
164 except pywintypes.error:
165 return os.lstat(pathname).st_nlink
165 return os.lstat(pathname).st_nlink
166
166
167 def testpid(pid):
167 def testpid(pid):
168 '''return True if pid is still running or unable to
168 '''return True if pid is still running or unable to
169 determine, False otherwise'''
169 determine, False otherwise'''
170 try:
170 try:
171 handle = win32api.OpenProcess(
171 handle = win32api.OpenProcess(
172 win32con.PROCESS_QUERY_INFORMATION, False, pid)
172 win32con.PROCESS_QUERY_INFORMATION, False, pid)
173 if handle:
173 if handle:
174 status = win32process.GetExitCodeProcess(handle)
174 status = win32process.GetExitCodeProcess(handle)
175 return status == win32con.STILL_ACTIVE
175 return status == win32con.STILL_ACTIVE
176 except pywintypes.error, details:
176 except pywintypes.error, details:
177 return details[0] != winerror.ERROR_INVALID_PARAMETER
177 return details[0] != winerror.ERROR_INVALID_PARAMETER
178 return True
178 return True
179
179
180 def system_rcpath_win32():
180 def system_rcpath_win32():
181 '''return default os-specific hgrc search path'''
181 '''return default os-specific hgrc search path'''
182 proc = win32api.GetCurrentProcess()
182 proc = win32api.GetCurrentProcess()
183 try:
183 try:
184 # This will fail on windows < NT
184 # This will fail on windows < NT
185 filename = win32process.GetModuleFileNameEx(proc, 0)
185 filename = win32process.GetModuleFileNameEx(proc, 0)
186 except:
186 except:
187 filename = win32api.GetModuleFileName(0)
187 filename = win32api.GetModuleFileName(0)
188 return [os.path.join(os.path.dirname(filename), 'mercurial.ini')]
188 return [os.path.join(os.path.dirname(filename), 'mercurial.ini')]
189
189
190 def user_rcpath():
190 def user_rcpath():
191 '''return os-specific hgrc search path to the user dir'''
191 '''return os-specific hgrc search path to the user dir'''
192 userdir = os.path.expanduser('~')
192 userdir = os.path.expanduser('~')
193 if userdir == '~':
193 if userdir == '~':
194 # We are on win < nt: fetch the APPDATA directory location and use
194 # We are on win < nt: fetch the APPDATA directory location and use
195 # the parent directory as the user home dir.
195 # the parent directory as the user home dir.
196 appdir = shell.SHGetPathFromIDList(
196 appdir = shell.SHGetPathFromIDList(
197 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
197 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
198 userdir = os.path.dirname(appdir)
198 userdir = os.path.dirname(appdir)
199 return os.path.join(userdir, 'mercurial.ini')
199 return os.path.join(userdir, 'mercurial.ini')
200
200
201 class posixfile_nt(object):
201 class posixfile_nt(object):
202 '''file object with posix-like semantics. on windows, normal
202 '''file object with posix-like semantics. on windows, normal
203 files can not be deleted or renamed if they are open. must open
203 files can not be deleted or renamed if they are open. must open
204 with win32file.FILE_SHARE_DELETE. this flag does not exist on
204 with win32file.FILE_SHARE_DELETE. this flag does not exist on
205 windows < nt, so do not use this class there.'''
205 windows < nt, so do not use this class there.'''
206
206
207 # tried to use win32file._open_osfhandle to pass fd to os.fdopen,
207 # tried to use win32file._open_osfhandle to pass fd to os.fdopen,
208 # but does not work at all. wrap win32 file api instead.
208 # but does not work at all. wrap win32 file api instead.
209
209
210 def __init__(self, name, mode='rb'):
210 def __init__(self, name, mode='rb'):
211 access = 0
211 access = 0
212 if 'r' in mode or '+' in mode:
212 if 'r' in mode or '+' in mode:
213 access |= win32file.GENERIC_READ
213 access |= win32file.GENERIC_READ
214 if 'w' in mode or 'a' in mode:
214 if 'w' in mode or 'a' in mode:
215 access |= win32file.GENERIC_WRITE
215 access |= win32file.GENERIC_WRITE
216 if 'r' in mode:
216 if 'r' in mode:
217 creation = win32file.OPEN_EXISTING
217 creation = win32file.OPEN_EXISTING
218 elif 'a' in mode:
218 elif 'a' in mode:
219 creation = win32file.OPEN_ALWAYS
219 creation = win32file.OPEN_ALWAYS
220 else:
220 else:
221 creation = win32file.CREATE_ALWAYS
221 creation = win32file.CREATE_ALWAYS
222 try:
222 try:
223 self.handle = win32file.CreateFile(name,
223 self.handle = win32file.CreateFile(name,
224 access,
224 access,
225 win32file.FILE_SHARE_READ |
225 win32file.FILE_SHARE_READ |
226 win32file.FILE_SHARE_WRITE |
226 win32file.FILE_SHARE_WRITE |
227 win32file.FILE_SHARE_DELETE,
227 win32file.FILE_SHARE_DELETE,
228 None,
228 None,
229 creation,
229 creation,
230 win32file.FILE_ATTRIBUTE_NORMAL,
230 win32file.FILE_ATTRIBUTE_NORMAL,
231 0)
231 0)
232 except pywintypes.error, err:
232 except pywintypes.error, err:
233 raise WinIOError(err, name)
233 raise WinIOError(err, name)
234 self.closed = False
234 self.closed = False
235 self.name = name
235 self.name = name
236 self.mode = mode
236 self.mode = mode
237
237
238 def __iter__(self):
238 def __iter__(self):
239 for line in self.read().splitlines(True):
239 for line in self.read().splitlines(True):
240 yield line
240 yield line
241
241
242 def read(self, count=-1):
242 def read(self, count=-1):
243 try:
243 try:
244 cs = cStringIO.StringIO()
244 cs = cStringIO.StringIO()
245 while count:
245 while count:
246 wincount = int(count)
246 wincount = int(count)
247 if wincount == -1:
247 if wincount == -1:
248 wincount = 1048576
248 wincount = 1048576
249 val, data = win32file.ReadFile(self.handle, wincount)
249 val, data = win32file.ReadFile(self.handle, wincount)
250 if not data: break
250 if not data: break
251 cs.write(data)
251 cs.write(data)
252 if count != -1:
252 if count != -1:
253 count -= len(data)
253 count -= len(data)
254 return cs.getvalue()
254 return cs.getvalue()
255 except pywintypes.error, err:
255 except pywintypes.error, err:
256 raise WinIOError(err)
256 raise WinIOError(err)
257
257
258 def write(self, data):
258 def write(self, data):
259 try:
259 try:
260 if 'a' in self.mode:
260 if 'a' in self.mode:
261 win32file.SetFilePointer(self.handle, 0, win32file.FILE_END)
261 win32file.SetFilePointer(self.handle, 0, win32file.FILE_END)
262 nwrit = 0
262 nwrit = 0
263 while nwrit < len(data):
263 while nwrit < len(data):
264 val, nwrit = win32file.WriteFile(self.handle, data)
264 val, nwrit = win32file.WriteFile(self.handle, data)
265 data = data[nwrit:]
265 data = data[nwrit:]
266 except pywintypes.error, err:
266 except pywintypes.error, err:
267 raise WinIOError(err)
267 raise WinIOError(err)
268
268
269 def seek(self, pos, whence=0):
269 def seek(self, pos, whence=0):
270 try:
270 try:
271 win32file.SetFilePointer(self.handle, int(pos), whence)
271 win32file.SetFilePointer(self.handle, int(pos), whence)
272 except pywintypes.error, err:
272 except pywintypes.error, err:
273 raise WinIOError(err)
273 raise WinIOError(err)
274
274
275 def tell(self):
275 def tell(self):
276 try:
276 try:
277 return win32file.SetFilePointer(self.handle, 0,
277 return win32file.SetFilePointer(self.handle, 0,
278 win32file.FILE_CURRENT)
278 win32file.FILE_CURRENT)
279 except pywintypes.error, err:
279 except pywintypes.error, err:
280 raise WinIOError(err)
280 raise WinIOError(err)
281
281
282 def close(self):
282 def close(self):
283 if not self.closed:
283 if not self.closed:
284 self.handle = None
284 self.handle = None
285 self.closed = True
285 self.closed = True
286
286
287 def flush(self):
287 def flush(self):
288 try:
288 try:
289 win32file.FlushFileBuffers(self.handle)
289 win32file.FlushFileBuffers(self.handle)
290 except pywintypes.error, err:
290 except pywintypes.error, err:
291 raise WinIOError(err)
291 raise WinIOError(err)
292
292
293 def truncate(self, pos=0):
293 def truncate(self, pos=0):
294 try:
294 try:
295 win32file.SetFilePointer(self.handle, int(pos),
295 win32file.SetFilePointer(self.handle, int(pos),
296 win32file.FILE_BEGIN)
296 win32file.FILE_BEGIN)
297 win32file.SetEndOfFile(self.handle)
297 win32file.SetEndOfFile(self.handle)
298 except pywintypes.error, err:
298 except pywintypes.error, err:
299 raise WinIOError(err)
299 raise WinIOError(err)
300
301 getuser_fallback = win32api.GetUserName
@@ -1,12 +1,26
1 #!/bin/sh
1 #!/bin/sh
2
2
3 unset HGUSER
3 unset HGUSER
4 EMAIL="My Name <myname@example.com>"
4 EMAIL="My Name <myname@example.com>"
5 export EMAIL
5 export EMAIL
6
6
7 hg init test
7 hg init test
8 cd test
8 cd test
9 touch asdf
9 touch asdf
10 hg add asdf
10 hg add asdf
11 hg commit -d '1000000 0' -m commit-1
11 hg commit -d '1000000 0' -m commit-1
12 hg tip
12 hg tip
13
14 unset EMAIL
15 echo 1 > asdf
16 hg commit -d '1000000 0' -m commit-1
17 hg commit -d '1000000 0' -u "foo@bar.com" -m commit-1
18 hg tip
19 echo "[ui]" >> .hg/hgrc
20 echo "username = foobar <foo@bar.com>" >> .hg/hgrc
21 echo 12 > asdf
22 hg commit -d '1000000 0' -m commit-1
23 hg tip
24 echo 1 > asdf
25 hg commit -d '1000000 0' -u "foo@bar.com" -m commit-1
26 hg tip
@@ -1,6 +1,27
1 changeset: 0:9426b370c206
1 changeset: 0:9426b370c206
2 tag: tip
2 tag: tip
3 user: My Name <myname@example.com>
3 user: My Name <myname@example.com>
4 date: Mon Jan 12 13:46:40 1970 +0000
4 date: Mon Jan 12 13:46:40 1970 +0000
5 summary: commit-1
5 summary: commit-1
6
6
7 abort: No default username available, use -u
8 transaction abort!
9 rollback completed
10 changeset: 1:2becd0bae6e6
11 tag: tip
12 user: foo@bar.com
13 date: Mon Jan 12 13:46:40 1970 +0000
14 summary: commit-1
15
16 changeset: 2:7a0176714f78
17 tag: tip
18 user: foobar <foo@bar.com>
19 date: Mon Jan 12 13:46:40 1970 +0000
20 summary: commit-1
21
22 changeset: 3:f9b58c5a6352
23 tag: tip
24 user: foo@bar.com
25 date: Mon Jan 12 13:46:40 1970 +0000
26 summary: commit-1
27
General Comments 0
You need to be logged in to leave comments. Login now